Python 算法交易实验68 回测对象重构

2024-03-16 11:28

本文主要是介绍Python 算法交易实验68 回测对象重构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说明

从有这个想法,到勉强完工,整个过程还是持续了很长时间。主要原因有:

  • 1 去年12月没有遵守【原则】,手工乱下了一堆单子,然后满仓了。等这些单子“解冻”估计还要一阵子,所以也没有很急。
  • 2 在做的过程中,想做一些工具层面的升级,所以会花时间做一些依赖服务。

最后觉得还是要尽快完成一版,所以才想写本篇文章。
在这个版本中,不去考虑回撤、或者平均模型分的问题。

内容

1 样例数据

假设是分钟数据,但我想甚至是按天的数据可能也行。嗯,后续可以试一下天级别数据建模。有时候我也在想,是不是我一开始把问题搞的过于复杂了,分钟级别的判断是否需要?

言归正传,还是回到分钟级数据。未来,等到国内允许(非常简单的,普遍的那种)用接口进行交易,那么其实还是要用分钟级数据的。秒级的倒真没必要,又不搞高频。

# 排序好的
rec_data_list = [{'data_dt': '2013-03-15 09:31:00',
'open': 3.03,
'close': 2.9,
'high': 3.04,
'low': 2.8,
'decision_score':111
},
{'data_dt': '2013-03-15 09:32:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':610}
,
{'data_dt': '2013-03-15 09:33:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':300
},
{'data_dt': '2013-03-16 09:33:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':300
},
{'data_dt': '2013-03-16 13:33:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':601
},
{'data_dt': '2013-03-20 13:33:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':601
},
{'data_dt': '2013-03-20 14:33:00',
'open': 3.13,
'close': 3.0,
'high': 3.14,
'low': 2.9,
'decision_score':601
},
{'data_dt': '2013-03-21 14:33:00',
'open': 3.13,
'close': 13.0,
'high': 3.14,
'low': 2.9,
'decision_score':601
},
{'data_dt': '2013-03-21 14:59:00',
'open': 3.13,
'close': 13.1,
'high': 3.14,
'low': 2.9,
'decision_score':610
},
{'data_dt': '2013-03-22 14:00:00',
'open': 3.13,
'close': 0.1,
'high': 3.14,
'low': 2.9,
'decision_score':610
}]rec_dt_list = ['2013-03-15 09:31:00','2013-03-15 09:32:00','2013-03-15 09:33:00',
'2013-03-16 09:33:00','2013-03-16 13:33:00','2013-03-20 13:33:00',
'2013-03-20 14:33:00','2013-03-21 14:33:00','2013-03-21 14:59:00',
'2013-03-22 14:00:00']

默认的策略参数,初始资本6000,每单金额5000,最大容许资产损失10%,最大持有天数为3。
买入的分值下限是600,卖出的分值上限是500。
费率设置为千5,订单的盈利终止是1个点,损失终止是2个点。

strategy_para = {
'init_cap': 6000,
'per_order_amt':5000,
'max_cap_loss_rate': -0.1,
'max_hold_order_num':1,
'max_order_hold_days':3,
'model_singal_score_buy':600,
'model_singal_score_sell':500,
'fee_rate': 0.005,
'etf_code': '510300',
'order_win_stop_rate':0.01,
'order_loss_stop_rate': -0.02
}

2 对象

对象依赖于两个服务:

  • 1 global_buffer: 这个服务提供了一个简单的访问redis变量的方法,提供set和get两种方法。一个核心的应用是访问/维护一个全局变量,限制在一段时间内可用的资金总量。这个服务未来可被更多的程序使用,提供全局缓存(元数据)。
  • 2 gfgo_lite: 这个服务提供了参数化的函数处理服务。在这里,用于计算时间间隔,例如订单的最长持久时间和最短禁止售卖时间。gfgo_lite的时间处理方式不同于time或者datetime这样的包,而是采用字典查询+偏移推算的方法,速度要快的多。而这个服务更大的作用在于确保global_func这个庞大而理想化的项目可以持续下去,最终实现完全的参数化调用函数以及弹性拓展算力的目的。再往后则是Agent相关的内容。

对象使用了transitions包的有限状态机模块,从而使各状态的变化更加清晰。虽然最后并不是理想的结果,但是也似乎可用。

另外本想在规则这块想写的高级点的,但是,就这样吧…

import copy
import requests as req 
import numpy as np 
import pandas as pd 
wan_ip ='****'class BackTest2:def __init__(self,name = None, global_buffer_ip = None, space_name = None):# 元数据 flip步需要基于数据,更新元数据  ~ update meta | dataself.meta = {}# 数据 flop步需要基于元数据,作出action ~ update data | metaself.data = {}# 行动,也就是变化self.action = None# 全局缓存ipself.global_buffer_ip = global_buffer_ipself.space_name  = space_nameself.close_orders = []# 接受策略参数,并进行初始化def _init_para(self, para_dict = None):'''初始资本 init_cap每笔交易金额(初始资本 > 每笔交易金额 * (1+资本回撤率)) per_order_amt最大资本亏损率 max_cap_loss_rate最大持有订单 max_hold_order_num最大订单持有(交易)时隙 max_order_hold_trade_slots订单买入模型分下限 model_singal_score_buy订单卖出模型分上限 model_singal_score_sell'''for x in ['init_cap', 'per_order_amt','max_cap_loss_rate','max_hold_order_num','max_order_hold_days','model_singal_score_buy','model_singal_score_sell','etf_code','order_win_stop_rate','order_loss_stop_rate' ] :self.meta[x] = para_dict[x]self.meta['cash'] = para_dict['init_cap']self.meta['hold'] = 0# 获取全局数据# BT2同时还需要收到全局参数的控制;这意味着若干BT2可以同时进行测试def _get_global_control_meta(self,global_buffer_ip = None, space_name = None,varname = None):# flipglobal_buffer_ip = global_buffer_ip or self.global_buffer_ipspace_name = space_name or self.space_namepara_dict  ={'space':space_name,'varname':varname,'ttl': 86400 # 可以不写,默认86400}return req.post('http://%s:24088/getx/' % global_buffer_ip,json = para_dict).json()def _set_global_control_meta(self,global_buffer_ip = None, space_name = None,varname = None, value = None,persist='yes'):# flipglobal_buffer_ip = global_buffer_ip or self.global_buffer_ipspace_name = space_name or self.space_namepara_dict  ={'space':space_name,'varname':varname,'value':value,'ttl': 86400, # 'persist':persist}return req.post('http://%s:24088/setx/' % global_buffer_ip,json = para_dict).json()def _get_time_gap(self,global_buffer_ip = None,start_dt = None, end_dt =None, time_unit = None,bias_hours = -8):global_buffer_ip = global_buffer_ip or self.global_buffer_ipsome_dict = {}some_dict['start_dt'] = start_dtsome_dict['end_dt'] = end_dtsome_dict['time_unit'] = time_unitsome_dict['bias_hours'] = bias_hoursres = req.post('http://%s:24090/time_gap/' % global_buffer_ip, json = some_dict).json()return res # 从外部获取数据def get_data(self,dt_list = None, rec_list = None):last_dt = self.meta.get('last_dt') or ''# 找到第一个大于 last_dt 的日期的索引pos = np.argwhere(np.array(dt_list) > last_dt)# 如果找到了满足条件的日期if len(pos) > 0:# 获取索引的第一个元素pos = pos[0][0]# 使用索引获取相应的日期和记录dt = dt_list[pos]rec = rec_list[pos]print("Date:", dt)print("Record:", rec)return  dt, recelse:print("No date found after", last_dt)return None # 买def _buy(self, price = None, dt = None):per_order_amt = self.meta['per_order_amt']stocks = int(per_order_amt /(100 * price)) * 100self.data['code'] = self.meta['etf_code']self.data['buy_price'] = price self.data['buy_dt'] = dt self.data['stocks'] = stocksself.data['buy_amt'] = price * stocksself.meta['cash'] = self.meta['cash'] - self.data['buy_amt'] self.meta['hold'] = self.data['buy_amt']# 改动全局总量monthly_quota = self._get_global_control_meta(varname='monthly_quota')monthly_quota -= self.data['buy_amt'] self._set_global_control_meta(varname='monthly_quota', value=monthly_quota)return True# 卖def _sell(self, price = None, dt = None, fee=0.005):self.data['sell_price'] = price self.data['sell_dt'] = dt self.data['sell_amt'] = price * self.data['stocks']self.data['gp'] = self.data['sell_amt'] - self.data['buy_amt']self.data['np'] = self.data['sell_amt'] * (1-fee) - self.data['buy_amt']self.data['npr'] = round(self.data['np']/self.data['buy_amt'],4)self.meta['cash'] = self.meta['cash'] + self.data['sell_amt'] * (1-fee)self.meta['hold'] = 0monthly_quota = self._get_global_control_meta(varname='monthly_quota')monthly_quota += self.data['buy_amt'] self._set_global_control_meta(varname='monthly_quota', value=monthly_quota)self.close_orders.append(copy.deepcopy(self.data))self.data = {}return True# 规则集将会直接更改元数据def ruleset(self, data = None):# input set : 收盘价,模型分,时间 |  init_cap , per_order_amt ,max_cap_loss_rate, max_hold_order_num ,max_order_hold_trade_slots , model_singal_score_buy , model_singal_score_sell# -9, close = data['close']dt = data['data_dt']decision_score = data['decision_score']cash = self.meta['cash']init_cap = self.meta['init_cap']model_singal_score_buy = self.meta['model_singal_score_buy']max_order_hold_days = self.meta['max_order_hold_days']model_singal_score_sell = self.meta['model_singal_score_sell']order_win_stop_rate = self.meta['order_win_stop_rate']order_loss_stop_rate = self.meta['order_loss_stop_rate']max_cap_loss_rate = self.meta['max_cap_loss_rate']per_order_amt = self.meta['per_order_amt']# 空仓if self.state == 'Init':the_event = 'unchange'self.trigger(the_event)return Trueif self.state.startswith('E'):# 如果收到全局控制,就不能再买入monthly_quota = self._get_global_control_meta(varname='monthly_quota')if monthly_quota < per_order_amt:self.trigger('unchange')return True# 判断买卖if decision_score >= model_singal_score_buy:the_event = 'buy'self._buy(price =close, dt = dt )else:the_event = 'unchange'self.trigger(the_event)return Trueif self.state.startswith('H'):hold_value = close * self.data['stocks']self.meta['hold'] = hold_valuecur_cap = cash + hold_valuerate = (cur_cap - init_cap)/init_capprint('rate: ',rate)# 判断涨跌if rate >=-0.03 and rate <0.03:if self.state in ['HL1','HL2']:the_event ='up'elif self.state in ['HW1','HW2']:the_event = 'down'else:the_event = 'unchange' elif rate >=-0.09 and rate <-0.03:if self.state in ['HB','HW1','HW2']:the_event  = 'down'elif self.state in ['HL2']:the_event = 'up'else:the_event = 'unchange' elif rate <-0.09:if self.state in ['HL1','HB','HW1','HW2']:the_event = 'down'else:the_event = 'unchange'elif rate >=0.03 and rate <0.09:if self. state in ['HL2','HL1','HB']:the_event = 'up'elif self.state in ['HW2']:the_event = 'down'else:the_event = 'unchange'else:if self.state in ['HL2','HL1','HB','HW1']:the_event = 'up'else:the_event = 'unchange'self.trigger(the_event)# 当资产损失超过阈值,会被停止if rate < max_cap_loss_rate:self._sell(price = close, dt =dt)self.trigger('stop')return True# 判断买卖## 时间限制buy_dt = self.data['buy_dt']time_gap = self._get_time_gap(start_dt =buy_dt ,end_dt =dt , time_unit ='hours')if time_gap < 8 :the_event = 'unchange'return True          else:if time_gap/24 >= max_order_hold_days:self._sell(price = close, dt =dt)self.trigger('sell')print('a')else:# 模型控制if decision_score < model_singal_score_sell:self._sell(price = close, dt =dt)self.trigger('sell')print('b')# 订单交易控制else:order_float_rev = (close - self.data['buy_price'])/self.data['buy_price']if order_float_rev >= order_win_stop_rate:self._sell(price = close, dt =dt)self.trigger('sell')print('c')elif order_float_rev < order_loss_stop_rate:self._sell(price = close, dt =dt)self.trigger('sell')print('d')else:self.trigger('unchange')return True                    # B是0+-3个点, W1是 6+-3个点,W2是大于9个点
# 11个状态
states = ['Init','EB','HB','HW1','HW2','EW1','EW2','HL1','HL2','EL1','EL2','Stop']
transitions = [# unchange事件{'trigger': 'unchange', 'source': 'Init', 'dest': 'EB'},{'trigger': 'unchange', 'source': 'EB', 'dest': 'EB'},{'trigger': 'unchange', 'source': 'HB', 'dest': 'HB'},{'trigger': 'unchange', 'source': 'HW1', 'dest': 'HW1'},{'trigger': 'unchange', 'source': 'HW2', 'dest': 'HW2'},{'trigger': 'unchange', 'source': 'EW1', 'dest': 'EW1'},{'trigger': 'unchange', 'source': 'EW2', 'dest': 'EW2'},{'trigger': 'unchange', 'source': 'HL1', 'dest': 'HL1'},{'trigger': 'unchange', 'source': 'HL2', 'dest': 'HL2'},{'trigger': 'unchange', 'source': 'EL1', 'dest': 'EL1'},{'trigger': 'unchange', 'source': 'EL2', 'dest': 'EL2'},# up事件:比上一个level高3个点, Init有一个InitCap,约定 B_center = InitCap,  B的Band定为3个点, B+3pt =  W1的下界以此类推{'trigger': 'up', 'source': 'Init', 'dest': 'EB'},# up对E无影响{'trigger': 'up', 'source': 'EB', 'dest': 'EB'},{'trigger': 'up', 'source': 'EW1', 'dest': 'EW1'},{'trigger': 'up', 'source': 'EW2', 'dest': 'EW2'},{'trigger': 'up', 'source': 'EL1', 'dest': 'EL1'},{'trigger': 'up', 'source': 'EL2', 'dest': 'EL2'},# up对H有影响{'trigger': 'up', 'source': 'HB', 'dest': 'HW1'},{'trigger': 'up', 'source': 'HW1', 'dest': 'HW2'},{'trigger': 'up', 'source': 'HW2', 'dest': 'Stop'},{'trigger': 'up', 'source': 'HL1', 'dest': 'HB'},{'trigger': 'up', 'source': 'HL2', 'dest': 'HL1'},    # down事件:类似up事件{'trigger': 'down', 'source': 'Init', 'dest': 'EB'},{'trigger': 'down', 'source': 'EB', 'dest': 'EB'},{'trigger': 'down', 'source': 'EW1', 'dest': 'EW1'},{'trigger': 'down', 'source': 'EW2', 'dest': 'EW2'},{'trigger': 'down', 'source': 'EL1', 'dest': 'EL1'},{'trigger': 'down', 'source': 'EL2', 'dest': 'EL2'},# down对H有影响{'trigger': 'down', 'source': 'HB', 'dest': 'HL1'},{'trigger': 'down', 'source': 'HW1', 'dest': 'HB'},{'trigger': 'down', 'source': 'HW2', 'dest': 'HW1'},{'trigger': 'down', 'source': 'HL1', 'dest': 'EL2'},{'trigger': 'down', 'source': 'HL2', 'dest': 'Stop'},    # buy事件,仅对E类生效{'trigger': 'buy', 'source': 'EB', 'dest': 'HB'},{'trigger': 'buy', 'source': 'EW1', 'dest': 'HW1'},{'trigger': 'buy', 'source': 'EW2', 'dest': 'HW2'},{'trigger': 'buy', 'source': 'EL1', 'dest': 'HL1'},{'trigger': 'buy', 'source': 'EL2', 'dest': 'HL2'},# sell事件,仅对H类生效{'trigger': 'sell', 'source': 'HB', 'dest': 'EB'},{'trigger': 'sell', 'source': 'HW1', 'dest': 'EW1'},{'trigger': 'sell', 'source': 'HW2', 'dest': 'EW2'},{'trigger': 'sell', 'source': 'HL1', 'dest': 'EL1'},{'trigger': 'sell', 'source': 'HL2', 'dest': 'EL2'}, # stop事件{'trigger': 'stop', 'source': 'Init', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'HB', 'dest': 'Stop'}, {'trigger': 'stop', 'source': 'HL1', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'HL2', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'HW1', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'HW2', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'EB', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'EL1', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'EL2', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'EW1', 'dest': 'Stop'},{'trigger': 'stop', 'source': 'EW2', 'dest': 'Stop'},# init事件{'trigger': 'init', 'source': 'Stop', 'dest': 'Init'},   
]

3 使用测试

先使用状态机对对象进行封装

from transitions import Machine
# 创建状态机
machine = Machine(model=BackTest2, states=states, transitions=transitions, initial='Init')bt2 = BackTest2(name='bt2',global_buffer_ip = wan_ip, space_name ='sp_qtv.bt001')
bt2._init_para(para_dict = strategy_para)

开始逐次运行测试,在实际使用时没个时隙唤起处理,然后再对具体的功能微调就可以了

res_tuple = bt2.get_data(dt_list = rec_dt_list, rec_list=rec_data_list)
if res_tuple is not None:print(bt2.meta['cash'] , bt2.meta['hold'])print(bt2.state)bt2.meta['last_dt'] = res_tuple[0]cur_data = res_tuple[1]bt2.ruleset(data=cur_data)else:print('next block')

测试1:测试模型分到达是否买入 9:32

Date: 2013-03-15 09:32:00
Record: {'data_dt': '2013-03-15 09:32:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 610}
6000 0
EBIn [155]: bt2.data
Out[155]:
{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-15 09:32:00','stocks': 1600,'buy_amt': 4800.0}

测试2:测试在8小时内,模型是否会hold住9没有在9:33卖出

Date: 2013-03-15 09:33:00
Record: {'data_dt': '2013-03-15 09:33:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 300}
1200.0 4800.0
HB
rate:  0.0In [157]: bt2.data
Out[157]:
{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-15 09:32:00','stocks': 1600,'buy_amt': 4800.0}

测试3:测试在8小时后,达到模型卖出分是否会卖出2013-03-16 09:33:00

Date: 2013-03-16 09:33:00
Record: {'data_dt': '2013-03-16 09:33:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 300}
1200.0 4800.0
HB
rate:  0.0
bIn [159]: bt2.data
Out[159]: {}In [160]: bt2.close_orders
Out[160]:
[{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-15 09:32:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 3.0,'sell_dt': '2013-03-16 09:33:00','sell_amt': 4800.0,'gp': 0.0,'np': -24.0,'npr': -0.005}]

测试4:测试卖出当日是否会再次买入2013-03-16 13:33:00

Date: 2013-03-16 13:33:00
Record: {'data_dt': '2013-03-16 13:33:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 601}
5976.0 0
EB
In [162]: bt2.data
Out[162]:
{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-16 13:33:00','stocks': 1600,'buy_amt': 4800.0}

测试5:测试模型达到持有时间上限后是否会卖出 2013-03-20 13:33:00

Date: 2013-03-20 13:33:00
Record: {'data_dt': '2013-03-20 13:33:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 601}
1176.0 4800.0
HB
rate:  -0.004
aIn [164]: bt2.data
Out[164]: {}In [165]: bt2.close_orders
Out[165]:
[{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-15 09:32:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 3.0,'sell_dt': '2013-03-16 09:33:00','sell_amt': 4800.0,'gp': 0.0,'np': -24.0,'npr': -0.005},{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-16 13:33:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 3.0,'sell_dt': '2013-03-20 13:33:00','sell_amt': 4800.0,'gp': 0.0,'np': -24.0,'npr': -0.005}]

测试6:在突然疯狂增长时,其状态可能不准确 2013-03-21 14:33:00 (EW1 - EW2)

Date: 2013-03-20 14:33:00
Record: {'data_dt': '2013-03-20 14:33:00', 'open': 3.13, 'close': 3.0, 'high': 3.14, 'low': 2.9, 'decision_score': 601}
5952.0 0
EBIn [169]: bt2.data
Out[169]:
{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-20 14:33:00','stocks': 1600,'buy_amt': 4800.0}同时可以看到全局资金也变少了
In [168]: bt2._get_global_control_meta(varname='monthly_quota')
Out[168]: 95200.0

测试7:止盈卖出(从打印c可以看到是订单本身的止盈卖出)

In [173]: bt2.close_orders
Out[173]:
[{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-15 09:32:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 3.0,'sell_dt': '2013-03-16 09:33:00','sell_amt': 4800.0,'gp': 0.0,'np': -24.0,'npr': -0.005},{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-16 13:33:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 3.0,'sell_dt': '2013-03-20 13:33:00','sell_amt': 4800.0,'gp': 0.0,'np': -24.0,'npr': -0.005},{'code': '510300','buy_price': 3.0,'buy_dt': '2013-03-20 14:33:00','stocks': 1600,'buy_amt': 4800.0,'sell_price': 13.0,'sell_dt': '2013-03-21 14:33:00','sell_amt': 20800.0,'gp': 16000.0,'np': 15896.0,'npr': 3.3117}]

测试8:订单止损卖出

Date: 2013-03-21 14:59:00
Record: {'data_dt': '2013-03-21 14:59:00', 'open': 3.13, 'close': 13.1, 'high': 3.14, 'low': 2.9, 'decision_score': 610}
21848.0 0
EW1In [175]: bt2.data
Out[175]:
{'code': '510300','buy_price': 13.1,'buy_dt': '2013-03-21 14:59:00','stocks': 300,'buy_amt': 3930.0}In [176]: res_tuple = bt2.get_data(dt_list = rec_dt_list, rec_list=rec_data_list)...: if res_tuple is not None:...:     print(bt2.meta['cash'] , bt2.meta['hold'])...:     print(bt2.state)...:     bt2.meta['last_dt'] = res_tuple[0]...:     cur_data = res_tuple[1]...:     bt2.ruleset(data=cur_data)...:...: else:...:     print('next block')...:
Date: 2013-03-22 14:00:00
Record: {'data_dt': '2013-03-22 14:00:00', 'open': 3.13, 'close': 0.1, 'high': 3.14, 'low': 2.9, 'decision_score': 610}
17918.0 3930.0
HW1
rate:  1.9913333333333334
d

4 结论

整体上,这个回测对象是可以使用的。

不必纠结于细节,可以直接进入下一步工程:主要是block规范的实现。数据的请求均是以block为单位,通过block manager实现。所以回测对象还需要被上一层的对象调用,形成worker - player模式。

另外,可以假设日数据可用(可盈利),试着以日收盘为周期建模,看效果。

这篇关于Python 算法交易实验68 回测对象重构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/815341

相关文章

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

Python中使用defaultdict和Counter的方法

《Python中使用defaultdict和Counter的方法》本文深入探讨了Python中的两个强大工具——defaultdict和Counter,并详细介绍了它们的工作原理、应用场景以及在实际编... 目录引言defaultdict的深入应用什么是defaultdictdefaultdict的工作原理

Python中@classmethod和@staticmethod的区别

《Python中@classmethod和@staticmethod的区别》本文主要介绍了Python中@classmethod和@staticmethod的区别,文中通过示例代码介绍的非常详细,对大... 目录1.@classmethod2.@staticmethod3.例子1.@classmethod

Python手搓邮件发送客户端

《Python手搓邮件发送客户端》这篇文章主要为大家详细介绍了如何使用Python手搓邮件发送客户端,支持发送邮件,附件,定时发送以及个性化邮件正文,感兴趣的可以了解下... 目录1. 简介2.主要功能2.1.邮件发送功能2.2.个性签名功能2.3.定时发送功能2. 4.附件管理2.5.配置加载功能2.6.

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写