1. # coding=utf-8
      2. from __future__ import print_function, absolute_import, unicode_literals
      3. import numpy as np
      4. from gm.api import *
      5. try:
      6. import statsmodels.tsa.stattools as ts
      7. except:
      8. print('请安装statsmodels库')
      9. '''
      10. 本策略根据EG两步法(1.序列同阶单整2.OLS残差平稳)判断序列具有协整关系之后(若无协整关系则全平仓位不进行操作)
      11. 通过计算两个真实价格序列回归残差的0.9个标准差上下轨,并在价差突破上轨的时候做空价差,价差突破下轨的时候做多价差
      12. 并在回归至标准差水平内的时候平仓
      13. 回测数据为:SHFE.rb1801和SHFE.rb1805的1min数据
      14. 回测时间为:2017-09-25 08:00:00到2017-10-01 15:00:00
      15. '''
      16. # 协整检验的函数
      17. def cointegration_test(series01, series02):
      18. urt_rb1801 = ts.adfuller(np.array(series01), 1)[1]
      19. urt_rb1805 = ts.adfuller(np.array(series01), 1)[1]
      20. # 同时平稳或不平稳则差分再次检验
      21. if (urt_rb1801 > 0.1 and urt_rb1805 > 0.1) or (urt_rb1801 < 0.1 and urt_rb1805 < 0.1):
      22. urt_diff_rb1801 = ts.adfuller(np.diff(np.array(series01)), 1)[1]
      23. urt_diff_rb1805 = ts.adfuller(np.diff(np.array(series01), 1))[1]
      24. # 同时差分平稳进行OLS回归的残差平稳检验
      25. if urt_diff_rb1801 < 0.1 and urt_diff_rb1805 < 0.1:
      26. matrix = np.vstack([series02, np.ones(len(series02))]).T
      27. beta, c = np.linalg.lstsq(matrix, series01)[0]
      28. resid = series01 - beta * series02 - c
      29. if ts.adfuller(np.array(resid), 1)[1] > 0.1:
      30. result = 0.0
      31. else:
      32. result = 1.0
      33. return beta, c, resid, result
      34. else:
      35. result = 0.0
      36. return 0.0, 0.0, 0.0, result
      37. else:
      38. result = 0.0
      39. return 0.0, 0.0, 0.0, result
      40. def init(context):
      41. context.goods = ['SHFE.rb1801', 'SHFE.rb1805']
      42. # 订阅品种
      43. subscribe(symbols=context.goods, frequency='60s', count=801, wait_group=True)
      44. def on_bar(context, bars):
      45. # 获取过去800个60s的收盘价数据
      46. close_01 = context.data(symbol=context.goods[0], frequency='60s', count=801, fields='close')['close'].values
      47. close_02 = context.data(symbol=context.goods[1], frequency='60s', count=801, fields='close')['close'].values
      48. # 展示两个价格序列的协整检验的结果
      49. beta, c, resid, result = cointegration_test(close_01, close_02)
      50. # 如果返回协整检验不通过的结果则全平仓位等待
      51. if not result:
      52. print('协整检验不通过,全平所有仓位')
      53. order_close_all()
      54. return
      55. # 计算残差的标准差上下轨
      56. mean = np.mean(resid)
      57. up = mean + 0.9 * np.std(resid)
      58. down = mean - 0.9 * np.std(resid)
      59. # 计算新残差
      60. resid_new = close_01[-1] - beta * close_02[-1] - c
      61. # 获取rb1801的多空仓位
      62. position_01_long = context.account().position(symbol=context.goods[0], side=PositionSide_Long)
      63. position_01_short = context.account().position(symbol=context.goods[0], side=PositionSide_Short)
      64. if not position_01_long and not position_01_short:
      65. # 上穿上轨时做空新残差
      66. if resid_new > up:
      67. order_target_volume(symbol=context.goods[0], volume=1, order_type=OrderType_Market,
      68. position_side=PositionSide_Short)
      69. print(context.goods[0] + '以市价单开空仓1手')
      70. order_target_volume(symbol=context.goods[1], volume=1, order_type=OrderType_Market,
      71. position_side=PositionSide_Long)
      72. print(context.goods[1] + '以市价单开多仓1手')
      73. # 下穿下轨时做多新残差
      74. if resid_new < down:
      75. order_target_volume(symbol=context.goods[0], volume=1, order_type=OrderType_Market,
      76. position_side=PositionSide_Long)
      77. print(context.goods[0], '以市价单开多仓1手')
      78. order_target_volume(symbol=context.goods[1], volume=1, order_type=OrderType_Market,
      79. position_side=PositionSide_Short)
      80. print(context.goods[1], '以市价单开空仓1手')
      81. # 新残差回归时平仓
      82. elif position_01_short:
      83. if resid_new <= up:
      84. order_close_all()
      85. print('价格回归,平掉所有仓位')
      86. # 突破下轨反向开仓
      87. if resid_new < down:
      88. order_target_volume(symbol=context.goods[0], volume=1, order_type=OrderType_Market,
      89. position_side=PositionSide_Long)
      90. print(context.goods[0], '以市价单开多仓1手')
      91. order_target_volume(symbol=context.goods[1], volume=1, order_type=OrderType_Market,
      92. position_side=PositionSide_Short)
      93. print(context.goods[1], '以市价单开空仓1手')
      94. elif position_01_long:
      95. if resid_new >= down:
      96. order_close_all()
      97. print('价格回归,平所有仓位')
      98. # 突破上轨反向开仓
      99. if resid_new > up:
      100. order_target_volume(symbol=context.goods[0], volume=1, order_type=OrderType_Market,
      101. position_side=PositionSide_Short)
      102. print(context.goods[0], '以市价单开空仓1手')
      103. order_target_volume(symbol=context.goods[1], volume=1, order_type=OrderType_Market,
      104. position_side=PositionSide_Long)
      105. print(context.goods[1], '以市价单开多仓1手')
      106. if __name__ == '__main__':
      107. '''
      108. strategy_id策略ID,由系统生成
      109. filename文件名,请与本文件名保持一致
      110. mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
      111. token绑定计算机的ID,可在系统设置-密钥管理中生成
      112. backtest_start_time回测开始时间
      113. backtest_end_time回测结束时间
      114. backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
      115. backtest_initial_cash回测初始资金
      116. backtest_commission_ratio回测佣金比例
      117. backtest_slippage_ratio回测滑点比例
      118. '''
      119. run(strategy_id='strategy_id',
      120. filename='main.py',
      121. mode=MODE_BACKTEST,
      122. token='token_id',
      123. backtest_start_time='2017-09-25 08:00:00',
      124. backtest_end_time='2017-10-01 16:00:00',
      125. backtest_adjust=ADJUST_PREV,
      126. backtest_initial_cash=500000,
      127. backtest_commission_ratio=0.0001,
      128. backtest_slippage_ratio=0.0001)

    跨期套利(期货)

    原文: https://www.myquant.cn/docs/python_strategyies/107