Skip to content

原生Python支持

自动化交易的潜在成功在很大程度上取决于实现该理念时可用技术的广度。正如我们在前面章节中所看到的,MQL5允许你突破严格的应用交易任务范畴,为与外部服务集成(例如基于网络功能和自定义符号)、使用关系型数据库处理和存储数据以及连接任意库提供了机会。

最后一点使得与任何以DLL格式提供API的软件进行交互成为可能。一些开发者利用这种方法连接到工业级分布式数据库管理系统(而非内置的SQLite)、像R或MATLAB这样的数学软件包,以及其他编程语言。

Python已成为最受欢迎的编程语言之一。它的特点是核心精简,并且有各种现成的脚本集合(即软件包)来构建应用解决方案。交易者可从丰富多样的软件包及其强大功能中受益,这些软件包可用于基础市场分析(统计计算、数据可视化)以及交易假设测试,包括机器学习。

顺应这一趋势,MQ于2019年在MQL5中引入了对Python的支持。这种更紧密的“开箱即用”集成允许将技术分析和交易算法完全迁移到Python环境中。

从技术角度来看,集成是通过在Python中安装“MetaTrader5”软件包来实现的,该软件包可组织与终端的进程间交互(在撰写本文时,是通过ipykernel/RPC机制实现的)。

该软件包的功能中,有与内置MQL5函数完全对应的功能,可用于获取有关终端、交易账户、市场报价窗口中的交易品种、报价、行情数据、市场深度、订单、持仓和交易的信息。此外,该软件包还允许切换交易账户、发送交易订单、检查保证金要求以及实时评估潜在的盈亏情况。

然而,与Python的集成存在一些限制。特别是在Python中无法实现诸如OnTick、OnBookEvent等事件处理。因此,有必要使用无限循环来检查新价格,这与我们在MQL5脚本中不得不做的类似。分析交易订单的执行情况同样困难:由于没有OnTradeTransaction事件,需要编写更多代码才能了解持仓是全部还是部分平仓。为了绕过这些限制,你可以组织Python脚本和MQL5之间的交互,例如通过套接字。mql5.com网站上有介绍实现这种桥梁的示例文章。

因此,在处理报价、行情数据或交易账户历史的机器学习任务中,将Python与MetaTrader 5结合使用似乎是理所当然的。遗憾的是,在Python中无法获取指标读数。

安装Python和MetaTrader5包

要学习本章的内容,你的计算机上必须安装Python。如果你还未安装,可从https://www.python.org/downloads/windows 下载最新版本的Python(例如,在撰写本文时是3.10版本)。

安装Python时,建议勾选“Add Python to PATH”选项,这样你就能从任意文件夹的命令行运行Python脚本。

Python下载并安装完成后,可通过命令行安装MetaTrader5模块(这里的pip是标准的Python包管理程序):

pip install MetaTrader5

随后,你可以使用以下命令行检查该包是否有更新:

pip install --upgrade MetaTrader5

安装其他常用包的语法与之类似。特别是,很多脚本需要数据分析和可视化包,分别是pandas和matplotlib。

pip install matplotlib
pip install pandas

你可以直接在MetaEditor的MQL5向导中创建新的Python脚本。除了脚本名称外,用户还能选择导入多个包的选项,比如TensorFlow、NumPy或Datetime。

默认情况下,建议将脚本放置在MQL5/Scripts文件夹中。新创建和已有的Python脚本会显示在MetaTrader 5导航器中,并带有特殊图标标记,且可以像平常一样从导航器中启动。Python脚本可以和其他MQL5脚本以及智能交易系统在图表上并行执行。如果脚本进入循环执行状态,要停止它,只需将其从图表中移除即可。

在终端中运行Python脚本

从终端启动的Python脚本会通过命令行参数接收交易品种名称和图表的时间周期。例如,我们可以在EURUSD的H1图表上运行以下脚本,其中的参数可以通过sys.argv数组获取:

python
import sys

print('The command line arguments are:')
for i in sys.argv:
    print(i)

它会在专家日志中输出:

The command line arguments are:
C:\Program Files\MetaTrader 5\MQL5\Scripts\MQL5Book\Python\python-args.py
EURUSD
60

此外,你可以在MetaEditor的设置对话框中指定Python的安装位置,然后在“编译器”选项卡中操作,这样就能直接从MetaEditor运行Python脚本。此时,扩展名为*.py的文件的编译命令就变成了运行命令。

最后,也可以在Python的原生环境中运行Python脚本,方法是从命令行调用python.exe并传入脚本作为参数,或者使用其他适配Python的集成开发环境(IDE),比如Jupyter Notebook。

如果终端中启用了算法交易,那么默认情况下也会启用从Python进行交易的功能。为了在使用第三方Python库时进一步保护账户,平台设置中提供了“Disable automatic trading via external Python API”(禁用通过外部Python API进行自动交易)选项。这样,Python脚本可以选择性地阻止交易,而让MQL程序仍可进行交易。启用此选项后,Python脚本中的交易函数调用将返回错误代码10027(TRADE_RETCODE_CLIENT_DISABLES_AT),表明客户端终端已禁用算法交易。

MQL5与Python对比

Python是一种解释型语言,这与编译型的MQL5不同。对于开发者而言,这让我们的工作更轻松,因为无需单独的编译阶段就能得到可运行的程序。不过,Python脚本的执行速度明显低于MQL5编译后的程序。

Python是动态类型语言:变量的类型由赋给它的值决定。一方面,这带来了灵活性,但也要求我们格外谨慎,以避免出现意外错误。而MQL5使用静态类型,也就是说,在描述变量时,必须明确指定其类型,编译器会检查类型兼容性。

Python会自动进行“垃圾清理”,即释放应用程序为对象分配的内存。在MQL5中,我们需要手动及时调用delete来释放动态对象的内存。

在Python语法中,源代码的缩进起着重要作用。如果需要编写包含多个嵌套语句块的复合语句(例如循环或条件语句),Python使用空格或制表符来实现(在语句块内,它们的大小必须一致),不允许混用制表符和空格。缩进错误会导致程序出错。在MQL5中,我们通过用花括号{ ... }括起来的方式来形成复合语句块,但格式本身并不影响程序性能,你可以采用任何喜欢的风格进行编写。

Python函数支持两种类型的参数:命名参数和位置参数。位置参数与我们在MQL5中习惯的参数类型相对应:每个参数的值必须严格按照其在参数列表中的顺序传递(根据函数原型)。相比之下,命名参数以名称和值的组合形式传递(中间用'='连接),因此可以按任意顺序指定,例如func(param2 = value2, param1 = value1)。

Python中MetaTrader5包的函数概述

Python中可用的API函数可以有条件地分为两组:在MQL5 API中有完全对应函数的一组,以及仅在Python中可用的一组函数。第二组函数存在的部分原因是,在使用应用函数之前,必须在技术上组织好Python与MetaTrader 5之间的连接。这就解释了initializeshutdown这一对函数的存在和用途:第一个函数用于建立与终端的连接,第二个函数用于终止该连接。

重要的是,在初始化过程中,可以启动所需的终端副本(如果尚未执行),并且可以选择特定的交易账户。此外,在已经打开与终端的连接的情况下,也可以更改交易账户:这通过login函数来完成。

在连接到终端后,Python脚本可以使用version函数获取终端版本的摘要信息。通过terminal_info可以获取关于终端的完整信息,它完全类似于三个TerminalInfo函数,就好像它们被合并为一个调用一样。

以下表格列出了Python应用函数及其在MQL5 API中的对应函数:

PythonMQL5
last_errorGetLastError(注意!Python有其自身的错误代码)
account_infoAccountInfoIntegerAccountInfoDoubleAccountInfoString
terminal_infoTerminalInfoIntegerTerminalInfoDoubleTerminalInfoDouble
symbols_totalSymbolsTotal(所有符号,包括自定义和已禁用的符号)
symbols_getSymbolsTotal + SymbolInfo函数
symbol_infoSymbolInfoIntegerSymbolInfoDoubleSymbolInfoString
symbol_info_tickSymbolInfoTick
symbol_selectSymbolSelect
market_book_addMarketBookAdd
market_book_getMarketBookGet
market_book_releaseMarketBookRelease
copy_rates_fromCopyRates(按柱数,从日期/时间开始)
copy_rates_from_posCopyRates(按柱数,从柱编号开始)
copy_rates_rangeCopyRates(在日期/时间范围内)
copy_ticks_fromCopyTicks(按Ticks数,从指定时间开始)
copy_ticks_rangeCopyTicksRange(在指定时间范围内)
orders_totalOrdersTotal
orders_getOrdersTotal + OrderGet函数
order_calc_marginOrderCalcMargin
order_calc_profitOrderCalcProfit
order_checkOrderCheck
order_sendOrderSend
positions_totalPositionsTotal
positions_getPositionsTotal + PositionGet函数
history_orders_totalHistoryOrdersTotal
history_orders_getHistoryOrdersTotal + HistoryOrderGet函数
history_deals_totalHistoryDealsTotal
history_deals_getHistoryDealsTotal + HistoryDealGet函数

Python API中的函数有几个特点:

如前所述,函数可以有命名参数:在调用函数时,这些参数连同名称和值一起指定,在每一对名称和值中,它们用等号=组合在一起。指定命名参数的顺序并不重要(这与MQL5中使用的位置参数不同,位置参数必须遵循函数原型指定的严格顺序)。

Python函数对Python原生的数据类型进行操作。这不仅包括常见的数字和字符串,还包括几种复合类型,有点类似于MQL5的数组和结构体。

例如,许多函数返回特殊的Python数据结构:元组(tuple)和具名元组(namedtuple)。

元组是任意类型元素的序列。可以将其视为一个数组,但与数组不同的是,元组的元素可以是不同的类型。也可以将元组看作是一组结构体字段。

具名元组与结构体的相似性更近,其中每个元素都有一个标识符。在普通元组中,只能使用索引(方括号,就像在MQL5中一样,即[i])来访问元素。然而,对于具名元组,我们可以应用解引用操作符(点.)来获取它的“属性”,就像在MQL5结构体中一样(tuple.field)。

此外,元组和具名元组在代码中不能被编辑(也就是说,它们是常量)。

另一种流行的类型是字典(dictionary):一种关联数组,存储键值对,并且键和值的类型都可以变化。使用操作符[]来访问字典的值,键(无论其类型是什么,例如字符串)在方括号之间指定,这使得字典类似于数组。字典不能有两个具有相同键的键值对,也就是说,键总是唯一的。特别是,具名元组可以使用namedtuple._asdict()方法轻松转换为字典。

将Python脚本连接到终端和账户

initialize函数用于建立与MetaTrader 5终端的连接,它有两种形式:简短形式(无参数)和完整形式(带有几个可选参数,第一个参数是路径,为位置参数,其余参数均为命名参数)。

python
bool initialize()
python
bool initialize(path, account = <ACCOUNT>, password = <"PASSWORD">,
  server = <"SERVER">, timeout = 60000, portable = False)

path参数用于设置终端文件(metatrader64.exe)的路径(请注意,与其他参数不同,这是一个未命名参数,所以如果指定了该参数,它必须在参数列表中排在首位)。

如果未指定路径,模块将尝试自行查找可执行文件(开发者未透露确切的查找算法)。为了避免歧义,请使用带参数的第二种函数形式。

在account参数中,可以指定交易账户的号码。如果未指定,将使用所选终端实例中最后一个交易账户。

交易账户的密码在password参数中指定,也可以省略:在这种情况下,将自动使用终端数据库中为指定交易账户存储的密码。

server参数的处理方式与交易服务器名称类似(按照在终端中指定的名称):如果未指定,将自动使用终端数据库中为指定交易账户保存的服务器名称。

timeout参数表示连接的超时时间(以毫秒为单位)(如果超过该时间,将发生错误)。默认值为60000(60秒)。

portable参数包含一个标志,用于指示是否以便携模式启动终端(默认值为False)。

如果成功连接到MetaTrader 5终端,该函数返回True,否则返回False。

如有必要,在调用initialize函数时,可以启动MetaTrader 5终端。

例如,连接到特定交易账户的操作如下所示。

python
import MetaTrader5 as mt5 
if not mt5.initialize(login = 562175752, server = "MetaQuotes-Demo", password = "abc"):
   print("initialize() failed, error code =", mt5.last_error()) 
   quit()
...

login函数也会使用指定的参数连接到交易账户。但这意味着已经建立了与终端的连接,也就是说,该函数通常用于切换账户。

python
bool login(account, password = <"PASSWORD">, server = <"SERVER">, timeout = 60000)

交易账户号码在account参数中提供。这是一个必需的未命名参数,即它必须在参数列表中排在首位。

password、server和timeout参数与initialize函数中的相关参数相同。

如果成功连接到交易账户,该函数返回True,否则返回False。

python
shutdown()

shutdown函数关闭之前建立的与MetaTrader 5终端的连接。

上述函数的示例将在下一节中给出。

建立连接后,脚本可以查询终端的版本信息。

python
tuple version()

version函数以包含三个值的元组形式返回有关MetaTrader 5终端版本的简要信息:版本号、构建版本号和构建日期。

字段类型描述
integerMetaTrader 5终端版本(当前为500)
integer构建版本号(例如3456)
string构建日期(例如'25 Feb 2022')

如果发生错误,该函数返回None,并且可以使用last_error获取错误代码。

可以使用terminal_info函数获取有关终端的更完整信息。

错误检查:last_error函数

last_error函数返回有关上一个Python错误的信息。

python
int last_error()

整数错误代码与为MQL5错误分配并由标准GetLastError函数返回的代码不同。在下表中,缩写“IPC”指的是“进程间通信(Inter-Process Communication)”这一术语。

常量含义描述
RES_S_OK1成功
RES_E_FAIL-1一般性错误
RES_E_INVALID_PARAMS-2无效参数
RES_E_NO_MEMORY-3内存分配错误
RES_E_NOT_FOUND-4未找到请求的历史记录
RES_E_INVALID_VERSION-5不支持的版本
RES_E_AUTH_FAILED-6授权错误
RES_E_UNSUPPORTED-7不支持的方法
RES_E_AUTO_TRADING_DISABLED-8算法交易已禁用
RES_E_INTERNAL_FAIL-10000一般性内部进程间通信错误
RES_E_INTERNAL_FAIL_SEND-10001发送进程间通信数据时的内部错误
RES_E_INTERNAL_FAIL_RECEIVE-10002接收进程间通信数据时的内部错误
RES_E_INTERNAL_FAIL_INIT-10003进程间通信内部初始化错误
RES_E_INTERNAL_FAIL_CONNECT-10003无进程间通信连接
RES_E_INTERNAL_FAIL_TIMEOUT-10005进程间通信超时

在下面的脚本(MQL5/Scripts/MQL5Book/Python/init.py)中,当连接到终端出现错误时,我们会显示错误代码并退出。

python
import MetaTrader5 as mt5
# 显示MetaTrader5软件包的版本
print("MetaTrader5 package version: ", mt5.__version__)  #  5.0.37
   
# 尝试建立连接或启动MetaTrader 5终端
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error()) 
   quit()
... # 脚本的工作部分将在此处
# 终止与终端的连接
mt5.shutdown()

获取交易账户信息

account_info 函数可获取当前交易账户的完整信息。

plaintext
namedtuple account_info()

该函数以命名元组(namedtuple)结构的形式返回信息。若出现错误,结果将为 None

使用此函数,你只需调用一次,就能获取 MQL5 中 AccountInfoIntegerAccountInfoDoubleAccountInfoString 函数所提供的所有信息,涵盖所有支持的属性。元组中的字段名与枚举元素名对应,但去掉了 “ACCOUNT_” 前缀,并转换为小写形式。

本书附带了以下脚本 MQL5/Scripts/MQL5Book/Python/accountinfo.py

python
import MetaTrader5 as mt5

# 建立与 MetaTrader 5 终端的连接
if not mt5.initialize():
    print("initialize() 失败,错误代码 =", mt5.last_error())
    quit()

account_info = mt5.account_info()
if account_info is not None:
    # 直接显示交易账户数据
    print(account_info)
    # 以字典形式显示交易账户数据
    print("显示 account_info()._asdict():")
    account_info_dict = account_info._asdict()
    for prop in account_info_dict:
        print(f"  {prop}={account_info_dict[prop]}")

# 断开与 MetaTrader 5 终端的连接
mt5.shutdown()

运行结果大致如下:

plaintext
AccountInfo(login=25115284, trade_mode=0, leverage=100, limit_orders=200, margin_so_mode=0, ... 

显示 account_info()._asdict(): 

  login=25115284 

  trade_mode=0 

  leverage=100 

  limit_orders=200 

  margin_so_mode=0 

  trade_allowed=True 

  trade_expert=True 

  margin_mode=2 

  currency_digits=2 

  fifo_close=False 

  balance=99511.4 

  credit=0.0 

  profit=41.82 

  equity=99553.22 

  margin=98.18 

  margin_free=99455.04 

  margin_level=101398.67590140559 

  margin_so_call=50.0 

  margin_so_so=30.0 

  margin_initial=0.0 

  margin_maintenance=0.0 

  assets=0.0 

  liabilities=0.0 

  commission_blocked=0.0 

  name=MetaQuotes Dev Demo 

  server=MetaQuotes - Demo 

  currency=USD 

  company=MetaQuotes Software Corp.

这段内容主要介绍了如何使用 MetaTrader5 库中的 account_info 函数获取交易账户的详细信息,并通过一个 Python 脚本展示了具体的使用方法和输出示例。

获取终端信息

terminal_info函数允许获取已连接的MetaTrader 5终端的状态和参数。

python
namedtuple terminal_info()

如果成功,该函数会将信息作为具名元组(namedtuple)结构返回;如果出现错误,则返回None

在调用此函数一次的情况下,你可以获取MQL5中TerminalInfoIntegerTerminalInfoDoubleTerminalInfoDouble所提供的所有信息,包括所有支持属性的变体。元组中字段的名称与枚举元素的名称相对应,但去掉了“TERMINAL_”前缀,并转换为小写形式。

例如(请参阅MQL5/Scripts/MQL5Book/Python/terminalinfo.py):

python
import MetaTrader5 as mt5
  
# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error())
   quit() 
   
# 显示MetaTrader 5版本的简要信息
print(mt5.version()) 
# 显示终端设置和状态的完整信息
terminal_info = mt5.terminal_info()
if terminal_info != None: 
   # 按原样显示终端数据
   print(terminal_info) 
   # 将数据显示为字典
   print("Show terminal_info()._asdict():")
   terminal_info_dict = mt5.terminal_info()._asdict()
   for prop in terminal_info_dict: 
      print("  {}={}".format(prop, terminal_info_dict[prop]))
   
# 断开与MetaTrader 5终端的连接
mt5.shutdown()

输出结果可能如下所示:

[500, 3428, '14 Sep 2022'] 

TerminalInfo(community_account=True, community_connection=True, connected=True,.... 

Show terminal_info()._asdict(): 

  community_account=True 

  community_connection=True 

  connected=True 

  dlls_allowed=False 

  trade_allowed=False 

  tradeapi_disabled=False 

  email_enabled=False 

  ftp_enabled=False 

  notifications_enabled=False 

  mqid=False 

  build=2366 

  maxbars=5000 

  codepage=1251 

  ping_last=77850 

  community_balance=707.10668201585 

  retransmission=0.0 

  company=MetaQuotes Software Corp. 

  name=MetaTrader 5 

  language=Russian 

  path=E:\ProgramFiles\MetaTrader 5 

  data_path=E:\ProgramFiles\MetaTrader 5 

  commondata_path=C:\Users\User\AppData\Roaming\MetaQuotes\Terminal\Common

获取金融工具信息

MetaTrader5包中的一组函数可用于获取有关金融工具的信息。

symbol_info函数以具名元组结构的形式返回关于一种金融工具的信息。

python
namedtuple symbol_info(symbol)

所需金融工具的名称在symbol参数中指定。

一次调用该函数就能提供所有可通过MQL5中的SymbolInfoInteger、SymbolInfoDouble和SymbolInfoString这三个函数及其所有属性获取的信息。具名元组中的字段名称与上述指定函数中使用的枚举元素名称相同,但去掉了“SYMBOL_”前缀,并且为小写形式。

如果发生错误,该函数返回None。

注意!为确保函数成功执行,必须在“市场报价”中选择所请求的交易品种。这可以在Python中通过调用symbol_select函数来实现(详见后文)。

示例(MQL5/Scripts/MQL5Book/Python/eurjpy.py):

python
import MetaTrader5 as mt5

# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error())
   quit()

# 确保EURJPY在“市场报价”中,否则终止算法
selected = mt5.symbol_select("EURJPY", True)
if not selected:
   print("Failed to select EURJPY")
   mt5.shutdown()
   quit()

# 显示EURJPY交易品种的属性
symbol_info = mt5.symbol_info("EURJPY")
if symbol_info != None:
   # 按原样显示数据(作为元组)
   print(symbol_info)
   # 输出几个特定属性
   print("EURJPY: spread =", symbol_info.spread, ", digits =", symbol_info.digits)
   # 将交易品种属性作为字典输出
   print("Show symbol_info(\"EURJPY\")._asdict():")
   symbol_info_dict = mt5.symbol_info("EURJPY")._asdict()
   for prop in symbol_info_dict:
      print("  {}={}".format(prop, symbol_info_dict[prop]))

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

结果:

SymbolInfo(custom=False, chart_mode=0, select=True, visible=True, session_deals=0, session_buy_orders=0, session_sell_orders=0, ... 

EURJPY: spread = 17, digits = 3 

Show symbol_info()._asdict(): 

  custom=False 

  chart_mode=0 

  select=True 

  visible=True 

  ...

  time=1585069682 

  digits=3 

  spread=17 

  spread_float=True 

  ticks_bookdepth=10 

  trade_calc_mode=0 

  trade_mode=4 

  ...

  trade_exemode=1 

  swap_mode=1 

  swap_rollover3days=3 

  margin_hedged_use_leg=False 

  expiration_mode=7 

  filling_mode=1 

  order_mode=127 

  order_gtc_mode=0 

  ...

  bid=120.024 

  ask=120.041 

  last=0.0 

  ...

  point=0.001 

  trade_tick_value=0.8977708350166538 

  trade_tick_value_profit=0.8977708350166538 

  trade_tick_value_loss=0.8978272580355541 

  trade_tick_size=0.001 

  trade_contract_size=100000.0 

  ...

  volume_min=0.01 

  volume_max=500.0 

  volume_step=0.01 

  volume_limit=0.0 

  swap_long=-0.2 

  swap_short=-1.2 

  margin_initial=0.0 

  margin_maintenance=0.0 

  margin_hedged=100000.0 

  ...

  currency_base=EUR 

  currency_profit=JPY 

  currency_margin=EUR 

  ...
python
bool symbol_select(symbol, enable = None)

symbol_select函数用于将指定的交易品种添加到“市场报价”中或从其中移除。第一个参数指定交易品种。第二个参数传入True或False,分别表示显示或隐藏该交易品种。

如果省略第二个可选的未命名参数,根据Python的类型转换规则,bool(none)等同于False。

该函数是SymbolSelect函数的Python版本。

python
int symbols_total()

symbols_total函数返回MetaTrader 5终端中所有工具的数量,包括自定义交易品种以及当前未在“市场报价”窗口中显示的交易品种。这是函数SymbolsTotal(false)的Python版本。

接下来,symbols_get函数返回一个元组数组,其中包含有关所有工具或收藏工具的信息,这些工具的名称与可选命名参数group中指定的过滤器匹配。

python
tuple[] symbols_get(group = "PATTERN")

数组元组中的每个元素都是一个具名元组,包含了交易品种的全套属性(我们在上面描述symbol_info函数时看到过类似的元组)。

由于只有一个参数,调用函数时可以省略其名称。

如果发生错误,该函数将返回特殊值None。

group参数允许按名称选择交易品种,还可以在搜索字符串的开头和/或结尾处使用通配符“”(可选)。“”表示0个或任意数量的字符。因此,你可以搜索名称中出现的子字符串,该子字符串前后可以有任意数量的其他字符。例如,“EUR*”表示以“EUR”开头且具有任意名称扩展名的交易品种(或者就是“EUR”本身)。“EUR”过滤器将返回名称中任何位置包含“EUR”子字符串的交易品种。

此外,group参数可以包含多个由逗号分隔的条件。每个条件都可以使用“”指定为掩码。要排除某些交易品种,可以使用逻辑非运算符“!”。在这种情况下,所有条件将按顺序应用,即首先需要指定包含条件,然后是排除条件。例如,group=", !EUR"表示我们首先需要选择所有交易品种,然后排除名称中(任意位置)包含“EUR”的交易品种。

例如,要显示除4种主要外汇货币对之外的交叉货币汇率信息,可以运行以下查询:

python
crosses = mt5.symbols_get(group = "*,!*USD*,!*EUR*,!*JPY*,!*GBP*")
print('len(*,!*USD*,!*EUR*,!*JPY*,!*GBP*):', len(crosses)) # 结果数组的大小 - 交叉货币对的数量
for s in crosses: 
   print(s.name, ":", s)

结果示例:

len(*,!*USD*,!*EUR*,!*JPY*,!*GBP*):  10 

AUDCAD : SymbolInfo(custom=False, chart_mode=0, select=True, visible=True, session_deals=0, session_buy_orders=0, session... 

AUDCHF : SymbolInfo(custom=False, chart_mode=0, select=True, visible=True, session_deals=0, session_buy_orders=0, session... 

AUDNZD : SymbolInfo(custom=False, chart_mode=0, select=True, visible=True, session_deals=0, session_buy_orders=0, session... 

CADCHF : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

NZDCAD : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

NZDCHF : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

NZDSGD : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

CADMXN : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

CHFMXN : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi... 

NZDMXN : SymbolInfo(custom=False, chart_mode=0, select=False, visible=False, session_deals=0, session_buy_orders=0, sessi...

symbol_info_tick函数可用于获取指定金融工具的最新报价。

python
tuple symbol_info_tick(symbol)

唯一的必需参数指定金融工具的名称。

返回的信息是一个元组,其字段与MqlTick结构中的字段相同。该函数是SymbolInfoTick函数的Python版本。

如果发生错误,返回None。

为使该函数正常工作,必须在“市场报价”中启用相应的交易品种。我们在脚本MQL5/Scripts/MQL5Book/Python/gbpusdtick.py中演示这一点。

python
import MetaTrader5 as mt5

# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error())
   quit()

# 尝试将GBPUSD交易品种添加到“市场报价”中
selected=mt5.symbol_select("GBPUSD", True)
if not selected:
   print("Failed to select GBPUSD")
   mt5.shutdown()
   quit()

# 将GBPUSD交易品种的最新报价显示为元组
lasttick = mt5.symbol_info_tick("GBPUSD")
print(lasttick)
# 以字典形式显示报价字段的值
print("Show symbol_info_tick(\"GBPUSD\")._asdict():")
symbol_info_tick_dict = lasttick._asdict()
for prop in symbol_info_tick_dict:
   print("  {}={}".format(prop, symbol_info_tick_dict[prop]))

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

结果应如下所示:

Tick(time=1585070338, bid=1.17264, ask=1.17279, last=0.0, volume=0, time_msc=1585070338728, flags=2, volume_real=0.0) 

Show symbol_info_tick._asdict(): 

  time=1585070338 

  bid=1.17264 

  ask=1.17279 

  last=0.0 

  volume=0 

  time_msc=1585070338728 

  flags=2 

  volume_real=0.0

订阅订单簿变化

Python API 包含三个用于处理订单簿的函数。

1. market_book_add(symbol)

python
bool market_book_add(symbol)

market_book_add 函数用于订阅指定交易品种的订单簿变化事件。所需金融工具的名称通过单个未命名参数指定。

该函数返回一个布尔值,表示操作是否成功。

此函数是 MQL5 中 MarketBookAdd 函数的等效实现。在完成订单簿相关工作后,应调用 market_book_release 函数取消订阅(详见后文)。

2. market_book_get(symbol)

python
tuple[] market_book_get(symbol)

market_book_get 函数用于请求指定交易品种的当前订单簿内容。结果以 BookInfo 记录的元组(数组)形式返回。每个记录是 MQL5 中 MqlBookInfo 结构体的等效形式,从 Python 角度看,它是一个命名元组,包含 typepricevolumevolume_dbl 字段。注意,在 Python 中该字段名为 volume_dbl,而在 MQL5 中对应的字段名为 volume_real。若出现错误,则返回 None

要使用此函数,必须先使用 market_book_add 函数订阅订单簿事件。

此函数是 MQL5 中 MarketBookGet 函数的等效实现。需注意,Python 脚本无法直接接收 OnBookEvent 事件,需要在循环中轮询订单簿内容。

3. market_book_release(symbol)

python
bool market_book_release(symbol)

market_book_release 函数用于取消对指定交易品种订单簿变化事件的订阅。操作成功时,函数返回 True。此函数是 MQL5 中 MarketBookRelease 函数的等效实现。

下面是一个简单的示例(见 MQL5/Scripts/MQL5Book/Python/eurusdbook.py):

python
import MetaTrader5 as mt5
import time               # 导入用于暂停的包

# 建立与 MetaTrader 5 终端的连接
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    mt5.shutdown()
    quit()

# 订阅 EURUSD 交易品种的订单簿更新
if mt5.market_book_add('EURUSD'):
    # 循环 10 次以读取订单簿数据
    for i in range(10):
        # 获取订单簿内容
        items = mt5.market_book_get('EURUSD')
        # 直接将整个订单簿内容打印在一行中
        print(items)
        # 为清晰起见,将每个价格水平以字典形式单独打印
        for it in items or []:
            print(it._asdict())
        # 在下一次请求订单簿数据前暂停 5 秒
        time.sleep(5) 
    # 取消对订单簿变化的订阅
    mt5.market_book_release('EURUSD')
else:
    print("mt5.market_book_add('EURUSD') failed, error code =", mt5.last_error())

# 结束与 MetaTrader 5 终端的连接
mt5.shutdown()

结果示例

(BookInfo(type=1, price=1.20036, volume=250, volume_dbl=250.0), BookInfo(type=1, price=1.20029, volume=100, volume...
{'type': 1, 'price': 1.20036, 'volume': 250, 'volume_dbl': 250.0}
{'type': 1, 'price': 1.20029, 'volume': 100, 'volume_dbl': 100.0}
{'type': 1, 'price': 1.20028, 'volume': 50, 'volume_dbl': 50.0}
{'type': 1, 'price': 1.20026, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20023, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20022, 'volume': 50, 'volume_dbl': 50.0}
{'type': 2, 'price': 1.20021, 'volume': 100, 'volume_dbl': 100.0}
{'type': 2, 'price': 1.20014, 'volume': 250, 'volume_dbl': 250.0}
(BookInfo(type=1, price=1.20035, volume=250, volume_dbl=250.0), BookInfo(type=1, price=1.20029, volume=100, volume...
{'type': 1, 'price': 1.20035, 'volume': 250, 'volume_dbl': 250.0}
{'type': 1, 'price': 1.20029, 'volume': 100, 'volume_dbl': 100.0}
{'type': 1, 'price': 1.20027, 'volume': 50, 'volume_dbl': 50.0}
{'type': 1, 'price': 1.20025, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20023, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20022, 'volume': 50, 'volume_dbl': 50.0}
{'type': 2, 'price': 1.20021, 'volume': 100, 'volume_dbl': 100.0}
{'type': 2, 'price': 1.20014, 'volume': 250, 'volume_dbl': 250.0}
(BookInfo(type=1, price=1.20037, volume=250, volume_dbl=250.0), BookInfo(type=1, price=1.20031, volume=100, volume...
{'type': 1, 'price': 1.20037, 'volume': 250, 'volume_dbl': 250.0}
{'type': 1, 'price': 1.20031, 'volume': 100, 'volume_dbl': 100.0}
{'type': 1, 'price': 1.2003, 'volume': 50, 'volume_dbl': 50.0}
{'type': 1, 'price': 1.20028, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20025, 'volume': 36, 'volume_dbl': 36.0}
{'type': 2, 'price': 1.20023, 'volume': 50, 'volume_dbl': 50.0}
{'type': 2, 'price': 1.20022, 'volume': 100, 'volume_dbl': 100.0}
{'type': 2, 'price': 1.20016, 'volume': 250, 'volume_dbl': 250.0}
...

此示例代码首先建立与 MetaTrader 5 终端的连接,然后订阅 EURUSD 交易品种的订单簿更新。接着在一个循环中,每隔 5 秒获取一次订单簿内容并打印,既打印整个订单簿元组,也将每个价格水平以字典形式单独打印。最后取消订阅并关闭与终端的连接。

读取报价数据

Python API提供了三个函数,可用于获取价格(K线)数组,这些函数根据指定请求数据范围的方式不同而有所区别:按K线数量或按时间范围。所有函数都类似于MQL5中不同形式的CopyRates函数。

对于所有这些函数,前两个参数用于指定交易品种名称和时间周期。时间周期在TIMEFRAME枚举中列出,它与MQL5中的ENUM_TIMEFRAMES枚举类似。

请注意:在Python中,此枚举的元素前缀为TIMEFRAME_,而MQL5中类似枚举的元素前缀为PERIOD_

标识符描述
TIMEFRAME_M11分钟
TIMEFRAME_M22分钟
TIMEFRAME_M33分钟
TIMEFRAME_M44分钟
TIMEFRAME_M55分钟
TIMEFRAME_M66分钟
TIMEFRAME_M1010分钟
TIMEFRAME_M1212分钟
TIMEFRAME_M1515分钟
TIMEFRAME_M2020分钟
TIMEFRAME_M3030分钟
TIMEFRAME_H11小时
TIMEFRAME_H22小时
TIMEFRAME_H33小时
TIMEFRAME_H44小时
TIMEFRAME_H66小时
TIMEFRAME_H88小时
TIMEFRAME_H1212小时
TIMEFRAME_D11天
TIMEFRAME_W11周
TIMEFRAME_MN11个月

这三个函数都以带有命名列的NumPy批量数组形式返回K线数据,这些列包括time(时间)、open(开盘价)、high(最高价)、low(最低价)、close(收盘价)、tick_volume(成交笔数)、spread(点差)和real_volume(真实成交量)。numpy.ndarray数组是命名元组的更高效替代方案。要访问列数据,可使用方括号表示法,即array['column']

如果发生错误,函数将返回None

所有函数的参数都是必填且未命名的。

  • numpy.ndarray copy_rates_from(symbol, timeframe, date_from, count)copy_rates_from函数用于请求从指定日期(date_from)开始的count根K线数据。日期可以通过datetime对象设置,也可以设置为自1970年1月1日以来的秒数。

在创建datetime对象时,Python使用本地时区,而MetaTrader 5终端将报价和K线开盘时间存储为协调世界时(UTC,格林威治标准时间,无偏移)。因此,为了正确执行使用时间的函数,必须创建UTC时区的datetime变量。要配置时区,可以使用pytz包。例如(见MQL5/Scripts/MQL5Book/Python/eurusdrates.py):

python
from datetime import datetime
import MetaTrader5 as mt5   
import pytz                    # 导入pytz模块用于处理时区
# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
    print("initialize()失败,错误代码 =", mt5.last_error())
    mt5.shutdown()
    quit()

# 设置时区为UTC
timezone = pytz.timezone("Etc/UTC")

# 创建一个UTC时区的datetime对象,以避免应用本地时区偏移
utc_from = datetime(2022, 1, 10, tzinfo = timezone)

# 从2022年1月10日(UTC时区)开始获取10根EURUSD H1周期的K线
rates = mt5.copy_rates_from("EURUSD", mt5.TIMEFRAME_H1, utc_from, 10)

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

# 显示接收到的数据(元组)中的每个元素
for rate in rates:
    print(rate)

接收数据示例:

(1641567600, 1.12975, 1.13226, 1.12922, 1.13017, 8325, 0, 0)
(1641571200, 1.13017, 1.13343, 1.1299, 1.13302, 7073, 0, 0)
(1641574800, 1.13302, 1.13491, 1.13293, 1.13468, 5920, 0, 0)
(1641578400, 1.13469, 1.13571, 1.13375, 1.13564, 3723, 0, 0)
(1641582000, 1.13564, 1.13582, 1.13494, 1.13564, 1990, 0, 0)
(1641585600, 1.1356, 1.13622, 1.13547, 1.13574, 1269, 0, 0)
(1641589200, 1.13572, 1.13647, 1.13568, 1.13627, 1031, 0, 0)
(1641592800, 1.13627, 1.13639, 1.13573, 1.13613, 982, 0, 0)
(1641596400, 1.1361, 1.13613, 1.1358, 1.1359, 692, 1, 0)
(1641772800, 1.1355, 1.13597, 1.13524, 1.1356, 1795, 10, 0)
  • numpy.ndarray copy_rates_from_pos(symbol, timeframe, start, count)copy_rates_from_pos函数用于请求从指定起始索引start开始的count根K线数据。

MetaTrader 5终端仅在用户图表上可用的历史数据范围内绘制K线。用户可用的K线数量在设置中通过“Max. bars in the window”(窗口中最大K线数)参数进行设置。

以下示例(MQL5/Scripts/MQL5Book/Python/ratescorr.py)展示了基于报价数据绘制几种货币的相关矩阵图。

python
import MetaTrader5 as mt5
import pandas as pd              # 导入pandas模块用于输出数据
import matplotlib.pyplot as plt  # 导入matplotlib模块用于绘图

# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
    print("initialize()失败,错误代码 =", mt5.last_error())
    mt5.shutdown()
    quit()

# 在沙盒中创建用于保存结果图像的路径
image = mt5.terminal_info().data_path + r'\MQL5\Files\MQL5Book\ratescorr'

# 用于计算相关性的交易货币对列表
sym = ['EURUSD','GBPUSD','USDJPY','USDCHF','AUDUSD','USDCAD','NZDUSD','XAUUSD']

# 将K线收盘价复制到DataFrame结构中
d = pd.DataFrame()
for i in sym:        # 每个交易品种的最后1000根M1周期K线
    rates = mt5.copy_rates_from_pos(i, mt5.TIMEFRAME_M1, 0, 1000)
    d[i] = [y['close'] for y in rates]

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

# 计算价格变化百分比
rets = d.pct_change()

# 计算相关性
corr = rets.corr()

# 绘制相关矩阵图
fig = plt.figure(figsize = (5, 5))
fig.add_axes([0.15, 0.1, 0.8, 0.8])
plt.imshow(corr, cmap = 'RdYlGn', interpolation = 'none', aspect = 'equal')
plt.colorbar()
plt.xticks(range(len(corr)), corr.columns, rotation = 'vertical')
plt.yticks(range(len(corr)), corr.columns)
plt.show()
plt.savefig(image)

图像文件ratescorr.png会在当前运行的MetaTrader 5的沙盒中生成。如果你的Python安装中没有包含可选功能“tcl/tk and IDLE”,或者没有安装pip install.tk包,那么使用plt.show()在单独窗口中交互式显示图像的功能可能无法正常工作。

  • numpy.ndarray copy_rates_range(symbol, timeframe, date_from, date_to)copy_rates_range函数用于获取指定日期和时间范围内的K线数据,范围是从date_fromdate_to:这两个值都表示为自1970年开始的秒数,并且采用UTC时区(因为Python使用的datetime是本地时区,所以应该使用pytz模块进行转换)。结果包括开盘时间满足time >= date_fromtime <= date_to的K线。

在以下脚本中,我们将请求特定时间范围内的K线数据。

python
from datetime import datetime
import MetaTrader5 as mt5
import pytz             # 导入pytz模块用于处理时区
import pandas as pd     # 导入pandas模块用于以表格形式显示数据

pd.set_option('display.max_columns', 500) # 设置显示的最大列数
pd.set_option('display.width', 1500)      # 设置显示的最大表格宽度

# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
    print("initialize()失败,错误代码 =", mt5.last_error())
    quit()

# 设置时区为UTC
timezone = pytz.timezone("Etc/UTC")
# 创建UTC时区的datetime对象,以避免应用本地时区偏移
utc_from = datetime(2020, 1, 10, tzinfo=timezone)
utc_to = datetime(2020, 1, 10, minute = 30, tzinfo=timezone)

# 获取2020年1月10日00:00 - 2020年1月10日00:30(UTC时区)期间USDJPY M5周期的K线数据
rates = mt5.copy_rates_range("USDJPY", mt5.TIMEFRAME_M5, utc_from, utc_to)

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

# 根据接收到的数据创建DataFrame
rates_frame = pd.DataFrame(rates)
# 将时间从秒数转换为datetime格式
rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit = 's')

# 输出数据
print(rates_frame)

结果示例:

                 time     open     high      low    close  tick_volume  spread  real_volume 
0 2020-01-10 00:00:00  109.513  109.527  109.505  109.521           43       2            0 
1 2020-01-10 00:05:00  109.521  109.549  109.518  109.543          215       8            0 
2 2020-01-10 00:10:00  109.543  109.543  109.466  109.505           98      10            0 
3 2020-01-10 00:15:00  109.504  109.534  109.502  109.517          155       8            0 
4 2020-01-10 00:20:00  109.517  109.539  109.513  109.527           71       4            0 
5 2020-01-10 00:25:00  109.526  109.537  109.484  109.520          106       9            0 
6 2020-01-10 00:30:00  109.520  109.524  109.508  109.510          205       7            0

读取Tick历史数据

Python API包含两个用于读取真实Tick历史数据的函数:copy_ticks_fromcopy_ticks_rangecopy_ticks_from可指定从特定日期开始的Tick数量来读取数据,copy_ticks_range则用于获取指定时间段内的所有Tick数据。

这两个函数都有四个必需的未命名参数,第一个参数指定交易品种,第二个参数指定请求的Tick数据的起始时间。第三个参数在copy_ticks_from函数中表示所需的Tick数量,在copy_ticks_range函数中表示Tick数据的结束时间。

最后一个参数决定返回的Tick数据类型,它可以包含以下标志之一(COPY_TICKS):

标识符描述
COPY_TICKS_ALL所有Tick数据
COPY_TICKS_INFO包含Bid和/或Ask价格变化的Tick数据
COPY_TICKS_TRADE包含Last价格和/或交易量(Volume)变化的Tick数据

两个函数都会将Tick数据作为numpy.ndarray(来自numpy包)数组返回,数组包含名为timebidasklastflags的列。flags字段的值是TICK_FLAG枚举中的位标志组合,每个位表示Tick属性中相应字段的变化。

标识符变化的Tick属性
TICK_FLAG_BIDBid价格
TICK_FLAG_ASKAsk价格
TICK_FLAG_LASTLast价格
TICK_FLAG_VOLUME交易量
TICK_FLAG_BUY最后买入价格
TICK_FLAG_SELL最后卖出价格
python
numpy.ndarray copy_ticks_from(symbol, date_from, count, flags)

copy_ticks_from函数从指定时间(date_from)开始按给定数量(count)请求Tick数据,它类似于MQL5中的CopyTicks函数。

python
numpy.array copy_ticks_range(symbol, date_from, date_to, flags)

copy_ticks_range函数用于获取指定时间范围内的Tick数据,类似于MQL5中的CopyTicksRange函数。

以下是一个示例代码(MQL5/Scripts/MQL5Book/Python/copyticks.py),它会生成一个带有Tick数据图表的交互式网页(注意:这里使用了plotly包,若要在Python中安装该包,可运行命令pip install plotly):

python
import MetaTrader5 as mt5
import pandas as pd
import pytz
from datetime import datetime
   
# 连接到终端
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error())
   quit()
   
# 设置保存到沙盒的文件名
path = mt5.terminal_info().data_path + r'\MQL5\Files\MQL5Book\copyticks.html'
   
# 从历史中的特定时刻复制1000个EURUSD的Tick数据
utc = pytz.timezone("Etc/UTC") 
rates = mt5.copy_ticks_from("EURUSD", \
datetime(2022, 5, 25, 1, 15, tzinfo = utc), 1000, mt5.COPY_TICKS_ALL)
bid = [x['bid'] for x in rates]
ask = [x['ask'] for x in rates]
time = [x['time'] for x in rates]
time = pd.to_datetime(time, unit = 's')
   
# 断开与终端的连接
mt5.shutdown()
   
# 导入绘图包并在网页上绘制Ask和Bid价格的两条曲线
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
data = [go.Scatter(x = time, y = bid), go.Scatter(x = time, y = ask)]
plot(data, filename = path)

运行上述代码后,会生成一个包含Ask和Bid价格随时间变化曲线的交互式网页。该网页会保存在指定的路径下,你可以在浏览器中打开查看。

计算保证金要求和评估利润

Python开发者可以在脚本中使用order_calc_marginorder_calc_profit函数直接计算拟进行的交易操作所需的保证金以及潜在的利润或亏损。如果函数执行成功,其结果为一个实数;否则,返回None

python
float order_calc_margin(action, symbol, volume, price)

order_calc_margin函数返回完成指定交易操作action所需的保证金金额(以账户货币为单位)。action可以是ENUM_ORDER_TYPE枚举中的两个元素之一:ORDER_TYPE_BUY(买入订单类型)或ORDER_TYPE_SELL(卖出订单类型)。后续参数分别指定金融工具的名称、交易操作的交易量以及开仓价格。

此函数是MQL5中OrderCalcMargin函数的Python版本。

python
float order_calc_profit(action, symbol, volume, price_open, price_close)

order_calc_profit函数返回指定交易类型、交易品种、交易量以及市场进出价格差所对应的利润或亏损金额(以账户货币为单位)。

此函数是MQL5中OrderCalcProfit函数的Python版本。

建议在发送订单之前,检查交易操作所需的保证金以及预期结果。

以下是一个简单的示例代码,展示如何使用这两个函数:

python
import MetaTrader5 as mt5

# 初始化连接到MetaTrader 5终端
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

# 定义交易参数
action = mt5.ORDER_TYPE_BUY
symbol = "EURUSD"
volume = 0.01
price_open = 1.1200
price_close = 1.1210

# 计算保证金
margin = mt5.order_calc_margin(action, symbol, volume, price_open)
if margin is not None:
    print(f"交易所需保证金: {margin} 账户货币")
else:
    print("计算保证金时出错:", mt5.last_error())

# 计算利润
profit = mt5.order_calc_profit(action, symbol, volume, price_open, price_close)
if profit is not None:
    print(f"预期利润: {profit} 账户货币")
else:
    print("计算利润时出错:", mt5.last_error())

# 关闭与MetaTrader 5终端的连接
mt5.shutdown()

在上述示例中,我们首先初始化与MetaTrader 5终端的连接,然后定义了交易的相关参数,包括交易类型、交易品种、交易量、开仓价格和预期平仓价格。接着,我们分别调用order_calc_marginorder_calc_profit函数来计算保证金和预期利润,并对可能出现的错误进行了处理。最后,关闭了与终端的连接。

检查并发送交易订单

如果有需要,你可以直接从Python脚本中进行交易。order_checkorder_send这两个函数可以让你先对交易操作进行预检查,然后再执行该交易操作。

对于这两个函数,唯一的参数都是交易请求结构体TradeRequest(在Python中可以初始化为字典,见示例)。该结构体的字段与MqlTradeRequest完全相同。

1. order_check(request)

python
OrderCheckResult order_check(request)

order_check函数用于检查交易请求字段的正确性以及资金是否足以完成所需的交易操作。

该函数的结果以OrderCheckResult结构体的形式返回。它与MqlTradeCheckResult的结构相同,但额外包含request字段,其中存储了原始请求的副本。

order_check函数是MQL5中OrderCheck函数的等效函数。

示例(MQL5/Scripts/MQL5Book/python/ordercheck.py):

python
import MetaTrader5 as mt5

# 建立与MetaTrader 5终端的连接
...

# 获取账户货币信息
account_currency=mt5.account_info().currency
print("Account currency:", account_currency)

# 获取交易品种的必要属性
symbol = "USDJPY"
symbol_info = mt5.symbol_info(symbol)
if symbol_info is None:
    print(symbol, "not found, can not call order_check()")
    mt5.shutdown()
    quit()

point = mt5.symbol_info(symbol).point
# 如果该交易品种在市场报价窗口中不可用,则添加它
if not symbol_info.visible:
    print(symbol, "is not visible, trying to switch on")
    if not mt5.symbol_select(symbol, True):
        print("symbol_select({}) failed, exit", symbol)
        mt5.shutdown()
        quit()

# 将请求结构体准备为字典形式
request = \
{
    "action": mt5.TRADE_ACTION_DEAL,
    "symbol": symbol,
    "volume": 1.0,
    "type": mt5.ORDER_TYPE_BUY,
    "price": mt5.symbol_info_tick(symbol).ask,
    "sl": mt5.symbol_info_tick(symbol).ask - 100 * point,
    "tp": mt5.symbol_info_tick(symbol).ask + 100 * point,
    "deviation": 10,
    "magic": 234000,
    "comment": "python script",
    "type_time": mt5.ORDER_TIME_GTC,
    "type_filling": mt5.ORDER_FILLING_RETURN,
}

# 运行测试并直接显示结果
result = mt5.order_check(request)
print(result)                       # [?这不在帮助日志中?]

# 将结果转换为字典并逐个输出元素
result_dict = result._asdict()
for field in result_dict.keys():
    print("   {}={}".format(field, result_dict[field]))
    # 如果这是交易请求结构体,则也逐个输出其元素
    if field == "request":
        traderequest_dict = result_dict[field]._asdict()
        for tradereq_filed in traderequest_dict:
            print("       traderequest: {}={}".format(tradereq_filed,
            traderequest_dict[tradereq_filed]))

# 终止与终端的连接
mt5.shutdown()

结果:

Account currency: USD

OrderCheckResult(retcode=0, balance=10000.17, equity=10000.17, profit=0.0, margin=1000.0,...

   retcode=0

   balance=10000.17

   equity=10000.17

   profit=0.0

   margin=1000.0

   margin_free=9000.17

   margin_level=1000.017

   comment=Done

   request=TradeRequest(action=1, magic=234000, order=0, symbol='USDJPY', volume=1.0, price=144.128,...

       traderequest: action=1

       traderequest: magic=234000

       traderequest: order=0

       traderequest: symbol=USDJPY

       traderequest: volume=1.0

       traderequest: price=144.128

       traderequest: stoplimit=0.0

       traderequest: sl=144.028

       traderequest: tp=144.228

       traderequest: deviation=10

       traderequest: type=0

       traderequest: type_filling=2

       traderequest: type_time=0

       traderequest: expiration=0

       traderequest: comment=python script

       traderequest: position=0

       traderequest: position_by=0

2. order_send(request)

python
OrderSendResult order_send(request)

order_send函数将来自终端的交易请求发送到交易服务器,以执行交易操作。

该函数的结果以OrderSendResult结构体的形式返回。它与MqlTradeResult的结构相同,但额外包含request字段,其中存储了原始请求的副本。

该函数是MQL5中OrderSend函数的等效函数。

示例(MQL5/Scripts/MQL5Book/python/ordersend.py):

python
import time 
import MetaTrader5 as mt5 

# 建立与MetaTrader 5终端的连接
...

# 指定交易品种的属性
symbol = "USDJPY"
symbol_info = mt5.symbol_info(symbol)
if symbol_info is None:
    print(symbol, "not found, can not trade")
    mt5.shutdown()
    quit()

# 如果该交易品种在市场报价窗口中不可用,则添加它
if not symbol_info.visible:
    print(symbol, "is not visible, trying to switch on")
    if not mt5.symbol_select(symbol, True):
        print("symbol_select({}) failed, exit", symbol)
        mt5.shutdown()
        quit()

# 准备买入的请求结构体
lot = 0.1
point = mt5.symbol_info(symbol).point
price = mt5.symbol_info_tick(symbol).ask
deviation = 20
request = \
{ 
    "action": mt5.TRADE_ACTION_DEAL, 
    "symbol": symbol, 
    "volume": lot, 
    "type": mt5.ORDER_TYPE_BUY, 
    "price": price, 
    "sl": price - 100 * point, 
    "tp": price + 100 * point, 
    "deviation": deviation, 
    "magic": 234000, 
    "comment": "python script open", 
    "type_time": mt5.ORDER_TIME_GTC, 
    "type_filling": mt5.ORDER_FILLING_RETURN, 
}

# 发送开仓的交易请求
result = mt5.order_send(request)
# 检查执行结果
print("1. order_send(): by {} {} lots at {}".format(symbol, lot, price));
if result.retcode != mt5.TRADE_RETCODE_DONE:
    print("2. order_send failed, retcode={}".format(result.retcode))
    # 将结果请求为字典并逐个显示元素
    result_dict = result._asdict()
    for field in result_dict.keys():
        print("   {}={}".format(field, result_dict[field]))
        # 如果这是交易请求结构体,则也逐个输出其元素
        if field == "request":
            traderequest_dict = result_dict[field]._asdict()
            for tradereq_filed in traderequest_dict: 
                print("       traderequest: {}={}".format(tradereq_filed,
                traderequest_dict[tradereq_filed]))
    print("shutdown() and quit")
    mt5.shutdown()
    quit()

print("2. order_send done, ", result)
print("   opened position with POSITION_TICKET={}".format(result.order))
print("   sleep 2 seconds before closing position #{}".format(result.order))
time.sleep(2)
# 创建平仓请求
position_id = result.order
price = mt5.symbol_info_tick(symbol).bid
request = \
{
    "action": mt5.TRADE_ACTION_DEAL, 
    "symbol": symbol, 
    "volume": lot, 
    "type": mt5.ORDER_TYPE_SELL, 
    "position": position_id, 
    "price": price, 
    "deviation": deviation, 
    "magic": 234000, 
    "comment": "python script close", 
    "type_time": mt5.ORDER_TIME_GTC, 
    "type_filling": mt5.ORDER_FILLING_RETURN, 
} 
# 发送平仓的交易请求
result = mt5.order_send(request)
# 检查执行结果
print("3. close position #{}: sell {} {} lots at {}".format(position_id,
symbol, lot, price));
if result.retcode != mt5.TRADE_RETCODE_DONE:
    print("4. order_send failed, retcode={}".format(result.retcode))
    print("   result", result)
else: 
    print("4. position #{} closed, {}".format(position_id, result))
    # 将结果请求为字典并逐个显示元素
    result_dict = result._asdict()
    for field in result_dict.keys():
        print("   {}={}".format(field, result_dict[field])) 
        # 如果这是交易请求结构体,则也逐个输出其元素
        if field == "request": 
            traderequest_dict = result_dict[field]._asdict()
            for tradereq_filed in traderequest_dict:
                print("       traderequest: {}={}".format(tradereq_filed,
                traderequest_dict[tradereq_filed]))

# 终止与终端的连接
mt5.shutdown()

结果:

1. order_send(): by USDJPY 0.1 lots at 144.132

2. order_send done,  OrderSendResult(retcode=10009, deal=1445796125, order=1468026008, volume=0.1, price=144.132,...

   opened position with POSITION_TICKET=1468026008

   sleep 2 seconds before closing position #1468026008

3. close position #1468026008: sell USDJPY 0.1 lots at 144.124

4. position #1468026008 closed, OrderSendResult(retcode=10009, deal=1445796155, order=1468026041, volume=0.1, price=144.124,...

   retcode=10009

   deal=1445796155

   order=1468026041

   volume=0.1

   price=144.124

   bid=144.124

   ask=144.132

   comment=Request executed

   request_id=2

   retcode_external=0

   request=TradeRequest(action=1, magic=234000, order=0, symbol='USDJPY', volume=0.1, price=144.124, stoplimit=0.0,...

       traderequest: action=1

       traderequest: magic=234000

       traderequest: order=0

       traderequest: symbol=USDJPY

       traderequest: volume=0.1

       traderequest: price=144.124

       traderequest: stoplimit=0.0

       traderequest: sl=0.0

       traderequest: tp=0.0

       traderequest: deviation=20

       traderequest: type=1

       traderequest: type_filling=2

       traderequest: type_time=0

       traderequest: expiration=0

       traderequest: comment=python script close

       traderequest: position=1468026008

       traderequest: position_by=0

获取活跃订单数量及列表

Python API提供了以下用于处理活跃订单的函数。

  • int orders_total()orders_total函数返回活跃订单的数量。该函数类似于MQL5中的OrdersTotal函数。
  • namedtuple[] orders_get()
  • namedtuple[] orders_get(symbol = <"SYMBOL">)
  • namedtuple[] orders_get(group = <"PATTERN">)
  • namedtuple[] orders_get(ticket = <TICKET>)

可以使用orders_get函数获取每个订单的详细信息,该函数有多种选项,能够按交易品种或订单号进行筛选。无论哪种方式,该函数都会返回命名元组TradeOrder的数组(字段名与ENUM_ORDER_PROPERTY_枚举中的名称相对应,但去掉了“ORDER_”前缀并转换为小写)。如果发生错误,结果为None

不带参数的orders_get函数会返回所有交易品种的订单。可选的命名参数symbol可用于指定特定的交易品种名称来筛选订单。可选的命名参数group用于使用通配符“*”(可替代模式中任意数量的任意字符,包括零个字符)和条件逻辑否定字符“!”指定搜索模式。过滤器模板的操作原理在“获取金融工具信息”部分有描述 。如果指定了ticket参数,则会搜索特定的订单。通过一次函数调用,你可以获取所有活跃订单。这类似于MQL5中OrdersTotalOrderSelectOrderGet函数的组合使用。

在接下来的示例(MQL5/Scripts/MQL5Book/Python/ordersget.py)中,我们通过不同方式请求订单信息。

python
import MetaTrader5 as mt5
import pandas as pd
pd.set_option('display.max_columns', 500) # 设置显示的最大列数
pd.set_option('display.width', 1500)      # 设置显示的最大表格宽度

# 建立与MetaTrader 5终端的连接
if not mt5.initialize(): 
    print("initialize()失败,错误代码 =", mt5.last_error())
    quit()

# 显示GBPUSD交易品种上活跃订单的信息
orders = mt5.orders_get(symbol = "GBPUSD")
if orders is None:
    print("GBPUSD上无订单,错误代码={}".format(mt5.last_error()))
else:
    print("GBPUSD上的订单总数:", len(orders))
    # 显示所有活跃订单
    for order in orders:
        print(order)
print()

# 获取名称中包含“*GBP*”的交易品种的订单列表
gbp_orders = mt5.orders_get(group="*GBP*")
if gbp_orders is None: 
    print("没有group=\"*GBP*\"的订单,错误代码={}".format(mt5.last_error()))
else: 
    print("orders_get(group=\"*GBP*\")={}".format(len(gbp_orders)))
    # 使用pandas.DataFrame将订单显示为表格
    df = pd.DataFrame(list(gbp_orders), columns = gbp_orders[0]._asdict().keys())
    df.drop(['time_done', 'time_done_msc', 'position_id', 'position_by_id',
    'reason', 'volume_initial', 'price_stoplimit'], axis = 1, inplace = True)
    df['time_setup'] = pd.to_datetime(df['time_setup'], unit = 's')
    print(df)

# 断开与MetaTrader 5终端的连接
mt5.shutdown()

示例结果如下:

GBPUSD上的订单总数: 2 
TradeOrder(ticket=554733548, time_setup=1585153667, time_setup_msc=1585153667718, time_done=0, time_done_msc=0, time_expiration=0, type=3, type_time=0, ... 
TradeOrder(ticket=554733621, time_setup=1585153671, time_setup_msc=1585153671419, time_done=0, time_done_msc=0, time_expiration=0, type=2, type_time=0, ... 

orders_get(group="*GBP*")=4 
      ticket          time_setup  time_setup_msc  type ... volume_current  price_open   sl   tp  price_current  symbol comment external_id 
0  554733548 2020-03-25 16:27:47   1585153667718     3 ...            0.2     1.25379  0.0  0.0        1.16803  GBPUSD                     
1  554733621 2020-03-25 16:27:51   1585153671419     2 ...            0.2     1.14370  0.0  0.0        1.16815  GBPUSD                     
2  554746664 2020-03-25 16:38:14   1585154294401     3 ...            0.2     0.93851  0.0  0.0        0.92428  EURGBP                     
3  554746710 2020-03-25 16:38:17   1585154297022     2 ...            0.2     0.90527  0.0  0.0        0.92449  EURGBP

获取未平仓头寸的数量和列表

positions_total函数用于返回未平仓头寸的数量。

python
int positions_total()

该函数是MQL5中PositionsTotal函数的对应函数。

若要获取每个头寸的详细信息,可以使用positions_get函数,它有多种调用方式。所有变体都会返回一个具名元组TradePosition的数组,其键对应头寸的属性(请参阅ENUM_POSITION_PROPERTY枚举中的元素,去掉“POSITION_”前缀并转换为小写)。如果出现错误,结果将为None

python
namedtuple[] positions_get()
namedtuple[] positions_get(symbol = <"SYMBOL">)
namedtuple[] positions_get(group = <"PATTERN">)
namedtuple[] positions_get(ticket = <TICKET>)

不带参数调用该函数时,将返回所有未平仓头寸。

带有symbol参数的函数可以选择指定交易品种的头寸。

带有group参数的函数可通过带有通配符*(可替换任意字符)和条件逻辑非!的搜索掩码进行筛选。有关详细信息,请参阅“获取金融工具信息”部分。

带有ticket参数的版本会选择具有特定订单号(POSITION_TICKET属性)的头寸。

positions_get函数可在一次调用中获取所有头寸及其属性,这使其类似于PositionsTotalPositionSelectPositionGet函数的组合。

在脚本MQL5/Scripts/MQL5Book/Python/positionsget.py中,我们请求特定交易品种和搜索掩码下的头寸。

python
import MetaTrader5 as mt5
import pandas as pd
pd.set_option('display.max_columns', 500) # 显示的最大列数
pd.set_option('display.width', 1500)      # 显示的最大表格宽度
   
# 建立与MetaTrader 5终端的连接
if not mt5.initialize():
   print("initialize() failed, error code =", mt5.last_error())
   quit()
   
# 获取USDCHF的未平仓头寸
positions = mt5.positions_get(symbol = "USDCHF")
if positions == None: 
   print("No positions on USDCHF, error code={}".format(mt5.last_error()))
elif len(positions) > 0:
   print("Total positions on USDCHF =", len(positions))
 # 显示所有未平仓头寸
   for position in positions:
      print(position)
   
# 获取名称中包含“*USD*”的交易品种的头寸列表
usd_positions = mt5.positions_get(group = "*USD*") 
if usd_positions == None:
    print("No positions with group=\"*USD*\", error code={}".format(mt5.last_error())) 
elif len(usd_positions) > 0: 
   print("positions_get(group=\"*USD*\") = {}".format(len(usd_positions)))
   # 使用pandas.DataFrame将头寸显示为表格
   df=pd.DataFrame(list(usd_positions), columns = usd_positions[0]._asdict().keys())
   df['time'] = pd.to_datetime(df['time'], unit='s')
   df.drop(['time_update', 'time_msc', 'time_update_msc', 'external_id'],
   axis=1, inplace=True)
   print(df)
   
# 断开与MetaTrader 5终端的连接
mt5.shutdown()

结果可能如下所示:

Total positions on USDCHF = 1

TradePosition(ticket=1468454363, time=1664217233, time_msc=1664217233239, time_update=1664217233,

   time_update_msc=1664217233239, type=1, magic=0, identifier=1468454363, reason=0, volume=0.01, price_open=0.99145,

   sl=0.0, tp=0.0, price_current=0.9853, swap=-0.01, profit=6.24, symbol='USDCHF', comment='', external_id='')

positions_get(group="*USD*") = 2

       ticket                time  type  ...  identifier  volume  price_open  ... _current  swap  profit  symbol comment

0  1468454363 2022-09-26 18:33:53     1  ...  1468454363    0.01     0.99145  ...  0.98530 -0.01    6.24  USDCHF        

1  1468475849 2022-09-26 18:44:00     0  ...  1468475849    0.01     1.06740  ...  1.08113  0.00   13.73  GBPUSD

读取订单和交易记录的历史数据

使用Python脚本处理账户历史记录中的订单和交易也是可行的。为此,有history_orders_totalhistory_orders_gethistory_deals_totalhistory_deals_get这些函数。

int history_orders_total(date_from, date_to)

history_orders_total函数返回指定时间间隔内交易历史记录中的订单数量。每个参数可以通过datetime对象设置,也可以设置为自1970年1月1日以来的秒数。

该函数是HistoryOrdersTotal的一个类似函数。

history_orders_get函数有多个版本,支持通过交易品种名称中的子字符串、订单号或仓位ID来过滤订单。所有版本都返回一个命名元组TradeOrder的数组(字段名称与ENUM_ORDER_PROPERTY枚举中的名称匹配,去掉了“ORDER_”前缀并采用小写形式)。如果没有匹配的订单,数组将为空。如果出现错误,函数将返回None

namedtuple[] history_orders_get(date_from, date_to, group = <"PATTERN">)

namedtuple[] history_orders_get(ticket = <ORDER_TICKET>)

namedtuple[] history_orders_get(position = <POSITION_ID>)

第一个版本在指定的时间范围内选择订单(类似于history_orders_total)。在可选的命名参数group中,你可以指定一个交易品种名称子字符串的搜索模式(可以在其中使用通配符“*”和否定符“!”,请参阅获取金融工具信息的部分)。

第二个版本用于通过订单号搜索特定的订单。

最后一个版本通过仓位ID(ORDER_POSITION_ID属性)选择订单。

任何一个选项都等同于调用几个MQL5函数:HistoryOrdersTotalHistoryOrderSelectHistoryOrderGet函数。

让我们看一个名为historyordersget.py的脚本示例,了解如何根据不同条件获取历史订单的数量和列表。

python
from datetime import datetime 
import MetaTrader5 as mt5 
import pandas as pd 
pd.set_option('display.max_columns', 500) # 显示的列数 
pd.set_option('display.width', 1500)      # 显示的表格最大宽度
...   
# 获取指定时间段内历史记录中的订单数量(总数以及包含*GBP*的订单数量)
from_date = datetime(2022, 9, 1)
to_date = datetime.now()
total = mt5.history_orders_total(from_date, to_date)
history_orders=mt5.history_orders_get(from_date, to_date, group="*GBP*")
# print(history_orders)
if history_orders == None: 
   print("No history orders with group=\"*GBP*\", error code={}".format(mt5.last_error())) 
else :
   print("history_orders_get({}, {}, group=\"*GBP*\")={} of total {}".format(from_date,
   to_date, len(history_orders), total))
   
# 显示仓位ID为0的所有已取消的历史订单
position_id = 0
position_history_orders = mt5.history_orders_get(position = position_id)
if position_history_orders == None:
   print("No orders with position #{}".format(position_id))
   print("error code =", mt5.last_error())
elif len(position_history_orders) > 0:
   print("Total history orders on position #{}: {}".format(position_id,
   len(position_history_orders)))
   # 按原样显示获取到的订单
   for position_order in position_history_orders:
      print(position_order)
   # 使用pandas.DataFrame将这些订单显示为表格
   df = pd.DataFrame(list(position_history_orders),
   columns = position_history_orders[0]._asdict().keys())
   df.drop(['time_expiration', 'type_time','state', 'position_by_id','reason', 'volume_current',
   'price_stoplimit','sl', 'tp', 'time_setup_msc', 'time_done_msc', 'type_filling', 'external_id'],
   axis = 1, inplace = True)
   df['time_setup'] = pd.to_datetime(df['time_setup'], unit='s')
   df['time_done'] = pd.to_datetime(df['time_done'], unit='s')
   print(df)
...

该脚本的运行结果(部分内容缩写显示):

history_orders_get(2022-09-01 00:00:00, 2022-09-26 21:50:04, group="*GBP*")=15 of total 44

   

Total history orders on position #0: 14

TradeOrder(ticket=1437318706, time_setup=1661348065, time_setup_msc=1661348065049, time_done=1661348083,

   time_done_msc=1661348083632, time_expiration=0, type=2, type_time=0, type_filling=2, state=2, magic=0,

   position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=0.99301,

   sl=0.0, tp=0.0, price_current=0.99311, price_stoplimit=0.0, symbol='EURUSD', comment='', external_id='')

TradeOrder(ticket=1437331579, time_setup=1661348545, time_setup_msc=1661348545750, time_done=1661348551,

   time_done_msc=1661348551354, time_expiration=0, type=2, type_time=0, type_filling=2, state=2, magic=0,

   position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=0.99281,

   sl=0.0, tp=0.0, price_current=0.99284, price_stoplimit=0.0, symbol='EURUSD', comment='', external_id='')

TradeOrder(ticket=1437331739, time_setup=1661348553, time_setup_msc=1661348553935, time_done=1661348563,

   time_done_msc=1661348563412, time_expiration=0, type=2, type_time=0, type_filling=2, state=2, magic=0,

   position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=0.99285,

   sl=0.0, tp=0.0, price_current=0.99286, price_stoplimit=0.0, symbol='EURUSD', comment='', external_id='')

...

   

        ticket          time_setup           time_done  type  ... _initial  price_open  price_current  symbol comment

0   1437318706 2022-08-24 13:34:25 2022-08-24 13:34:43     2          0.01     0.99301        0.99311  EURUSD        

1   1437331579 2022-08-24 13:42:25 2022-08-24 13:42:31     2          0.01     0.99281        0.99284  EURUSD        

2   1437331739 2022-08-24 13:42:33 2022-08-24 13:42:43     2          0.01     0.99285        0.99286  EURUSD

...

我们可以看到,在9月份,只有44笔订单,其中15笔订单涉及英镑货币(由于有未平仓头寸,这个数字有些奇怪)。历史记录中有14笔已取消的订单。

int history_deals_total(date_from, date_to)

history_deals_total函数返回指定时间段内历史记录中的交易数量。

该函数是HistoryDealsTotal的一个类似函数。

history_deals_get函数有几种形式,用于选择交易记录,并能够通过订单号或仓位ID进行过滤。该函数的所有形式都返回一个命名元组TradeDeal的数组,其字段反映了ENUM_DEAL_PROPERTY枚举中的属性(字段名称去掉了“DEAL_”前缀并采用小写形式)。如果出现错误,将得到None

namedtuple[] history_deals_get(date_from, date_to, group = <"PATTERN">)

namedtuple[] history_deals_get(ticket = <ORDER_TICKET>)

namedtuple[] history_deals_get(position = <POSITION_ID>)

该函数的第一种形式类似于使用history_orders_get来请求历史订单。

第二种形式允许通过特定订单的订单号(DEAL_ORDER属性)来选择该订单产生的交易。

最后,第三种形式请求那些形成了具有给定ID的仓位的交易(DEAL_POSITION_ID属性)。

该函数允许在一次调用中获取所有交易及其属性,这等同于调用一系列HistoryDealsTotalHistoryDealSelectHistoryDealGet函数。

以下是测试脚本historydealsget.py的主要部分。

python
# 设置时间范围
from_date = datetime(2020, 1, 1)
to_date = datetime.now() 
   
# 获取交易品种名称中既不包含“EUR”也不包含“GBP”的交易记录
deals = mt5.history_deals_get(from_date, to_date, group="*,!*EUR*,!*GBP*") 
if deals == None: 
   print("No deals, error code={}".format(mt5.last_error()))
elif len(deals) > 0: 
   print("history_deals_get(from_date, to_date, group=\"*,!*EUR*,!*GBP*\") =",
   len(deals)) 
   # 按原样显示所有获取到的交易记录
   for deal in deals: 
      print("  ",deal) 
   # 使用pandas.DataFrame将这些交易显示为表格
   df = pd.DataFrame(list(deals), columns = deals[0]._asdict().keys()) 
   df['time'] = pd.to_datetime(df['time'], unit='s')
   df.drop(['time_msc', 'commission', 'fee'], axis = 1, inplace = True)
   print(df)

结果示例:

history_deals_get(from_date, to_date, group="*,!*EUR*,!*GBP*") = 12

   TradeDeal(ticket=1109160642, order=0, time=1632188460, time_msc=1632188460852, type=2, entry=0, magic=0, position_id=0, reason=0, volume=0.0, price=0.0, commission=0.0, swap=0.0, profit=10000.0, fee=0.0, symbol='', comment='', external_id='')

   TradeDeal(ticket=1250629232, order=1268074569, time=1645709385, time_msc=1645709385815, type=0, entry=0, magic=0, position_id=1268074569, reason=0, volume=0.01, price=1970.98, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250639814, order=1268085019, time=1645709950, time_msc=1645709950618, type=1, entry=1, magic=0, position_id=1268074569, reason=0, volume=0.01, price=1970.09, commission=0.0, swap=0.0, profit=-0.89, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250639928, order=1268085129, time=1645709955, time_msc=1645709955502, type=1, entry=0, magic=0, position_id=1268085129, reason=0, volume=0.01, price=1969.98, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250640111, order=1268085315, time=1645709965, time_msc=1645709965148, type=0, entry=1, magic=0, position_id=1268085129, reason=0, volume=0.01, price=1970.17, commission=0.0, swap=0.0, profit=-0.19, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250640309, order=1268085512, time=1645709973, time_msc=1645709973623, type=1, entry=0, magic=0, position_id=1268085512, reason=0, volume=0.1, price=1970.09, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250640400, order=1268085611, time=1645709978, time_msc=1645709978701, type=0, entry=1, magic=0, position_id=1268085512, reason=0, volume=0.1, price=1970.22, commission=0.0, swap=0.0, profit=-1.3, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250640616, order=1268085826, time=1645709988, time_msc=1645709988277, type=1, entry=0, magic=0, position_id=1268085826, reason=0, volume=1.1, price=1969.95, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1250640810, order=1268086019, time=1645709996, time_msc=1645709996990, type=0, entry=1, magic=0, position_id=1268085826, reason=0, volume=1.1, price=1969.88, commission=0.0, swap=0.0, profit=7.7, fee=0.0, symbol='XAUUSD', comment='', external_id='')

   TradeDeal(ticket=1445796125, order=1468026008, time=1664199450, time_msc=1664199450488, type=0, entry=0, magic=234000, position_id=1468026008, reason=3, volume=0.1, price=144.132, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='USDJPY', comment='python script op', external_id='')

   TradeDeal(ticket=1445796155, order=1468026041, time=1664199452, time_msc=1664199452567, type=1, entry=1, magic=234000, position_id=1468026008, reason=3, volume=0.1, price=144.124, commission=0.0, swap=0.0, profit=-0.56, fee=0.0, symbol='USDJPY', comment='python script cl', external_id='')

   TradeDeal(ticket=1446217804, order=1468454363, time=1664217233, time_msc=1664217233239, type=1, entry=0, magic=0, position_id=1468454363, reason=0, volume=0.01, price=0.99145, commission=0.0, swap=0.0, profit=0.0, fee=0.0, symbol='USDCHF', comment='', external_id='')

   

        ticket       order                time t…  e…  … position_id  volume       price    profit  symbol  comment external_id

0   1109160642           0 2021-09-21 01:41:00  2   0              0    0.00     0.00000  10000.00                             

1   1250629232  1268074569 2022-02-24 13:29:45  0   0     1268074569    0.01  1970.98000      0.00  XAUUSD                     

2   1250639814  1268085019 2022-02-24 13:39:10  1   1     1268074569    0.01  1970.09000     -0.89  XAUUSD                     

3   1250639928  1268085129 2022-02-24 13:39:15  1   0     1268085129    0.01  1969.98000      0.00  XAUUSD                     

4   1250640111  1268085315 2022-02-24 13:39:25  0   1     1268085129    0.01  1970.17000     -0.19  XAUUSD                     

5   1250640309  1268085512 2022-02-24 13:39:33  1   0     1268085512    0.10  1970.09000      0.00  XAUUSD                     

6   1250640400  1268085611 2022-02-24 13:39:38  0   1     1268085512    0.10  1970.22000     -1.30  XAUUSD                     

7   1250640616  1268085826 2022-02-24 13:39:48  1   0     1268085826    1.10  1969.95000      0.00  XAUUSD                     

8   1250640810  1268086019 2022-02-24 13:39:56  0   1     1268085826    1.10  1969.88000      7.70  XAUUSD                     

9   1445796125  1468026008 2022-09-26 13:37:30  0   0     1468026008    0.10   144.13200      0.00  USDJPY  python script op   

10  1445796155  1468026041 2022-09-26 13:37:32  1   1     1468026008    0.10   144.12400     -0.56  USDJPY  python script cl   

11  1446217804  1468454363 2022-09-26 18:33:53  1   0     1468454363    0.01     0.99145      0.00  USDCHF