请选择 进入手机版 | 继续访问电脑版
楼主: 唉人好累66
3827 2

[交易策略] 【干货分享】从零开始学量化:11Arbitrage 套利策略 [推广有奖]

  • 0关注
  • 41粉丝

讲师

55%

还不是VIP/贵宾

-

威望
1
论坛币
1457 个
通用积分
5.0477
学术水平
11 点
热心指数
14 点
信用等级
11 点
经验
3660 点
帖子
214
精华
0
在线时间
119 小时
注册时间
2016-9-24
最后登录
2020-4-8

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
1.策略介绍及逻辑
   策略在过去经验的统计验证基础上,认为两个股票或期货的价格比值符合统计稳定规律,如果价差超出某一阀值后,存在套利机会。本示例中,使用IF1703,IF1704当做标的。a:IF1703,b:IF1704。
代码中用lna - lnb 来表示的价格比。对价格取ln可以降低数据出现异值的可能性,提升数据的可用性。
交易规则:
监测lna - lnb
- 如果大于0:
    - 如果价格比值超出设定阀值,且低于止损阀值,空a多b;
    - 如果超出止损阀值,平空a,平多b;
- 如果小于0:
    - 如果小于负的设定阀值,且高于负的止损阀值,多a空b;
    - 如果小于负的止损阀值,平多a,平空b;
- 在价格比接近于1时,认为回归,平掉套利仓位
- 防止单腿成交:
  在check_positions中,判断如果4个tick数据(每只代码各2个tick更新)后,仍然只有单腿成交,则平掉单腿仓位。
注:只是一个示例套利的程序框架,实际应用中需要按照具体情况修改。
用同类品种跨期的价格差,可以直接用两者之间的价格相减。
2.策略代码
   2.1配置文件【strategy_sa.ini】(提示ini配置文件,需要保存成UTF8格式)
  1. [strategy]
  2. username=
  3. password=
  4. ;回测模式
  5. mode=4
  6. td_addr=localhost:8001
  7. strategy_id=
  8. ;订阅代码注意及时更新
  9. subscribe_symbols=CFFEX.IF1703.tick,CFFEX.IF1704.tick,CFFEX.IF1703.bar.15,CFFEX.IF1704.bar.15

  10. [backtest]
  11. start_time=2017-03-01 09:00:00
  12. end_time=2017-03-08 16:00:00
  13. ;策略初始资金
  14. initial_cash=10000000

  15. ;委托量成交比率,默认=1(每个委托100%成交)
  16. transaction_ratio=1

  17. ;手续费率,默认=0(不计算手续费)
  18. commission_ratio=0

  19. ;滑点比率,默认=0(无滑点)
  20. slippage_ratio=0

  21. [ss]
  22. bar_type=15
  23. window_size=20
  24. trade_exchange_a=CFFEX
  25. trade_secid_a=IF1703
  26. trade_unit_a=1
  27. trade_exchange_b=CFFEX
  28. trade_secid_b=IF1704
  29. trade_unit_b=1
  30. tick_size=0.2

  31. sigma=2.34

  32. ##############################################################
  33. # logger settings
  34. ##############################################################
  35. [loggers]
  36. keys=root

  37. [logger_root]
  38. level=DEBUG
  39. handlers=console,file

  40. [handlers]
  41. keys=console,file

  42. [handler_file]
  43. class=handlers.RotatingFileHandler
  44. args=('strategy_sa.log','a',1000,5)
  45. formatter=simple

  46. [handler_console]
  47. class=StreamHandler
  48. args = (sys.stdout,)
  49. formatter=simple

  50. [formatters]
  51. keys = simple

  52. [formatter_simple]
  53. format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
  54. datefmt=
复制代码
  2.2策略文件【strategy_sa.py】
  1. #!/usr/bin/env python
  2. # encoding: utf-8

  3. import logging
  4. import time
  5. import numpy as np
  6. from collections import deque
  7. from gmsdk import *
  8. from math import log
  9. eps = 1e-6

  10. class StatArb(StrategyBase):
  11.     '''
  12.         statistics arbitrage demo
  13.     '''
  14.     def __init__(self, *args, **kwargs):
  15.         super(StatArb, self).__init__(*args, **kwargs)
  16.         logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s')
  17.         self.tick_size = self.config.getfloat('ss', 'tick_size') or 0.2

  18.         self.threshold = self.config.getfloat('ss', 'sigma') or 2.34
  19.         self.significant_diff = self.threshold * 0.0015   ## 3/4 sigma
  20.         self.stop_lose_threshold = self.threshold * 0.002  ## 2 * sigma

  21.         self.trade_exchange_a = self.config.get('ss', 'trade_exchange_a') or 'CFFEX'
  22.         self.trade_secid_a = self.config.get('ss', 'trade_secid_a')
  23.         self.trade_unit_a = self.config.get('ss', 'trade_unit_a') or 1
  24.         self.last_price_a = 0.0

  25.         self.trade_exchange_b = self.config.get('ss', 'trade_exchange_b') or 'CFFEX'
  26.         self.trade_secid_b = self.config.get('ss', 'trade_secid_b')
  27.         self.trade_unit_b = self.config.get('ss', 'trade_unit_b') or 1
  28.         self.last_price_b = 0.0

  29.         self.pos_side_up = False
  30.         self.pos_side_down = False

  31.         self.window_size = 20

  32.         self.close_buffer_symbol_a = deque(maxlen=self.window_size)
  33.                self.close_buffer_symbol_b = deque(maxlen=self.window_size)
  34.         self.at_risk = 0
  35.         self.bar_type = self.config.get('ss', 'bar_type')

  36.     def on_tick(self, tick):
  37.         if tick.sec_id == self.trade_secid_a:
  38.             self.last_price_a = tick.last_price
  39.         elif tick.sec_id == self.trade_secid_b:
  40.             self.last_price_b = tick.last_price

  41.         self.check_position()

  42.     def on_bar(self, bar):

  43.         if bar.bar_type == 15:
  44.             #print (bar.sec_id == 'IF1704')

  45.             if bar.sec_id == 'IF1703':
  46.                 a = 1
  47.                 #print ('bar')

  48.                 self.close_buffer_symbol_a.append(bar.close)

  49.             elif bar.sec_id == 'IF1704': #self.trade_secid_b:
  50.                 b = 1
  51.                 a = 1
  52.                 #print (bar.close)

  53.                 #print (bar.sec_id == self.trade_secid_a)
  54.                 self.close_buffer_symbol_b.append(bar.close)
  55.                 if a == 1 and b == 1:
  56.                     self.algo_action()
  57.             #print ('action')

  58.     def open_side_up(self):
  59.         self.open_short(self.trade_exchange_a, self.trade_secid_a, self.last_price_a, self.trade_unit_a)
  60.         self.open_long(self.trade_exchange_b, self.trade_secid_b, self.last_price_b, self.trade_unit_b)
  61.         self.pos_side_up = True

  62.     def close_side_up(self):
  63.         self.close_short(self.trade_exchange_a, self.trade_secid_a, self.last_price_a, self.trade_unit_a)
  64.         self.close_long(self.trade_exchange_b, self.trade_secid_b, self.last_price_b, self.trade_unit_b)
  65.         self.pos_side_up = False

  66.     def open_side_down(self):
  67.         self.open_long(self.trade_exchange_a, self.trade_secid_a, self.last_price_a, self.trade_unit_a)
  68.         self.open_short(self.trade_exchange_b, self.trade_secid_b, self.last_price_b, self.trade_unit_b)
  69.         self.pos_side_down = True

  70.     def close_side_down(self):
  71.         self.close_long(self.trade_exchange_a, self.trade_secid_a, self.last_price_a, self.trade_unit_a)
  72.         self.close_short(self.trade_exchange_b, self.trade_secid_b, self.last_price_b, self.trade_unit_b)
  73.         self.pos_side_down = False

  74.     def algo_action(self):
  75.         # type: () -> object

  76.         latest_a = self.close_buffer_symbol_a.pop()
  77.         lna = log(latest_a)

  78.         latest_b = self.close_buffer_symbol_b.pop()
  79.         lnb = log(latest_b)


  80.         diff = lna - lnb
  81.        #print (diff)
  82.         #print(self.stop_lose_threshold)

  83.         if diff > self.stop_lose_threshold:
  84.             self.close_side_up()
  85.             #print ('a')
  86.         elif diff > self.significant_diff and diff < self.stop_lose_threshold:
  87.             self.open_side_up()
  88.             #print ('b')
  89.         elif diff < - self.stop_lose_threshold:
  90.             self.close_side_down()
  91.             #print ('c')
  92.         elif diff < - self.significant_diff and diff > - self.stop_lose_threshold:
  93.             self.open_side_down()
  94.             #print ('d')
  95.         elif abs(diff) < self.threshold:
  96.             if self.pos_side_up:
  97.                 self.close_side_up()
  98.             if self.pos_side_down:
  99.                 self.close_side_down()


  100.     def check_position(self):
  101.         """  TODO: check if one leg position and close it  """
  102.         ps = self.get_positions()
  103.         count = len(ps)
  104.         if count % 2 != 0:
  105.             self.at_risk += 1
  106.             ## if more than 4 tick data passed, need to force quit
  107.             if self.at_risk < 4:
  108.                 return

  109.             for p in ps:
  110.                 if self.pos_side_up:
  111.                     if p.side == OrderSide_Ask:
  112.                         self.close_short(p.exchange, p.sec_id, self.last_price_a, p.volume)
  113.                     elif p.side == OrderSide_Bid:
  114.                         self.close_long(p.exchange, p.sec_id, self.last_price_b, p.volume)
  115.                 if self.pos_side_down:
  116.                     if p.side == OrderSide_Ask:
  117.                         self.close_short(p.exchange, p.sec_id, self.last_price_b, p.volume)
  118.                     elif p.side == OrderSide_Bid:
  119.                         self.close_long(p.exchange, p.sec_id, self.last_price_a, p.volume)
  120.         else:
  121.             self.at_risk = 0


  122. if __name__ == '__main__':
  123.     #import pdb; pdb.set_trace()
  124.     dm = StatArb(config_file='strategy_sa.ini')
  125.     ret = dm.run()
  126.     print("Statistics Arbitrage: ", dm.get_strerror(ret))
复制代码


3.代码涉及的函数代码
    3.1 python函数及package

功能函数原型参数返回值
参数名含义
sys提供了一系列有关Python运行环境的变量和函数。



sys.argv[0]当前程序名
sys.argv获取当前正在执行的命令行参数的参数列表(list)。sys.argvsys.argv[1]第一个参数
sys.argv[2]第二个参数
arrow标准的时间日期库。
ta-lib被广泛应用的金融市场数据分析的库
pandasPython Data Analysis Library 或 pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的
numpy一套用于支持科学计算的python第三方库
time返回当前时间的时间戳time.time()

返回当前时间的时间戳
len返回对象(字符、列表、元组等)长度或项目个数。len(s)s对象返回对象长度。
append用于在列表末尾添加新的对象。list.append(obj)obj添加到列表末尾的对象。该方法无返回值,但是会修改原来的列表。

3.2掘金接口函数

功能函数原型参数返回值
参数名类型说明
on_bar响应Bar事件,收到Bar数据后本函数被调用。on_bar(bar)barbarbar数据
open_long异步开多仓,以参数指定的symbol、价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口open_long(exchange, sec_id, price, volume)exchangestring交易所代码, 如上交所SHSE委托下单生成的Order对象
sec_idstring证券代码,如浦发银行600000
pricefloat委托价,如果price=0,为市价单,否则为限价单
volumefloat委托量
close_long异步平多仓接口,以参数指定的exchange, 证券代码sec_id, 价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。close_long(exchange, sec_id, price, volume)exchangestring交易所代码, 如上交所SHSE委托下单生成的Order对象
sec_idstring证券代码,如浦发银行600000
pricefloat委托价,如果price=0,为市价单,否则为限价单
volumefloat平仓量
open_short异步开空仓,以参数指定的symbol、价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口open_short(exchange, sec_id, price, volume)exchangestring交易所代码, 如上交所SHSE委托下单生成的Order对象
sec_idstring证券代码,如浦发银行600000
pricefloat委托价,如果price=0,为市价单,否则为限价单
volumefloat委托量
close_short异步平空仓接口,以参数指定的exchange, 证券代码sec_id, 价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。close_long(exchange, sec_id, price, volume)exchangestring交易所代码, 如上交所SHSE委托下单生成的Order对象
sec_idstring证券代码,如浦发银行600000
pricefloat委托价,如果price=0,为市价单,否则为限价单
volumefloat平仓量
get_last_n_dailybars提取单个代码的最新n条DailyBar数据, 策略类和行情服务类都提供该接口。get_last_n_dailybars(symbol, n, end_time='')symbolstring证券代码, 带交易所代码以确保唯一,如SHSE.600000Bar列表
nint提取的数据条数
end_timestring指定截止时间, 如2015-10-30 15:00:00
get_dailybars提取指定时间段的历史Bar数据,支持单个代码提取或多个代码组合提取。策略类和行情服务类都提供该接口。get_dailybars(symbol_list, begin_time, end_time)symbol_liststring证券代码, 带交易所代码以确保唯一,如SHSE.600000,同时支持多只代码DailyBar列表
begin_timestring开始日期, 如2015-10-19
end_timestring结束日期, 如2015-10-30
get_position查询当前策略指定symbol(由交易所代码和证券ID组成)和买卖方向的持仓信息。策略类和交易服务类都提供该接口。get_position(exchange, sec_id, side);exchangestring交易所代码Position对象,持仓信息
sec_idstring证券代码
sideint买卖方向

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Arbitrage 从零开始 Age bit significant

军旗飞扬 发表于 2017-7-29 22:19:58 |显示全部楼层 |坛友微信交流群
谢谢楼主分享!

使用道具

军旗飞扬 发表于 2017-7-29 22:19
谢谢楼主分享!
不客气~一起进步!以后还会有这样的策略分享希望继续关注呀

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注jr
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-3-29 02:01