Python 精简多品种 MACD 趋势策略框架(注释版)

Author: 小小梦, Created: 2017-06-23 19:14:58, Updated: 2017-10-11 10:39:27

Python 精简多品种 MACD 趋势策略框架(注释版)

Python超级精简的多品种MACD趋势策略框架, 代码超级精简, 注释超级详细啰嗦。 >_<! 需要引用 python版CTP商品期货交易类库(支持2/3 测试版) 模板, 模板的代码有JS语言版本的注释(逻辑一致)。

  • 代码附上:

'''
/*backtest
start: 2016-01-30        
end: 2016-12-30           
period: 1440
periodBase: 60
mode: 0                 
*/
'''
# 以上 为设置的回测默认参数
# ------------ 
# 作者: Zero
# ------------

class Trader:                                   # 声明一个 python 类 
    def __init__(self, q, symbol):              # Trader 类的构造函数, 参数 self(代表类实例化以后的对象) , q(引用 商品期货交易类库 模板 构造的 交易处理对象),   symbol (商品期货合约代码)
        self.q = q                              # 给 构造函数 构造的对象添加属性q  ,并用 参数 q 赋值。 
        self.symbol = symbol                    # 同上, 给构造的对象添加symbol 属性,并用 参数 symbol 赋值。
        self.position = 0                       # 添加 属性 position 赋值 0    ,   该属性是用于 记录仓位数量。
        self.isPending = False                  # 添加 属性 isPending 赋值 False     ,   该属性用于标记 对象状态,是否是挂起状态。

    def onOpen(self, task, ret):                # 类成员函数 , 执行开仓完成后的 回调函数(即 在模拟多线程处理交易的对象q 完成当前任务后 回调该 onOpen 函数处理一些开仓后的工作。)
        if ret:                                 # 交易处理对象 q ,会在处理交易任务完成后 回调onOpen ,传入2个参数 ,第一个 就是由形参task 接收,具体数据为执行的交易任务数据, 第二个参数就是 交易完成的情况
                                                # 如果ret 为有效数据(交易未成功 ret 为 None),则处理  if 块内代码
            self.position = ret['position']['Amount'] * (1 if (ret['position']['Type'] == PD_LONG or ret['position']['Type'] == PD_LONG_YD) else -1)
                                                # 对调用该函数的对象的属性position 赋值, ret['position']['Amount'] 为交易后的持仓数量,根据 ret['position']['Type'] 持仓类型 等于 PD_LONG (持多仓)
                                                # 还是 PD_LONG_YD(持空仓)去选择 ret['position']['Amount'] 乘 1 还是 -1 ,最后把持仓数量赋值 给 position ,(作用是通过 position 区分持多仓还是持空仓)
        Log(task["desc"], "Position:", self.position, ret)  # 打印多个项: q 执行的任务的数据 task 字典的 描述内容,赋值过的position , q 对象处理的交易的完成情况(当前持仓信息)
        self.isPending = False                  # 给 isPending 赋值 False 即代表 当前的 品种 交易逻辑 处于 非挂起状态,可以接受任务。

    def onCover(self, task, ret):               # 平仓任务完成后要执行的回调函数, 参数同  onOpen 一致
        self.isPending = False                  # 设置 isPending 为False 当前 品种的交易逻辑为 非挂起状态,可以接受任务。
        self.position = 0                       # 给记录持仓的变量  position 赋值 为0, 即没有持仓。
        Log(task["desc"], ret)                  # 打印 交易处理对象 q 本次处理的任务 描述(desc),  完成处理的结果(ret)

    def onTick(self):                           # 主要交易逻辑 , MACD 策略核心。
        if self.isPending:                      # 如果 Trader类构造的当前逻辑对象的 isPending 属性 为True 则代表 目前有 交易任务 正在 交易处理对象q 队列中执行。
            return                              # 交易逻辑处于挂起状态,不做任何处理。
        ct = exchange.SetContractType(self.symbol)   # 根据构造函数 传入的 symbol 赋值给 对象成员属性symbol 传入 API 函数 即:交易平台对象exchange 的 SetContractType 函数, 用来设置操作的合约类型
        if not ct:                              # SetContractType 函数切换 交易合约代码(symbol) 成功后会返回 该合约的详细信息, 如果返回 None , 即 not ct 为真, 则立即返回,等待下一轮。
            return

        r = exchange.GetRecords()               # 声明 变量 r (用来储存K线数据) , 调用 API 函数 GetRecords 获取 设置后的该合约的 K线数据,赋值给 r 。
        if not r or len(r) < 35:                # 如果 r 为 None 或者 r的长度小于35 (因为要计算 MACD 指标,必须有足够数量的K线bar,小于35 无法计算)
            return                              # 立即返回 ,下一轮处理。
        macd = TA.MACD(r)                       # 调用 API  指标函数  TA.MACD  ,传入实参 r ,计算MACD 指标数据 ,并赋值给 变量 macd 。(TA.MACD 成功返回的数据是一个 二维数组 [dif, dea, 量柱]) 
                                                # 不明白 dif 、dea 的可以百度  MACD 指标
        diff = macd[0][-2] - macd[1][-2]        # 计算dif和dea的差值 (注意,此处计算使用的是计算出的倒数第二bar的dif、dea,因为倒数第一bar 的K线是一直变动的,macd指标也是一直在变动,准确不变的是倒数第二bar)
        if abs(diff) > 0 and self.position == 0:     # 开仓,如果此刻 指标 (dif - dea)的绝对值 大于0 并且 没有持仓(即: position 等于 0)
            self.isPending = True                    # 改变状态 设置为 挂起状态,  isPending = True 
            self.q.pushTask(exchange, self.symbol, ("buy" if diff > 0 else "sell"), 1, self.onOpen)      # 调用交易处理对象q的成员函数pushTask发出开仓交易任务,参数:exchange交易平台对象(传入即可)
                                                     # self.symbol 合约代码(构造时传入), 根据diff 大于0 还是小于0 去设置 "buy" 或者 "sell", 1 这个参数指的是下单量1手, self.onOpen 传入回调函数的引用 
        if abs(diff) > 0 and ((diff > 0 and self.position < 0) or (diff < 0 and self.position > 0)):     # 平仓,如果此刻指标(dif - dea)的绝对值 大于0 并且 and后的 条件任意一个成立执行if 块内代码,
                              # diff 大于0 并且 持空仓          或者     diff小于0 并且 持多仓 ,均为 平仓条件。
            self.isPending = True               # 设置 为挂起状态。
            self.q.pushTask(exchange, self.symbol, ("closebuy" if self.position > 0 else "closesell"), 1, self.onCover)    # 发送平仓交易任务, 参数 同上发送开仓任务。

def main():                                     # 入口函数
    q = ext.NewTaskQueue()                      # 调用 python版商品期货交易类库 模板 的导出函数(即接口),  ext.NewTaskQueue  返回一个 构造的 交易处理对象。引用 给 变量 q 
    Log(_C(exchange.GetAccount))                # 启动 调用 _C 容错函数 ,传入要容错处理的  API : GetAccount 函数,  返回账户信息 由 Log 函数 输出到日志 , 显示。
    tasks = []                                  # 声明一个 空数组  tasks  。
    for symbol in ["MA701", "rb1701"]:          # 遍历 数组 ["MA701", "rb1701"] 中的元素 , 每次循环 把其中的元素symbol 和 交易处理对象q 作为 参数 传递给 Trader 类的构造函数 去构造交易逻辑对象。
        tasks.append(Trader(q, symbol))         # 构造好的  交易逻辑对象 压入 tasks 数组。用以 循环遍历执行处理。
    while True:                                 # 设置一个 while 死循环
        if exchange.IO("status"):               # 每次循环 调用  API 函数 IO ,传入参数  "status" 去检测 与 期货公司 前置服务器 的连接状态(CTP协议), 返回 True 即连接 交易服务器 和 行情服务器
            for t in tasks:                     # 遍历 tasks 数组, 调用 构造 的 Trader 类的 对象 t的成员函数 onTick ,不断检测行情, 择时开仓 、平仓。
                t.onTick()                      # 见 Trader 类 中的 onTick 函数
            q.poll()                            # 调用 交易处理对象 q 的成员函数 poll  去处理 q 对象内的队列中的 交易任务。
        Sleep(1000)                         # 程序每次 while 循环 暂停一段时间 Sleep(1000) 即: 暂停1秒 (1000毫秒),以免访问 API 过于 频繁。

欢迎提出BUG ,建议。


Related

More