Skip to main content
Glama

metatrader-mcp-server

server.py10.9 kB
#!/usr/bin/env python3 import os import argparse import logging from dotenv import load_dotenv from mcp.server.fastmcp import FastMCP, Context from contextlib import asynccontextmanager from collections.abc import AsyncIterator from dataclasses import dataclass from typing import Optional, Union from metatrader_mcp.utils import init, get_client # ──────────────────────────────────────────────────────────────────────────────── # 1) Lifespan context definition # ──────────────────────────────────────────────────────────────────────────────── @dataclass class AppContext: client: str @asynccontextmanager async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: try: client = init( os.getenv("login"), os.getenv("password"), os.getenv("server"), os.getenv("path") ) yield AppContext(client=client) finally: client.disconnect() # ──────────────────────────────────────────────────────────────────────────────── # 2) Instantiate FastMCP as `mcp` (must be named `mcp`, `server`, or `app`) # ──────────────────────────────────────────────────────────────────────────────── mcp = FastMCP( "metatrader", lifespan=app_lifespan, dependencies=[], ) # ──────────────────────────────────────────────────────────────────────────────── # 3) Register tools with @mcp.tool() # ──────────────────────────────────────────────────────────────────────────────── @mcp.tool() def get_account_info(ctx: Context) -> dict: """Get account information (balance, equity, profit, margin level, free margin, account type, leverage, currency).""" client = get_client(ctx) return client.account.get_trade_statistics() @mcp.tool() def get_deals(ctx: Context, from_date: Optional[str] = None, to_date: Optional[str] = None, symbol: Optional[str] = None) -> str: """Get historical deals as CSV. Date input in format: 'YYYY-MM-DD'.""" client = get_client(ctx) df = client.history.get_deals_as_dataframe(from_date=from_date, to_date=to_date, group=symbol) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_orders(ctx: Context, from_date: Optional[str] = None, to_date: Optional[str] = None, symbol: Optional[str] = None) -> str: """Get historical orders as CSV. Date input in format: 'YYYY-MM-DD'""" client = get_client(ctx) df = client.history.get_orders_as_dataframe(from_date=from_date, to_date=to_date, group=symbol) return df.to_csv() if hasattr(df, 'to_csv') else str(df) def get_candles_by_date(ctx: Context, symbol_name: str, timeframe: str, from_date: str = None, to_date: str = None) -> str: """Get candle data for a symbol in a given timeframe and date range as CSV. Date input in format: ISO 8601 or 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM'.""" client = get_client(ctx) df = client.market.get_candles_by_date(symbol_name=symbol_name, timeframe=timeframe, from_date=from_date, to_date=to_date) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_candles_latest(ctx: Context, symbol_name: str, timeframe: str, count: int = 100) -> str: """Get the latest N candles for a symbol and timeframe as CSV.""" client = get_client(ctx) df = client.market.get_candles_latest(symbol_name=symbol_name, timeframe=timeframe, count=count) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_symbol_price(ctx: Context, symbol_name: str) -> dict: """Get the latest price info for a symbol as a dictionary.""" client = get_client(ctx) return client.market.get_symbol_price(symbol_name=symbol_name) @mcp.tool() def get_all_symbols(ctx: Context) -> list: """Get a list of all available market symbols.""" client = get_client(ctx) return client.market.get_symbols() @mcp.tool() def get_symbols(ctx: Context, group: Optional[str] = None) -> list: """ Get a list of available market symbols. Filter symbols by group pattern (e.g., '*USD*'). """ client = get_client(ctx) return client.market.get_symbols(group=group) # ──────────────────────────────────────────────────────────────────────────────── # Order module tools # ──────────────────────────────────────────────────────────────────────────────── @mcp.tool() def get_all_positions(ctx: Context) -> list: """Get all open positions.""" client = get_client(ctx) df = client.order.get_all_positions() return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_positions_by_symbol(ctx: Context, symbol: str) -> list: """Get open positions for a specific symbol.""" client = get_client(ctx) df = client.order.get_positions_by_symbol(symbol=symbol) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_positions_by_id(ctx: Context, id: Union[int, str]) -> list: """Get open positions by ID.""" client = get_client(ctx) df = client.order.get_positions_by_id(id=id) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_all_pending_orders(ctx: Context) -> list: """Get all pending orders.""" client = get_client(ctx) df = client.order.get_all_pending_orders() return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_pending_orders_by_symbol(ctx: Context, symbol: str) -> list: """Get pending orders for a specific symbol.""" client = get_client(ctx) df = client.order.get_pending_orders_by_symbol(symbol=symbol) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def get_pending_orders_by_id(ctx: Context, id: Union[int, str]) -> list: """Get pending orders by id.""" client = get_client(ctx) df = client.order.get_pending_orders_by_id(id=id) return df.to_csv() if hasattr(df, 'to_csv') else str(df) @mcp.tool() def place_market_order(ctx: Context, symbol: str, volume: float, type: str) -> dict: """ Place a market order. Parameters: symbol: Symbol name (e.g., 'EURUSD') volume: Lot size. (e.g. 1.5) type: Order type ('BUY' or 'SELL') """ client = get_client(ctx) return client.order.place_market_order(symbol=symbol, volume=volume, type=type) @mcp.tool() def place_pending_order(ctx: Context, symbol: str, volume: float, type: str, price: float, stop_loss: Optional[Union[int, float]] = 0.0, take_profit: Optional[Union[int, float]] = 0.0) -> dict: """ Place a pending order. Parameters: symbol: Symbol name (e.g., 'EURUSD') volume: Lot size. (e.g. 1.5) type: Order type ('BUY', 'SELL'). price: Pending order price. stop_loss (optional): Stop loss price. take_profit (optional): Take profit price. """ client = get_client(ctx) return client.order.place_pending_order(symbol=symbol, volume=volume, type=type, price=price, stop_loss=stop_loss, take_profit=take_profit) @mcp.tool() def modify_position(ctx: Context, id: Union[int, str], stop_loss: Optional[Union[int, float]] = None, take_profit: Optional[Union[int, float]] = None) -> dict: """Modify an open position by ID.""" client = get_client(ctx) return client.order.modify_position(id=id, stop_loss=stop_loss, take_profit=take_profit) @mcp.tool() def modify_pending_order(ctx: Context, id: Union[int, str], price: Optional[Union[int, float]] = None, stop_loss: Optional[Union[int, float]] = None, take_profit: Optional[Union[int, float]] = None) -> dict: """Modify a pending order by ID.""" client = get_client(ctx) return client.order.modify_pending_order(id=id, price=price, stop_loss=stop_loss, take_profit=take_profit) @mcp.tool() def close_position(ctx: Context, id: Union[int, str]) -> dict: """Close an open position by ID.""" client = get_client(ctx) return client.order.close_position(id=id) @mcp.tool() def cancel_pending_order(ctx: Context, id: Union[int, str]) -> dict: """Cancel a pending order by ID.""" client = get_client(ctx) return client.order.cancel_pending_order(id=id) @mcp.tool() def close_all_positions(ctx: Context) -> dict: """Close all open positions.""" client = get_client(ctx) return client.order.close_all_positions() @mcp.tool() def close_all_positions_by_symbol(ctx: Context, symbol: str) -> dict: """Close all open positions for a specific symbol.""" client = get_client(ctx) return client.order.close_all_positions_by_symbol(symbol=symbol) @mcp.tool() def close_all_profitable_positions(ctx: Context) -> dict: """Close all profitable positions.""" client = get_client(ctx) return client.order.close_all_profitable_positions() @mcp.tool() def close_all_losing_positions(ctx: Context) -> dict: """Close all losing positions.""" client = get_client(ctx) return client.order.close_all_losing_positions() @mcp.tool() def cancel_all_pending_orders(ctx: Context) -> dict: """Cancel all pending orders.""" client = get_client(ctx) return client.order.cancel_all_pending_orders() @mcp.tool() def cancel_pending_orders_by_symbol(ctx: Context, symbol: str) -> dict: """Cancel all pending orders for a specific symbol.""" client = get_client(ctx) return client.order.cancel_pending_orders_by_symbol(symbol=symbol) if __name__ == "__main__": load_dotenv() parser = argparse.ArgumentParser(description="MetaTrader MCP Server") parser.add_argument("--login", type=str, help="MT5 login") parser.add_argument("--password", type=str, help="MT5 password") parser.add_argument("--server", type=str, help="MT5 server name") parser.add_argument("--path", type=str, help="Path to MT5 terminal executable (optional)") args = parser.parse_args() # inject into lifespan via env vars if args.login: os.environ["login"] = args.login if args.password: os.environ["password"] = args.password if args.server: os.environ["server"] = args.server if args.path: os.environ["path"] = args.path # run the MCP server (must call mcp.run) mcp.run( transport="stdio" )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ariadng/metatrader-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server