上了三个小象学院的量化交易网课,是时候写点东西了。按照进阶课的内容,先把宋战江老师第一课针对商品期货的海龟交易法写一下,他是在TB上写的,我想将代码改到聚宽上。
先上聚宽搜商品期货数据的信息,看到有个帖子直接给出了海龟交易法,由于第一次用聚宽,先逐字敲一下代码
1 # 克隆自聚宽文章:https://www.joinquant.com/post/9184 2 # 标题:商品期货策略——海龟交易法 3 # 作者:ScintiGimcki 4 5 # 导入函数库 6 import jqdata 7 #import statsmodels.api as sm 8 #from statsmodels.tsa.stattools import adfuller 9 10 ## 初始化函数,设定基准等等 11 def initialize(context): 12 # 设置参数 13 set_params(context) 14 # 设定基准 15 set_benchmark(get_future_code(g.future_index)) 16 # 开启动态复权模式(真实价格) 17 set_option(\'use_real_price\', True) 18 # 过滤掉order系列API产生的比error级别低的log 19 log.set_level(\'order\', \'error\') 20 # 初始化标的 21 g.future = get_future_code(g.future_index) 22 23 24 ### 期货相关设定 ### 25 # 设定账户为金融账户 26 set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type=\'index_futures\')]) 27 # 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23 28 set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type=\'index_futures\') 29 # 设定保证金比例 30 set_option(\'futures_margin_rate\', 0.15) 31 32 # 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入\'IF1512.CCFX\'或\'IH1602.CCFX\'是一样的) 33 # 开盘前运行 34 run_daily( before_market_open, time=\'before_open\', reference_security=get_future_code(g.future_index)) 35 # 开盘时运行 36 run_daily( while_open, time=\'open\', reference_security=get_future_code(g.future_index)) 37 # 收盘后运行 38 run_daily( after_market_close, time=\'after_close\', reference_security=get_future_code(g.future_index)) 39 40 41 def set_params(context): 42 # 设置唐奇安通道时间窗口 43 g.window = 10 44 # 最大unit数目 45 g.limit_unit = 6 46 # 每次交易unit数目 47 g.unit = 0 48 # 加仓次数 49 g.add_time = 0 50 # 持仓状态 51 g.position = 0 52 # 最高价指标,用作移动止损 53 g.price_mark = 0 54 # 最近一次交易的合约 55 g.last_future = None 56 # 上一次交易的价格 57 g.last_price = 0 58 # 合约 59 g.future_index = \'CU\' 60 61 62 63 ## 开盘前运行函数 64 def before_market_open(context): 65 ## 获取要操作的期货(g.为全局变量) 66 # 获取当月期货合约 67 g.future = get_dominant_future(g.future_index) 68 69 70 71 72 73 74 ## 开盘时运行函数 75 def while_open(context): 76 # 如果期货标的改变,重置参数 77 if g.last_future == None: 78 g.last_future = g.future 79 elif g.last_future != g.future: 80 if g.position == -1: 81 order_target(g.last_future,0,side=\'short\') 82 g.position == 0 83 elif g.position == 1: 84 order_target(g.last_future,0,side=\'long\') 85 g.position == 0 86 g.last_future = g.future 87 re_set() 88 log.info("主力合约改变,平仓!") 89 90 # 当月合约 91 future = g.future 92 # 获取当月合约交割日期 93 end_date = get_CCFX_end_date(future) 94 # 当月合约交割日当天不开仓 95 if (context.current_dt.date() == end_date): 96 return 97 price_list = attribute_history(future,g.window+1,\'1d\',[\'close\',\'high\',\'low\']) 98 # 如果没有数据,返回 99 if len(price_list) == 0: 100 return 101 close_price = price_list[\'close\'].iloc[-1] 102 # 计算ATR 103 ATR = get_ATR(price_list,g.window) 104 105 ## 判断加仓或止损 106 # 先判断是否持仓 107 #g.position = get_position(context) 108 if g.position != 0 : 109 signal = get_next_signal(close_price,g.last_price,ATR,g.position) 110 # 判断加仓且持仓没有达到上限 111 if signal == 1 and g.add_time < g.limit_unit: 112 g.unit = get_unit(context.portfolio.total_value,ATR,g.future_index) 113 # 多头加仓 114 if g.position == 1: 115 order(future,g.unit,side=\'long\') 116 log.info( \'多头加仓成功:\',context.current_dt.time(),future,g.unit) 117 g.last_prcie = close_price 118 g.add_time += 1 119 # 空头加仓 120 elif g.position == -1: 121 order(future,g.unit,side=\'short\') 122 log.info( \'空头加仓成功:\',context.current_dt.time(),future,g.unit) 123 g.last_prcie = close_price 124 g.add_time += 1 125 # 判断平仓止损 126 elif signal == -1: 127 # 多头平仓 128 if g.position == 1: 129 order_target(future,0,side=\'long\') 130 g.price_mark = 0 131 g.position = 0 132 log.info( \'多头止损成功:\',context.current_dt.time(),future) 133 log.info(\'----------------------------------------------------------\') 134 # 空头平仓 135 elif g.position == -1: 136 order_target(future,0,side=\'long\') 137 g.price_mark = 0 138 g.position = 0 139 log.info( \'空头止损成功:\',context.current_dt.time(),future) 140 log.info(\'----------------------------------------------------------\') 141 # 重新初始化参数 142 re_set() 143 144 ## 开仓 145 # 得到开仓信号 146 open_signal = check_break(price_list,close_price,g.window) 147 # 多头开仓 148 if open_signal ==1 and g.position !=1: 149 # 检测否需要空头平仓 150 if g.position == -1: 151 order_target(future,0,side=\'short\') 152 if context.portfolio.short_positions[future].total_amount==0: 153 g.price_mark = 0 154 # 重新初始化参数 155 re_set() 156 log.info( \'空头平仓成功:\',context.current_dt.time(),future) 157 log.info(\'----------------------------------------------------------\') 158 # 多头开仓 159 g.unit = get_unit(context.portfolio.total_value,ATR,g.future_index) 160 order(future,g.unit,side=\'long\') 161 if context.portfolio.positions[future].total_amount>0: 162 g.position = 1 163 g.price_mark = context.portfolio.long_positions[future].price 164 log.info( \'多头建仓成功:\',context.current_dt.time(),future,g.unit) 165 log.info(\'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\') 166 g.add_time = 1 167 g.last_prcie = close_price 168 g.last_future= future 169 # 空头开仓 170 elif open_signal == -1 and g.position != -1: 171 # 检测否需要多头平仓 172 if g.position == 1: 173 order_target(future,0,side=\'long\') 174 if context.portfolio.positions[future].total_amount==0: 175 g.price_mark = 0 176 # 重新初始化参数 177 re_set() 178 log.info( \'多头平仓成功:\',context.current_dt.time(),future) 179 log.info(\'----------------------------------------------------------\') 180 # 空头开仓 181 g.unit = get_unit(context.portfolio.total_value,ATR,g.future_index) 182 order(future,g.unit,side=\'short\') 183 if context.portfolio.short_positions[future].total_amount > 0: 184 g.position = -1 185 g.price_mark = context.portfolio.short_positions[future].price 186 log.info( \'空头建仓成功:\',context.current_dt.time(),future,g.unit) 187 log.info(\'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\') 188 g.add_time = 1 189 g.last_prcie = close_price 190 g.last_future= future 191 192 # 判断今日是否出现最高价 193 if g.position != 0: 194 set_price_mark(context,future) 195 # 得到止损信号 196 signal = get_risk_signal(context,future) 197 # 止损平仓 198 if signal: 199 order_target(future, 0, side=\'short\') 200 order_target(future, 0, side=\'long\') 201 if context.portfolio.positions[future].total_amount==0 and context.portfolio.short_positions[future].total_amount==0: 202 log.info("止损平仓!") 203 g.position = 0 204 g.price_mark = 0 205 return 206 207 208 209 ## 收盘后运行函数 210 def after_market_close(context): 211 pass 212 213 214 ########################## 自定义函数 ################################# 215 # 重置参数 216 def re_set(): 217 # 每次交易unit数目 218 g.unit = 0 219 # 加仓次数 220 g.add_time = 0 221 # 持仓状态 222 g.position = 0 223 224 def check_break(price_list,price,T): 225 up = max(price_list[\'high\'].iloc[-T-1:-2]) 226 down = min(price_list[\'low\'].iloc[-T-1:-2]) 227 if price>up: 228 return 1 229 elif price<down: 230 return -1 231 else: 232 return 0 233 234 def get_ATR(price_list,T): 235 TR_list = [max(price_list[\'high\'].iloc[i]-price_list[\'low\'].iloc[i],abs(price_list[\'high\'].iloc[i]-price_list[\'close\'].iloc[i-1]),abs(price_list[\'close\'].iloc[i-1]-price_list[\'low\'].iloc[i])) for i in range(1,T+1)] 236 ATR = np.array(TR_list).mean() 237 return ATR 238 239 def get_next_signal(price,last_price,ATR,position):# 加仓或止损 240 if (price >= last_price + 0.5*ATR and position==1) or (price <= last_price - 0.5*ATR and position==-1): # 多头加仓或空头加仓 241 return 1 242 elif (price <= last_price - 2*ATR and position==1) or (price >= last_price + 2*ATR and position==-1): # 多头止损或空头止损 243 return -1 244 else: 245 return 0 246 247 def get_position(context): # 0为未持仓,1为持多,-1为持空 248 try: 249 tmp = context.portfolio.positions.keys()[0] 250 if not context.portfolio.long_positions[tmp].total_amount and not context.portfolio.short_positions[tmp].total_amount: 251 return 0 252 elif context.portfolio.long_positions[tmp].total_amount: 253 return 1 254 elif context.portfolio.short_positions[tmp].total_amount: 255 return -1 256 else: 257 return 0 258 except: 259 return 0 260 261 def get_unit(cash,ATR,symbol): 262 future_coef_list = {\'A\':10, \'AG\':15, \'AL\':5, \'AU\':1000, 263 \'B\':10, \'BB\':500, \'BU\':10, \'C\':10, 264 \'CF\':5, \'CS\':10, \'CU\':5, \'ER\':10, 265 \'FB\':500, \'FG\':20, \'FU\':50, \'GN\':10, 266 \'HC\':10, \'I\':100, \'IC\':200, \'IF\':300, 267 \'IH\':300, \'J\':100, \'JD\':5, \'JM\':60, 268 \'JR\':20, \'L\':5, \'LR\':10, \'M\':10, 269 \'MA\':10, \'ME\':10, \'NI\':1, \'OI\':10, 270 \'P\':10, \'PB\':5, \'PM\':50, \'PP\':5, 271 \'RB\':10, \'RI\':20, \'RM\':10, \'RO\':10, 272 \'RS\':10, \'RU\':10, \'SF\':5, \'SM\':5, 273 \'SN\':1, \'SR\':10, \'T\':10000, \'TA\':5, 274 \'TC\':100, \'TF\':10000, \'V\':5, \'WH\':20, 275 \'WR\':10, \'WS\':50, \'WT\':10, \'Y\':10, 276 \'ZC\':100, \'ZN\':5} 277 return (cash*0.01/ATR)/future_coef_list[symbol] 278 279 def set_price_mark(context,future): 280 if g.position == -1: 281 g.price_mark = min(context.portfolio.short_positions[future].price,g.price_mark) 282 elif g.position == 1: 283 g.price_mark = max(context.portfolio.long_positions[future].price,g.price_mark) 284 285 def get_risk_signal(context,future): 286 if g.position == -1: 287 if context.portfolio.short_positions[future].price >=1.05*g.price_mark: 288 log.info("空头仓位止损,时间: "+str(context.current_dt.time())) 289 return True 290 else: 291 return False 292 elif g.position == 1: 293 if context.portfolio.long_positions[future].price <= 0.95*g.price_mark: 294 log.info("多头仓位止损,时间: "+str(context.current_dt.time())) 295 return True 296 else: 297 return False 298 299 ########################## 获取期货合约信息,请保留 ################################# 300 # 获取当天时间正在交易的期货主力合约 301 def get_future_code(symbol): 302 future_code_list = {\'A\':\'A9999.XDCE\', \'AG\':\'AG9999.XSGE\', \'AL\':\'AL9999.XSGE\', \'AU\':\'AU9999.XSGE\', 303 \'B\':\'B9999.XDCE\', \'BB\':\'BB9999.XDCE\', \'BU\':\'BU9999.XSGE\', \'C\':\'C9999.XDCE\', 304 \'CF\':\'CF9999.XZCE\', \'CS\':\'CS9999.XDCE\', \'CU\':\'CU9999.XSGE\', \'ER\':\'ER9999.XZCE\', 305 \'FB\':\'FB9999.XDCE\', \'FG\':\'FG9999.XZCE\', \'FU\':\'FU9999.XSGE\', \'GN\':\'GN9999.XZCE\', 306 \'HC\':\'HC9999.XSGE\', \'I\':\'I9999.XDCE\', \'IC\':\'IC9999.CCFX\', \'IF\':\'IF9999.CCFX\', 307 \'IH\':\'IH9999.CCFX\', \'J\':\'J9999.XDCE\', \'JD\':\'JD9999.XDCE\', \'JM\':\'JM9999.XDCE\', 308 \'JR\':\'JR9999.XZCE\', \'L\':\'L9999.XDCE\', \'LR\':\'LR9999.XZCE\', \'M\':\'M9999.XDCE\', 309 \'MA\':\'MA9999.XZCE\', \'ME\':\'ME9999.XZCE\', \'NI\':\'NI9999.XSGE\', \'OI\':\'OI9999.XZCE\', 310 \'P\':\'P9999.XDCE\', \'PB\':\'PB9999.XSGE\', \'PM\':\'PM9999.XZCE\', \'PP\':\'PP9999.XDCE\', 311 \'RB\':\'RB9999.XSGE\', \'RI\':\'RI9999.XZCE\', \'RM\':\'RM9999.XZCE\', \'RO\':\'RO9999.XZCE\', 312 \'RS\':\'RS9999.XZCE\', \'RU\':\'RU9999.XSGE\', \'SF\':\'SF9999.XZCE\', \'SM\':\'SM9999.XZCE\', 313 \'SN\':\'SN9999.XSGE\', \'SR\':\'SR9999.XZCE\', \'T\':\'T9999.CCFX\', \'TA\':\'TA9999.XZCE\', 314 \'TC\':\'TC9999.XZCE\', \'TF\':\'TF9999.CCFX\', \'V\':\'V9999.XDCE\', \'WH\':\'WH9999.XZCE\', 315 \'WR\':\'WR9999.XSGE\', \'WS\':\'WS9999.XZCE\', \'WT\':\'WT9999.XZCE\', \'Y\':\'Y9999.XDCE\', 316 \'ZC\':\'ZC9999.XZCE\', \'ZN\':\'ZN9999.XSGE\'} 317 try: 318 return future_code_list[symbol] 319 except: 320 return \'WARNING: 无此合约\' 321 322 323 # 获取当天时间正在交易的股指期货合约 324 def get_stock_index_futrue_code(context,symbol,month=\'current_month\'): 325 \'\'\' 326 获取当天时间正在交易的股指期货合约。其中: 327 symbol: 328 \'IF\' #沪深300指数期货 329 \'IC\' #中证500股指期货 330 \'IH\' #上证50股指期货 331 month: 332 \'current_month\' #当月 333 \'next_month\' #隔月 334 \'next_quarter\' #下季 335 \'skip_quarter\' #隔季 336 \'\'\' 337 display_name_dict = {\'IC\':\'中证500股指期货\',\'IF\':\'沪深300指数期货\',\'IH\':\'上证50股指期货\'} 338 month_dict = {\'current_month\':0, \'next_month\':1, \'next_quarter\':2, \'skip_quarter\':3} 339 340 display_name = display_name_dict[symbol] 341 n = month_dict[month] 342 dt = context.current_dt.date() 343 a = get_all_securities(types=[\'futures\'], date=dt) 344 try: 345 df = a[(a.display_name == display_name) & (a.start_date <= dt) & (a.end_date >= dt)] 346 return df.index[n] 347 except: 348 return \'WARRING: 无此合约\' 349 350 # 获取当天时间正在交易的国债期货合约 351 def get_treasury_futrue_code(context,symbol,month=\'current\'): 352 \'\'\' 353 获取当天时间正在交易的国债期货合约。其中: 354 symbol: 355 \'T\' #10年期国债期货 356 \'TF\' #5年期国债期货 357 month: 358 \'current\' #最近期 359 \'next\' #次近期 360 \'skip\' #最远期 361 \'\'\' 362 display_name_dict = {\'T\':\'10年期国债期货\',\'TF\':\'5年期国债期货\'} 363 month_dict = {\'current\':0, \'next\':1, \'skip\':2} 364 365 display_name = display_name_dict[symbol] 366 n = month_dict[month] 367 dt = context.current_dt.date() 368 a = get_all_securities(types=[\'futures\'], date=dt) 369 try: 370 df = a[(a.display_name == display_name) & (a.start_date <= dt) & (a.end_date >= dt)] 371 return df.index[n] 372 except: 373 return \'WARRING: 无此合约\' 374 375 # 获取金融期货合约到期日 376 def get_CCFX_end_date(fature_code): 377 # 获取金融期货合约到期日 378 return get_security_info(fature_code).end_date
发现有低级错误,是第189行中空头建仓后,建仓价格记录在last_prcie,而非last_price,导致后续的加仓、止损都会有判断问题
但回测帖子所给的时间段、资金量以及相同的期货品种,发现收益差了很多,原有代码是47%的年化收益,修改后只有18%了
于是觉得代码可能还是哪里有坑,拿不准那个才是正确,于是还是想原来的老路,改宋老师TB的代码,这样可以对照宋老师的回测结果(后面看也是不行,宋博的回测时间是2009年开始的,聚宽只能提供2010年以后的数据)。我将其简化版海龟策略(就是缺了加仓和反向头寸止损的操作)的TB的代码改成可以在聚宽上跑了。
1 # 导入函数库 2 from jqdata import * 3 import pandas as pd 4 import numpy as np 5 6 ## 初始化函数,设定基准等等 7 def initialize(context): 8 set_params(context) 9 # 设定所交易期货指数作为基准 10 set_benchmark(get_future_code(g.future_index)) 11 # 开启动态复权模式(真实价格) 12 set_option(\'use_real_price\', True) 13 # 过滤掉order系列API产生的比error级别低的log 14 # log.set_level(\'order\', \'error\') 15 # 输出内容到日志 log.info() 16 log.info(\'初始函数开始运行且全局只运行一次\') 17 18 ### 期货相关设定 ### 19 # 设定账户为金融账户 20 set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type=\'index_futures\')]) 21 # 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23 22 set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type=\'index_futures\') 23 # 设定保证金比例 24 set_option(\'futures_margin_rate\', 0.15) 25 26 # 设置期货交易的滑点 27 set_slippage(FixedSlippage(0.2)) 28 # 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入\'IF1512.CCFX\'或\'IH1602.CCFX\'是一样的) 29 # 开盘前运行 30 run_daily( before_market_open, time=\'before_open\', reference_security=get_future_code(g.future_index)) 31 # 开盘时运行 32 run_daily( market_open, time=\'open\', reference_security=get_future_code(g.future_index)) 33 # 收盘后运行 34 run_daily( after_market_close, time=\'after_close\', reference_security=get_future_code(g.future_index)) 35 36 37 ## 开盘前运行函数 38 def before_market_open(context): 39 # 输出运行时间 40 log.info(\'函数运行时间(before_market_open):\'+str(context.current_dt.time())) 41 42 # 给微信发送消息(添加模拟交易,并绑定微信生效) 43 #send_message(\'美好的一天~\') 44 45 ## 获取要操作的股票(g.为全局变量) 46 # 获取当月期货合约 47 g.future = get_dominant_future(g.future_index) 48 49 50 ## 开盘时运行函数 51 def market_open(context): 52 calc_window = int(3.45*(g.ATRLength + 1)) 53 price_list = attribute_history(g.future, calc_window + 1, \'1d\', [\'close\',\'high\',\'low\']) 54 55 if len(price_list) == 0: 56 return # 如果没有数据,返回 57 58 AvgTR = XAverage(TrueRange(price_list, calc_window),g.ATRLength) 59 N = AvgTR[-2] 60 TurtleUnits = get_unit(context.portfolio.total_value, N, g.future_index, g.RiskRatio) 61 62 DonchianHi = HighestFC(price_list[\'high\'], g.boLength) 63 DonchianLo = LowestFC(price_list[\'low\'], g.boLength) 64 65 ExitLowestPrice = LowestFC(price_list[\'low\'], g.teLength) 66 ExitHighestPrice = HighestFC(price_list[\'high\'], g.teLength) 67 68 High = price_list[\'high\'].iloc[-1] 69 Low = price_list[\'low\'].iloc[-1] 70 71 # 当不使用过滤条件,或者使用过滤条件并且条件为PreBreakoutFailure为True进行后续操作 72 if g.MarketPosition == 0: 73 log.info(context.current_dt.time(), \' High:\', High, \' DonchianHi:\', DonchianHi, \' Low:\', Low, \' DonchianLo:\', DonchianLo) 74 # 突破开仓 75 if High > DonchianHi and TurtleUnits >= 1: 76 # 开仓价格取突破上轨+一个价位和最高价之间的较小值,这样能更接近真实情况,并能尽量保证成交 77 # myEntryPrice = min(High, DonchianHi)# + MinPoint) 78 # myEntryPrice = Open if myEntryPrice < Open else myEntryPrice # 大跳空的时候用开盘价代替 79 order(g.future, TurtleUnits, side=\'long\') 80 g.MarketPosition = 1 81 82 if Low < DonchianLo and TurtleUnits >= 1: 83 # 开仓价格取突破下轨-一个价位和最低价之间的较大值,这样能更接近真实情况,并能尽量保证成交 84 # myEntryPrice = max(Low,DonchianLo)# - MinPoint) 85 # myEntryPrice = Open if myEntryPrice > Open else myEntryPrice # 大跳空的时候用开盘价代替 86 order(g.future, TurtleUnits, side=\'short\') 87 g.MarketPosition = -1 88 89 if g.MarketPosition == 1: 90 # 有多仓的情况 91 log.info(\'ExitLowestPrice=\', ExitLowestPrice) 92 if Low < ExitLowestPrice: 93 # myExitPrice = max(Low,ExitLowestPrice)# - MinPoint) 94 # myExitPrice = Open if myExitPrice > Open else myExitPrice # 大跳空的时候用开盘价代替 95 order_target(g.future, 0, side=\'long\') # 数量用0的情况下将全部平仓 96 g.MarketPosition = 0 97 elif g.MarketPosition == -1: 98 # 有空仓的情况 99 log.info(\'ExitHighestPrice=\', ExitHighestPrice) 100 if High > ExitHighestPrice: 101 # myExitPrice = Min(High,ExitHighestPrice)# + MinPoint) 102 # myExitPrice = Open if myExitPrice < Open else myExitPrice # 大跳空的时候用开盘价代替 103 order_target(g.future, 0, side=\'short\') # 数量用0的情况下将全部平仓 104 g.MarketPosition = 0 105 106 ## 收盘后运行函数 107 def after_market_close(context): 108 log.info(\'函数运行时间(after_market_close):\', context.current_dt.time()) 109 # 得到当天所有成交记录 110 trades = get_trades() 111 for _trade in trades.values(): 112 log.info(\'成交记录:\'+str(_trade)) 113 log.info(\'一天结束\') 114 log.info(\'##############################################################\') 115 116 117 ########################## 自定义函数 ################################# 118 def set_params(context): 119 g.RiskRatio = 10 # % Risk Per N ( 0 - 100) 120 g.ATRLength = 6 # 平均波动周期 ATR Length 121 g.boLength = 0 # 短周期 BreakOut Length 122 g.teLength = 0 # 离市周期 Trailing Exit Length 123 g.future_index = \'RB\' # 合约 124 g.MarketPosition = 0 125 126 # 求指数平均 127 def XAverage(Price, Length): 128 alpha = 2 / (Length + 1) 129 res = [] 130 for i in range(0, len(Price)): 131 res.append(ema_calc(alpha, Price, i)) 132 return res 133 134 def ema_calc(alpha, price, t): 135 if (t == 0): 136 return price[t] 137 else: 138 return alpha*price[t] + (1-alpha) * ema_calc(alpha, price, t-1) 139 140 def TrueRange(price_list, T): 141 return [max(price_list[\'high\'].iloc[i]-price_list[\'low\'].iloc[i], abs(price_list[\'high\'].iloc[i]-price_list[\'close\'].iloc[i-1]), abs(price_list[\'close\'].iloc[i-1]-price_list[\'low\'].iloc[i])) for i in range(1, T+1)] 142 143 def get_unit(cash, ATR, symbol, RiskRatio): 144 # 各品种期货的交易单位(一手合约包含多少计量单位,如CU:5即铜合约交易单位为5吨/手) 145 future_contract_size = {\'A\':10, \'AG\':15, \'AL\':5, \'AU\':1000, 146 \'B\':10, \'BB\':500, \'BU\':10, \'C\':10, 147 \'CF\':5, \'CS\':10, \'CU\':5, \'ER\':10, 148 \'FB\':500, \'FG\':20, \'FU\':50, \'GN\':10, 149 \'HC\':10, \'I\':100, \'IC\':200, \'IF\':300, 150 \'IH\':300, \'J\':100, \'JD\':5, \'JM\':60, 151 \'JR\':20, \'L\':5, \'LR\':10, \'M\':10, 152 \'MA\':10, \'ME\':10, \'NI\':1, \'OI\':10, 153 \'P\':10, \'PB\':5, \'PM\':50, \'PP\':5, 154 \'RB\':10, \'RI\':20, \'RM\':10, \'RO\':10, 155 \'RS\':10, \'RU\':10, \'SF\':5, \'SM\':5, 156 \'SN\':1, \'SR\':10, \'T\':10000, \'TA\':5, 157 \'TC\':100, \'TF\':10000, \'V\':5, \'WH\':20, 158 \'WR\':10, \'WS\':50, \'WT\':10, \'Y\':10, 159 \'ZC\':100, \'ZN\':5} 160 TurtleUnits = (cash*RiskRatio) / (100.0 * ATR * future_contract_size[symbol]) 161 TurtleUnits = int(TurtleUnits) # 对小数取整 162 print(\'TurtleUnits: \', TurtleUnits) 163 return TurtleUnits 164 165 def HighestFC(Prices, Length): 166 highest = Prices[-2] 167 for i in range(-2, -Length-2): 168 if highest < Prices[i]: 169 highest = Prices 170 return highest 171 172 def LowestFC(Prices, Length): 173 lowest = Prices[-2] 174 for i in range(-2, -Length-2): 175 if lowest > Prices[i]: 176 lowest = Prices 177 return lowest 178 179 ########################## 获取期货合约信息,请保留 ################################# 180 # 获得当天时间正在交易的期货主力合约 181 def get_future_code(symbol): 182 future_code_list = {\'A\':\'A9999.XDCE\', \'AG\':\'AG9999.XSGE\', \'AL\':\'AL9999.XSGE\', \'AU\':\'AU9999.XSGE\', 183 \'B\':\'B9999.XDCE\', \'BB\':\'BB9999.XDCE\', \'BU\':\'BU9999.XSGE\', \'C\':\'C9999.XDCE\', 184 \'CF\':\'CF9999.XZCE\', \'CS\':\'CS9999.XDCE\', \'CU\':\'CU9999.XSGE\', \'ER\':\'ER9999.XZCE\', 185 \'FB\':\'FB9999.XDCE\', \'FG\':\'FG9999.XZCE\', \'FU\':\'FU9999.XSGE\', \'GN\':\'GN9999.XZCE\', 186 \'HC\':\'HC9999.XSGE\', \'I\':\'I9999.XDCE\', \'IC\':\'IC9999.CCFX\', \'IF\':\'IF9999.CCFX\', 187 \'IH\':\'IH9999.CCFX\', \'J\':\'J9999.XDCE\', \'JD\':\'JD9999.XDCE\', \'JM\':\'JM9999.XDCE\', 188 \'JR\':\'JR9999.XZCE\', \'L\':\'L9999.XDCE\', \'LR\':\'LR9999.XZCE\', \'M\':\'M9999.XDCE\', 189 \'MA\':\'MA9999.XZCE\', \'ME\':\'ME9999.XZCE\', \'NI\':\'NI9999.XSGE\', \'OI\':\'OI9999.XZCE\', 190 \'P\':\'P9999.XDCE\', \'PB\':\'PB9999.XSGE\', \'PM\':\'PM9999.XZCE\', \'PP\':\'PP9999.XDCE\', 191 \'RB\':\'RB9999.XSGE\', \'RI\':\'RI9999.XZCE\', \'RM\':\'RM9999.XZCE\', \'RO\':\'RO9999.XZCE\', 192 \'RS\':\'RS9999.XZCE\', \'RU\':\'RU9999.XSGE\', \'SF\':\'SF9999.XZCE\', \'SM\':\'SM9999.XZCE\', 193 \'SN\':\'SN9999.XSGE\', \'SR\':\'SR9999.XZCE\', \'T\':\'T9999.CCFX\', \'TA\':\'TA9999.XZCE\', 194 \'TC\':\'TC9999.XZCE\', \'TF\':\'TF9999.CCFX\', \'V\':\'V9999.XDCE\', \'WH\':\'WH9999.XZCE\', 195 \'WR\':\'WR9999.XSGE\', \'WS\':\'WS9999.XZCE\', \'WT\':\'WT9999.XZCE\', \'Y\':\'Y9999.XDCE\', 196 \'ZC\':\'ZC9999.XZCE\', \'ZN\':\'ZN9999.XSGE\'} 197 try: 198 return future_code_list[symbol] 199 except: 200 return \'WARNING : 无此合约\' 201 202 203 # 获取金融期货合约到期日 204 def get_CCFX_end_date(fature_code): 205 # 获取金融期货合约到期日 206 return get_security_info(fature_code).end_date
在改代码的过程中遇到多个问题,包括:
1. TB提供一个XAverage的函数计算指数平均,也就是EMA,聚宽下需要我自行实现,关于其计算公式,我有疑问,也发到小象去问老师了:
想问问关于ema计算公式的问题,如果我要得到某个股票今天的ema(20)值,是不是要回溯到这个股票上市开始一直算这个ema(20)算到今天,还是说只需要回退到20天前就开始算这个ema值?
举个例子,就是价格序列是[0,1,2,3,4,5,6,7],对应第一天、第二天、...第八天,想问问求ema(3),其alpha是2 / (3+1) = 0.5,那么第六天的ema(3),应该是4.25,还是4.03125,还是说我两个计算方式都不对?
A. 只需往前倒2天:
ema(3)1=3
ema(3)2=0.5*4 + 0.5*3 = 3.5
ema(3)3=0.5*5+0.5*3.5=4.25
还是B. 从价格序列最开始算ema
ema(3)1=0
ema(3)2=0.5*1 + 0.5*0 = 0.5
ema(3)3=0.5*2+0.5*0.5=1.25
ema(3)4=0.5*3 + 0.5*1.25 = 2.125
ema(3)5=0.5*4+0.5*2.125 = 3.0625
ema(3)6=0.5*5+0.5*3.0625=4.03125
主要是我看这里https://www.zybuluo.com/Channelchan/note/1081375中的“2. Pandas递归函数”例子,里面算第10天的ema(6),我按只往前倒5天(第5天,数值为4)算,应该是6.4335948,而非页面上面算的(9*2/7)+(5.669401*5/7)=6.621001,所以比较疑惑。
不知道如果我要算某个时期,例如17年整年,某股票的ema(20),我在计算17年6月1号的ema(20)时应该是,将往前倒20天就用公式开始算,还是由17年1月1日这个我开始算ema的时期代入公式开始求,还是要追溯会股价第一天开始来代入计算公式?
网上搜索还看到,https://zh.wikipedia.org/wiki/%E7%A7%BB%E5%8B%95%E5%B9%B3%E5%9D%87,说ema(N)要往前算到3.45*(N+1),能包含99.9%的加权,那是应该这样么?
而且听老师的课,这个第10天的ema(6)应该是往前倒6天,由第4天(数值为3)那个开始算,那应该是5.56640515。我的理解有错么?
暂时没有回复,我自己的判断是应该往前算3.45*(N+1)即可,代码上也是这样实现的。
2. 现在的回测结果是收益-85%,觉得是代码有问题。碰到一个困难是,宋博的TB代码是算出交易的量和价格,但聚宽的交易接口只提供交易量,不能设置其交易价格,很奇怪。然后我也看到一些log,说一开始就全仓买进,甚至买到资金不够,估计是代码逻辑有问题,或者是TB跟聚宽用的单位不同。总之是这些导致策略收益是亏损。后面要去好好调试,将bug抓出来。