MetaTrader5 Python版深度解析与实战教程

MetaTrader5 (Python) 深度解析教程:数据获取篇

MetaTrader5 Python 库是 MetaQuotes 公司官方提供的接口,允许 Python 程序与 MetaTrader 5 交易终端进行交互。本教程将深入探讨如何使用该库连接到 MT5 终端并获取各种类型的金融数据。


目录

  1. 简介与安装
  2. 1.1 什么是 MetaTrader5 Python 库?
  3. 1.2 环境准备与安装
  4. 1.3 MT5 终端设置
  5. 连接与初始化
  6. 2.1 导入库
  7. 2.2 初始化连接 (initialize)
  8. 2.3 检查连接状态
  9. 2.4 关闭连接 (shutdown)
  10. 获取账户信息
  11. 3.1 account_info()
  12. 3.2 账户信息属性解析
  13. 获取交易品种 (Symbol) 信息
  14. 4.1 什么是交易品种?
  15. 4.2 symbol_select() – 选择品种(重要
  16. 4.3 symbol_info() – 获取单个品种的详细规格
  17. 4.4 symbols_get() – 获取多个品种的信息
  18. 4.5 品种信息属性解析
  19. 获取实时报价 (Ticks)
  20. 5.1 symbol_info_tick() – 获取最新 Tick
  21. 5.2 Tick 数据属性解析
  22. 5.3 copy_ticks_from() / copy_ticks_range() – 获取历史 Tick 数据 (量大时慎用)
  23. 获取历史 K 线数据 (Bars/Candlesticks)
  24. 6.1 K 线数据的重要性
  25. 6.2 时间周期 (Timeframe) 常量
  26. 6.3 copy_rates_from(symbol, timeframe, start_pos, count) – 从指定位置获取
  27. 6.4 copy_rates_from_pos(symbol, timeframe, start_pos, count) – 同上 (别名)
  28. 6.5 copy_rates_range(symbol, timeframe, date_from, date_to) – 按日期范围获取 (常用)
  29. 6.6 返回数据格式 (NumPy Array)
  30. 6.7 转换为 Pandas DataFrame (推荐)
  31. 6.8 K 线数据字段解析
  32. 获取订单、持仓与交易历史
  33. 7.1 获取挂单 (Pending Orders)
  34. orders_total() – 获取挂单总数
  35. orders_get() – 获取挂单详细信息
  36. 7.2 获取当前持仓 (Open Positions)
  37. positions_total() – 获取持仓总数
  38. positions_get() – 获取持仓详细信息
  39. 7.3 获取历史成交记录 (History Deals)
  40. history_deals_get(date_from, date_to, group="*") – 获取指定时间范围的成交记录
  41. 7.4 数据结构解析与转换为 DataFrame
  42. 错误处理与最佳实践
  43. 8.1 检查函数返回值
  44. 8.2 使用 mt5.last_error()
  45. 8.3 symbol_select 的重要性
  46. 8.4 使用 try...except 结构
  47. 8.5 注意数据获取的限制
  48. 综合示例
  49. 总结

1. 简介与安装

1.1 什么是 MetaTrader5 Python 库?

这是一个允许 Python 脚本直接与本地运行的 MetaTrader 5 交易终端进行通信的库。你可以用它来获取市场数据、账户信息、执行交易操作等。

1.2 环境准备与安装
  • Python: 确保你安装了 Python (建议 3.6 或更高版本)。
  • MetaTrader 5 终端: 你必须在你的电脑上安装 MetaTrader 5 交易终端。
  • 安装库: 使用 pip 安装:
    pip install MetaTrader5 --upgrade
    
  • 1.3 MT5 终端设置

    为了让 Python 库能够连接并执行操作,你需要在 MT5 终端进行设置:

    1. 打开 MT5 终端。
    2. 点击菜单栏 “工具 (Tools)” -> “选项 (Options)”。
    3. 切换到 “EA交易 (Expert Advisors)” 选项卡。
    4. 勾选 “允许 Algo Trading” (Allow automated trading / Allow DLL imports – 不同版本名称可能略有差异)。
    5. 点击 “确定”。
    6. 确保 MT5 终端已经登录到你的交易账户并且正在运行。

    2. 连接与初始化

    2.1 导入库
    import MetaTrader5 as mt5
    from datetime import datetime
    import pandas as pd
    import time # 用于可能的延时
    
    # 设置 Pandas 显示选项(可选)
    pd.set_option('display.max_columns', 500) # 显示更多列
    pd.set_option('display.width', 1500)      # 设置显示宽度
    
    2.2 初始化连接 (initialize)

    这是连接到 MT5 终端的第一步。

    # 尝试初始化连接到 MetaTrader 5 终端
    # 如果 MT5 安装在非标准路径,可能需要指定 path 参数
    # 如果需要登录特定账户,可以传入 login, password, server 参数,但通常 MT5 已登录即可
    if not mt5.initialize():
        print("initialize() failed, error code =", mt5.last_error())
        # 可以选择退出脚本或进行其他错误处理
        quit() # 如果初始化失败,直接退出
    
    print("MetaTrader 5 initialized successfully!")
    print(f"MetaTrader 5 version: {mt5.version()}")
    
    2.3 检查连接状态

    initialize() 的返回值 (True/False) 表明了连接是否成功。

    2.4 关闭连接 (shutdown)

    完成所有操作后,务必关闭与终端的连接,释放资源。

    # 在脚本结束时或不再需要连接时调用
    # mt5.shutdown()
    # print("MetaTrader 5 connection closed.")
    # 暂时注释掉,以便后续代码可以继续执行
    

    注意: 在后续示例中,mt5.shutdown() 会放在脚本的最后。

    3. 获取账户信息

    3.1 account_info()

    此函数返回一个包含当前连接账户详细信息的对象 (如果成功) 或 None (如果失败)。

    # 获取账户信息
    account_info = mt5.account_info()
    
    if account_info is not None:
        print("Account info retrieved successfully:")
        # account_info 是一个 named tuple (或类似结构),可以通过 . 访问属性
        print(f"  Login: {account_info.login}")
        print(f"  Name: {account_info.name}")
        print(f"  Server: {account_info.server}")
        print(f"  Currency: {account_info.currency}")
        print(f"  Balance: {account_info.balance}")
        print(f"  Equity: {account_info.equity}")
        print(f"  Profit: {account_info.profit}")
        print(f"  Margin: {account_info.margin}")
        print(f"  Margin Free: {account_info.margin_free}")
        print(f"  Margin Level: {account_info.margin_level}%")
    else:
        print("Failed to get account info, error code =", mt5.last_error())
    
    # 也可以将账户信息转换为字典方便查看
    account_info_dict = account_info._asdict()
    print("\nAccount info as dictionary:")
    print(account_info_dict)
    
    3.2 账户信息属性解析
  • login: 账户号
  • name: 账户持有人姓名
  • server: 交易服务器名称
  • currency: 账户基础货币
  • balance: 账户余额 (不含未平仓订单盈亏)
  • equity: 账户净值 (余额 + 未平仓订单盈亏)
  • profit: 当前未平仓订单的总盈亏
  • margin: 已用保证金
  • margin_free: 可用保证金
  • margin_level: 保证金水平 (%) = (净值 / 已用保证金) * 100%
  • 还有其他属性如 trade_mode, limit_orders 等。
  • 4. 获取交易品种 (Symbol) 信息

    4.1 什么是交易品种?

    交易品种指的是可以在 MT5 交易的金融工具,例如 “EURUSD” (欧元/美元), “GBPUSD” (英镑/美元), “XAUUSD” (黄金/美元), “AAPL.US” (苹果股票) 等。

    4.2 symbol_select() – 选择品种(重要

    在使用 symbol_info_tick, copy_rates_*, copy_ticks_* 等函数获取特定品种的数据之前,必须确保该品种已经在 MT5 终端的 “市场报价 (Market Watch)” 窗口中可见,或者通过 symbol_select() 将其添加到 Market Watch 中。

    symbol = "EURUSD" # 示例品种
    
    # 尝试将品种添加到 MarketWatch 并使其数据可用
    selected = mt5.symbol_select(symbol, True) # 第二个参数 True 表示选中
    if not selected:
        print(f"Failed to select {symbol}, maybe it's not available on the server?")
        # 尝试取消选择(如果不需要): mt5.symbol_select(symbol, False)
        # 如果无法选择,后续获取该品种数据的操作会失败
    else:
        print(f"{symbol} selected and data available in MarketWatch.")
    
    4.3 symbol_info() – 获取单个品种的详细规格

    获取指定交易品种的详细信息和规格。

    symbol_to_check = "GBPUSD"
    
    # 确保品种被选中
    if not mt5.symbol_select(symbol_to_check, True):
         print(f"Failed to select {symbol_to_check}")
    else:
        # 获取品种信息
        info = mt5.symbol_info(symbol_to_check)
        if info is not None:
            print(f"\nInformation for {symbol_to_check}:")
            print(f"  Digits (小数点位数): {info.digits}")
            print(f"  Point (点值): {info.point}")
            print(f"  Spread (当前点差): {info.spread}")
            print(f"  Contract Size (合约大小): {info.trade_contract_size}")
            print(f"  Tick Value (每点价值): {info.tick_value}")
            print(f"  Tick Size (最小变动单位): {info.tick_size}")
            print(f"  Trade Mode (交易模式): {info.trade_mode}") # e.g., TRADE_MODE_FULL
            print(f"  Description (描述): {info.description}")
    
            # 转换为字典
            # print("\nSymbol info as dictionary:")
            # print(info._asdict())
        else:
            print(f"Failed to get info for {symbol_to_check}, error code =", mt5.last_error())
    
    4.4 symbols_get() – 获取多个品种的信息

    可以按组(例如 “forex”, “metals”)或不指定组(获取 Market Watch 中所有可见的)来获取多个品种的信息。

    # 获取 MarketWatch 中所有可见品种的信息
    all_symbols_info = mt5.symbols_get()
    print(f"\nTotal symbols in MarketWatch: {len(all_symbols_info)}")
    if len(all_symbols_info) > 0:
        print("First few symbols:")
        for i, sym_info in enumerate(all_symbols_info[:5]): # 只显示前 5 个
            print(f"  {i+1}. {sym_info.name} - {sym_info.description}")
    
    # 按组获取 (组名取决于你的 Broker 设置)
    # forex_symbols_info = mt5.symbols_get("group_name") # 例如 "Forex", "Indices"
    # if forex_symbols_info:
    #     print(f"\nForex symbols found: {len(forex_symbols_info)}")
    
    4.5 品种信息属性解析

    SymbolInfo 对象包含大量属性,常用的有:

  • name: 品种名称 (e.g., “EURUSD”)
  • description: 品种描述
  • digits: 价格的小数位数
  • point: 最小价格变动单位 (点)
  • spread: 当前点差 (整数点数)
  • trade_contract_size: 一手交易的合约大小
  • tick_value: 价格变动一个 tick (最小变动单位) 时,对于一手头寸的价值 (账户货币)
  • tick_size: 一个 tick 的价格变动量
  • trade_mode: 交易模式 (允许交易/只允许平仓/不允许交易)
  • bid, ask: 当前买卖价 (注意: 这不是实时更新的,不如 symbol_info_tick 及时)
  • 还有交易时间、保证金要求、隔夜利息等很多细节。
  • 5. 获取实时报价 (Ticks)

    5.1 symbol_info_tick() – 获取最新 Tick

    获取指定品种的最新报价信息 (买价、卖价、最后价等)。

    symbol_tick = "USDJPY"
    
    # 确保选中
    if not mt5.symbol_select(symbol_tick, True):
        print(f"Failed to select {symbol_tick}")
    else:
        # 获取最新 tick
        last_tick = mt5.symbol_info_tick(symbol_tick)
        if last_tick:
            print(f"\nLatest tick for {symbol_tick}:")
            # Tick 时间是 POSIX 时间戳 (自 1970-01-01 以来的秒数)
            tick_time_dt = datetime.fromtimestamp(last_tick.time)
            print(f"  Time: {tick_time_dt.strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"  Bid: {last_tick.bid}")
            print(f"  Ask: {last_tick.ask}")
            print(f"  Last Price: {last_tick.last}") # 如果有最后成交价 (股票/期货)
            print(f"  Volume: {last_tick.volume}") # 如果有成交量
            print(f"  Flags: {last_tick.flags}") # Tick 类型标志
        else:
            print(f"Failed to get tick for {symbol_tick}, error code =", mt5.last_error())
    
    5.2 Tick 数据属性解析

    Tick 对象包含:

  • time: Tick 发生的时间 (POSIX 时间戳)
  • bid: 当前买价
  • ask: 当前卖价
  • last: 最后成交价 (主要用于交易所品种)
  • volume: 最后成交量 (主要用于交易所品种)
  • flags: 描述 Tick 类型的标志位 (例如,是买价变动、卖价变动还是两者都变动)。
  • 5.3 copy_ticks_from() / copy_ticks_range() – 获取历史 Tick 数据

    可以获取指定时间范围或从某个时间点开始的历史 Tick 数据。注意:Tick 数据量非常大,获取长时间范围的 Tick 数据可能消耗大量内存和时间,并可能受到 Broker 服务器的限制。

    symbol_hist_ticks = "EURUSD"
    # 确保选中
    if not mt5.symbol_select(symbol_hist_ticks, True):
        print(f"Failed to select {symbol_hist_ticks}")
    else:
        # 定义时间范围 (例如:过去 1 小时)
        time_to = datetime.now()
        time_from = time_to - pd.Timedelta(hours=1) # 使用 Pandas Timedelta
    
        # 获取 Tick 数据
        # copy_ticks_range(symbol, dt_from, dt_to, flags)
        # flags 控制获取的数据类型: mt5.COPY_TICKS_ALL, mt5.COPY_TICKS_INFO (买卖价), mt5.COPY_TICKS_TRADE (最后价)
        try:
            ticks = mt5.copy_ticks_range(symbol_hist_ticks, time_from, time_to, mt5.COPY_TICKS_ALL)
            if ticks is not None:
                print(f"\nRetrieved {len(ticks)} ticks for {symbol_hist_ticks} in the last hour.")
    
                # 转换为 DataFrame
                ticks_df = pd.DataFrame(ticks)
                # 将时间戳转换为 datetime 对象
                ticks_df['time'] = pd.to_datetime(ticks_df['time'], unit='s')
                ticks_df = ticks_df.set_index('time') # 将时间设为索引
                print("Last 5 ticks:")
                print(ticks_df.tail())
            elif mt5.last_error() == mt5.RES_E_INTERNAL_ERROR: # 常见错误:可能是服务器限制或无数据
                 print(f"Could not retrieve ticks for {symbol_hist_ticks}. No data or server restriction? Error: {mt5.last_error()}")
            else:
                print(f"Failed to retrieve ticks for {symbol_hist_ticks}, error code =", mt5.last_error())
    
        except Exception as e:
            print(f"An exception occurred while fetching ticks: {e}")
    
    

    6. 获取历史 K 线数据 (Bars/Candlesticks)

    这是进行技术分析和策略回测最常用的数据。

    6.1 K 线数据的重要性

    K 线(蜡烛图)包含了每个时间周期内的开盘价 (Open)、最高价 (High)、最低价 (Low)、收盘价 (Close) 和成交量 (Volume),是市场行为的核心表示。

    6.2 时间周期 (Timeframe) 常量

    MetaTrader5 库定义了标准的时间周期常量:

  • mt5.TIMEFRAME_M1: 1 分钟
  • mt5.TIMEFRAME_M5: 5 分钟
  • mt5.TIMEFRAME_M15: 15 分钟
  • mt5.TIMEFRAME_M30: 30 分钟
  • mt5.TIMEFRAME_H1: 1 小时
  • mt5.TIMEFRAME_H4: 4 小时
  • mt5.TIMEFRAME_D1: 日线
  • mt5.TIMEFRAME_W1: 周线
  • mt5.TIMEFRAME_MN1: 月线
  • 6.3 copy_rates_from(symbol, timeframe, start_date, count) – 从指定日期获取

    start_date (datetime 对象) 开始向前获取 count 根 K 线。

    symbol_rates = "EURUSD"
    timeframe = mt5.TIMEFRAME_H1 # 1 小时 K 线
    count = 100 # 获取 100 根
    start_date = datetime(2024, 1, 1) # 指定开始日期 (注意是向前获取)
    
    # 确保选中
    if not mt5.symbol_select(symbol_rates, True):
        print(f"Failed to select {symbol_rates}")
    else:
        rates_from = mt5.copy_rates_from(symbol_rates, timeframe, start_date, count)
        if rates_from is not None and len(rates_from) > 0:
            print(f"\nRetrieved {len(rates_from)} H1 bars for {symbol_rates} starting before {start_date}")
            # rates_from 是 NumPy array
        else:
            print(f"Failed to get rates using copy_rates_from, error code =", mt5.last_error())
    
    6.4 copy_rates_from_pos(symbol, timeframe, start_pos, count) – 从指定位置获取

    从当前时间开始倒数第 start_pos 根 K 线开始,获取 count 根。start_pos=0 表示最新的那根未完成的 K 线。

    start_pos = 0 # 从最新的 K 线开始 (第 0 根)
    count_pos = 50 # 获取 50 根
    
    # 确保选中
    if not mt5.symbol_select(symbol_rates, True):
        print(f"Failed to select {symbol_rates}")
    else:
        rates_pos = mt5.copy_rates_from_pos(symbol_rates, timeframe, start_pos, count_pos)
        if rates_pos is not None and len(rates_pos) > 0:
            print(f"\nRetrieved {len(rates_pos)} H1 bars for {symbol_rates} starting from position {start_pos}")
        else:
            print(f"Failed to get rates using copy_rates_from_pos, error code =", mt5.last_error())
    
    6.5 copy_rates_range(symbol, timeframe, date_from, date_to) – 按日期范围获取 (常用)

    获取从 date_from (包含) 到 date_to (不包含) 之间的 K 线数据。这是最常用的方式。date_fromdate_to 应该是 datetime 对象。

    symbol_rates = "XAUUSD" # 黄金
    timeframe_range = mt5.TIMEFRAME_D1 # 日线
    date_from = datetime(2023, 1, 1)
    date_to = datetime(2024, 1, 1) # 获取 2023 全年的数据
    
    # 确保选中
    if not mt5.symbol_select(symbol_rates, True):
        print(f"Failed to select {symbol_rates}")
    else:
        rates_range = mt5.copy_rates_range(symbol_rates, timeframe_range, date_from, date_to)
        if rates_range is not None and len(rates_range) > 0:
            print(f"\nRetrieved {len(rates_range)} D1 bars for {symbol_rates} from {date_from} to {date_to}")
        else:
            print(f"Failed to get rates using copy_rates_range, error code =", mt5.last_error())
    
    6.6 返回数据格式 (NumPy Array)

    所有 copy_rates_* 函数都返回一个 NumPy 结构化数组 (structured array),或者在失败时返回 None

    6.7 转换为 Pandas DataFrame (推荐)

    NumPy 数组不方便进行时间序列分析,通常会将其转换为 Pandas DataFrame。

    if rates_range is not None and len(rates_range) > 0:
        # 将 NumPy 结构化数组转换为 Pandas DataFrame
        rates_df = pd.DataFrame(rates_range)
    
        # 转换时间戳列 (通常是第一列 'time') 为 datetime 对象
        # MT5 返回的是 POSIX 时间戳 (秒)
        rates_df['time'] = pd.to_datetime(rates_df['time'], unit='s')
    
        # 将 'time' 列设置为索引
        rates_df = rates_df.set_index('time')
    
        # 可以重命名列名,使其更符合习惯 (OHLCV)
        # rates_df.rename(columns={'open': 'Open', 'high': 'High', 'low': 'Low', 'close': 'Close', 'tick_volume': 'Volume'}, inplace=True)
    
        print("\nRates data as Pandas DataFrame (last 5 rows):")
        print(rates_df.tail())
        print("\nDataFrame Info:")
        rates_df.info()
    else:
        print("No rates data to convert to DataFrame.")
    
    6.8 K 线数据字段解析

    DataFrame 中的列通常包括:

  • time: K 线开始时间 (datetime 对象)
  • open: 开盘价
  • high: 最高价
  • low: 最低价
  • close: 收盘价
  • tick_volume: Tick 成交量 (价格变化的次数)
  • spread: K 线结束时的点差 (整数点)
  • real_volume: 真实成交量 (如果 Broker 提供)
  • 7. 获取订单、持仓与交易历史

    这些函数用于获取账户的交易活动信息。

    7.1 获取挂单 (Pending Orders)

    挂单是尚未成交的订单 (如限价单、止损单)。

    # 获取挂单总数
    orders_count = mt5.orders_total()
    print(f"\nTotal pending orders: {orders_count}")
    
    if orders_count > 0:
        # 获取所有挂单的详细信息
        # 可以按 symbol 或 ticket 过滤: orders_get(symbol="EURUSD")
        orders = mt5.orders_get()
        if orders is not None:
            print("Details of pending orders:")
            orders_list = []
            for order in orders:
                orders_list.append(order._asdict()) # 转为字典方便查看
                # print(f"  Ticket: {order.ticket}, Symbol: {order.symbol}, Type: {order.type_str}, Price: {order.price_open}, Volume: {order.volume_current}")
    
            # 转换为 DataFrame (可选)
            orders_df = pd.DataFrame(orders_list)
            # 转换时间相关列
            time_cols_order = ['time_setup', 'time_expiration', 'time_done']
            for col in time_cols_order:
                if col in orders_df.columns:
                     orders_df[col] = pd.to_datetime(orders_df[col], unit='s')
            print(orders_df[['ticket', 'symbol', 'type', 'volume_initial', 'price_open', 'sl', 'tp', 'time_setup']].head())
        else:
            print("Failed to get pending orders details, error code =", mt5.last_error())
    
  • OrderInfo 对象包含订单号 (ticket)、品种 (symbol)、类型 (type – 如 ORDER_TYPE_BUY_LIMIT)、开仓价 (price_open)、止损 (sl)、止盈 (tp)、手数 (volume_initial)、设置时间 (time_setup) 等。
  • 7.2 获取当前持仓 (Open Positions)

    持仓是已经成交并且尚未平仓的头寸。

    # 获取持仓总数
    positions_count = mt5.positions_total()
    print(f"\nTotal open positions: {positions_count}")
    
    if positions_count > 0:
        # 获取所有持仓的详细信息
        # 可以按 symbol 或 ticket 过滤: positions_get(symbol="EURUSD")
        positions = mt5.positions_get()
        if positions is not None:
            print("Details of open positions:")
            positions_list = []
            for position in positions:
                positions_list.append(position._asdict())
                # print(f"  Ticket: {position.ticket}, Symbol: {position.symbol}, Type: {position.type}, Price: {position.price_open}, Profit: {position.profit}, Volume: {position.volume}")
    
            # 转换为 DataFrame (可选)
            positions_df = pd.DataFrame(positions_list)
            time_cols_pos = ['time', 'time_update']
            for col in time_cols_pos:
                 if col in positions_df.columns:
                    positions_df[col] = pd.to_datetime(positions_df[col], unit='s')
            print(positions_df[['ticket', 'symbol', 'type', 'volume', 'price_open', 'sl', 'tp', 'profit', 'time']].head())
        else:
            print("Failed to get open positions details, error code =", mt5.last_error())
    
    
  • PositionInfo 对象包含头寸号 (ticket)、品种 (symbol)、类型 (typePOSITION_TYPE_BUYPOSITION_TYPE_SELL)、开仓价 (price_open)、手数 (volume)、止损 (sl)、止盈 (tp)、当前盈亏 (profit)、开仓时间 (time) 等。
  • 7.3 获取历史成交记录 (History Deals)

    成交记录 (Deals) 是实际发生的买入或卖出操作的记录。一个订单或头寸可能对应多个成交记录 (例如部分成交)。

    # 定义要获取历史记录的时间范围
    hist_date_from = datetime(2024, 1, 1)
    hist_date_to = datetime.now()
    
    # 获取指定时间范围内的所有成交记录
    # 可以按 group (包含/排除的品种掩码) 或 position (特定头寸的 ticket) 过滤
    history_deals = mt5.history_deals_get(hist_date_from, hist_date_to) # 获取所有品种
    
    if history_deals is not None:
        print(f"\nTotal deals found from {hist_date_from} to {hist_date_to}: {len(history_deals)}")
        if len(history_deals) > 0:
            deals_list = []
            for deal in history_deals:
                deals_list.append(deal._asdict())
                # print(f"  Ticket: {deal.ticket}, Order: {deal.order}, Symbol: {deal.symbol}, Type: {deal.type}, Price: {deal.price}, Profit: {deal.profit}, Time: {datetime.fromtimestamp(deal.time)}")
    
            # 转换为 DataFrame
            deals_df = pd.DataFrame(deals_list)
            deals_df['time'] = pd.to_datetime(deals_df['time'], unit='s')
            deals_df = deals_df.sort_values(by='time') # 按时间排序
            print("Last 5 history deals:")
            print(deals_df[['ticket', 'order', 'symbol', 'type', 'entry', 'volume', 'price', 'profit', 'commission', 'fee', 'time']].tail())
        else:
            print("No deals found in the specified period.")
    else:
        print("Failed to get history deals, error code =", mt5.last_error())
    
    
  • TradeDeal 对象包含成交号 (ticket)、关联的订单号 (order)、关联的头寸号 (position_id)、品种 (symbol)、类型 (typeDEAL_TYPE_BUYDEAL_TYPE_SELL)、方向 (entryDEAL_ENTRY_IN, DEAL_ENTRY_OUT, DEAL_ENTRY_INOUT)、成交价 (price)、手数 (volume)、盈亏 (profit)、手续费 (commission)、库存费 (fee)、成交时间 (time) 等。
  • 7.4 数据结构解析与转换为 DataFrame

    如上所示,获取到的订单、持仓、历史成交都是元组 (tuple) of named tuples。将它们转换为 Pandas DataFrame 通常更便于分析和处理。

    8. 错误处理与最佳实践

    8.1 检查函数返回值

    MetaTrader5 库的大多数函数在成功时返回所需的数据(对象、列表、数组等),在失败时返回 NoneFalse每次调用后检查返回值是必须的。

    8.2 使用 mt5.last_error()

    当函数调用失败时,使用 mt5.last_error() 获取具体的错误代码和描述,这对于调试至关重要。错误代码的含义可以在 MQL5 文档中查找。

    8.3 symbol_select 的重要性

    再次强调,在获取特定品种的 Tick 或 K 线数据前,必须使用 mt5.symbol_select(symbol, True) 确保该品种在 MarketWatch 中是激活状态。

    8.4 使用 try...except 结构

    对于可能发生网络问题或预期外错误的操作(尤其是历史数据获取),使用 try...except 可以增加脚本的健壮性。

    8.5 注意数据获取的限制
  • 历史数据深度: Broker 可能只提供有限深度的历史数据(尤其是 Tick 数据和分钟级别数据)。
  • 请求频率: 过于频繁地请求大量数据可能会被 Broker 服务器限制。在循环中获取数据时,适当加入 time.sleep()
  • Tick 数据量: 获取长时间的 Tick 数据非常耗资源。
  • 9. 综合示例

    下面是一个简单的脚本,演示了连接、获取账户信息、获取 EURUSD 的最新 Tick 和最近 10 条 H1 K 线数据,并转换为 DataFrame 的完整流程。

    import MetaTrader5 as mt5
    from datetime import datetime
    import pandas as pd
    import time
    
    # --- 1. 初始化连接 ---
    if not mt5.initialize():
        print("initialize() failed, error code =", mt5.last_error())
        quit()
    print("MT5 Initialized")
    
    # --- 2. 获取账户信息 ---
    account_info = mt5.account_info()
    if account_info:
        print(f"Account login: {account_info.login}, Balance: {account_info.balance} {account_info.currency}")
    else:
        print("Failed to get account info")
    
    # --- 3. 获取 EURUSD 数据 ---
    symbol = "EURUSD"
    timeframe = mt5.TIMEFRAME_H1
    bars_to_get = 10
    
    # 确保选中
    if not mt5.symbol_select(symbol, True):
        print(f"Failed to select {symbol}, error code =", mt5.last_error())
        mt5.shutdown()
        quit()
    print(f"{symbol} selected.")
    time.sleep(0.5) # 短暂等待数据同步
    
    # 获取最新 Tick
    tick = mt5.symbol_info_tick(symbol)
    if tick:
        tick_time = datetime.fromtimestamp(tick.time).strftime('%Y-%m-%d %H:%M:%S')
        print(f"\nLatest {symbol} Tick ({tick_time}): Bid={tick.bid}, Ask={tick.ask}")
    else:
        print(f"Failed to get {symbol} tick, error code =", mt5.last_error())
    
    # 获取最近的 K 线
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, bars_to_get)
    if rates is not None and len(rates) > 0:
        rates_df = pd.DataFrame(rates)
        rates_df['time'] = pd.to_datetime(rates_df['time'], unit='s')
        rates_df = rates_df.set_index('time')
        print(f"\nLast {len(rates_df)} H1 bars for {symbol}:")
        print(rates_df[['open', 'high', 'low', 'close', 'tick_volume']])
    else:
        print(f"Failed to get {symbol} rates, error code =", mt5.last_error())
    
    # --- 4. 关闭连接 ---
    mt5.shutdown()
    print("\nMT5 Connection closed.")
    

    10. 总结

    本教程详细介绍了如何使用 MetaTrader5 Python 库连接到 MT5 终端,并重点演示了获取各种关键金融数据的方法:

  • 账户信息 (account_info)
  • 交易品种规格 (symbol_info, symbols_get)
  • 实时报价 Ticks (symbol_info_tick, copy_ticks_*)
  • 历史 K 线数据 (copy_rates_*),并强调了转换为 Pandas DataFrame 的方法
  • 挂单 (orders_get)、持仓 (positions_get) 和历史成交 (history_deals_get)
  • 掌握这些数据获取方法是利用 Python 进行量化交易分析、策略开发和执行交易的基础。务必注意错误处理和 MT5 终端的正确设置。要了解交易执行相关的功能,请查阅官方文档或其他教程。

    作者:hiquant

    物联沃分享整理
    物联沃-IOTWORD物联网 » MetaTrader5 Python版深度解析与实战教程

    发表回复