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模式。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/747078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matplotlib-直方图

日期&#xff1a;2024.03.114 内容&#xff1a;将matplotlib的常用方法做一个记录&#xff0c;方便后续查找。 # 引入需要的库 from matplotlib import pyplot as plt import numpy as np# 设置画布大小 plt.figure(figsize (20,8),dpi 200)# 全局设置中文字体 plt.rcParams…

Android NDK入门:在应用中加入C和C++的力量

目录 ​编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师&#xff0c;却从来没有真正仔细了解过NDK&#…

Swift:.ignoresSafeArea():自由布局的全方位掌握

ignoresSafeArea(_ regions : edges:)修饰符的说明 SwiftUI布局系统会调整视图的尺寸和位置&#xff0c;以避免特定的安全区域。这就确保了系统内容&#xff08;比如软件键盘&#xff09;或设备边缘不会遮挡您的视图。要将您的内容扩展到这些区域&#xff0c;您可以通过应用该修…

VC++ 设置网卡接口MTU大小

在 Windows C/C 之中一共有三种方法可以设置网卡的MTU大小。 方法一&#xff1a; SetIpInterfaceEntry 法 static bool SetInterfaceMtu2(int interface_index, int mtu) noexcept{PIP_ADAPTER_ADDRESSES pAddresses NULL;ULONG ulBufLen 0;GetAdaptersAddresses(AF_UNSPEC…

植物神经功能紊乱患者每天从5片黛力新减少至2片,只因找对了治疗方法!

植物神经功能紊乱是一种常见的心理疾病&#xff0c;其症状包括焦虑、失眠、疲劳、头痛、胃肠不适等&#xff0c;给患者带来很大的困扰。然而&#xff0c;这种疾病是可以治疗的。本文将介绍一位植物神经功能紊乱患者的治疗经历&#xff0c;希望能够帮助更多的人了解和治疗此病。…

生成二维码及加入logo和文字

<html> <!-- 存放二维码的容器 --> <div idqrcode></div> <script typetext/javascript srchttp://cdn.staticfile.org/jquery/2.1.1/jquery.min.js></script> <script src"https://cdn.bootcss.com/jquery.qrcode/1.0/jquery.qr…

Chrome 跨域问题CORS 分析

先叠个甲,有错误,望沟通指正! 文章目录 1.什么是跨域报错2.为什么postman可以,浏览器访问就不行?根本原因是什么?2.1浏览器是依据什么来报错跨域的? 3.常规解决方案的分析方案1.通过代理解决方案2.被请求的B域的服务端开启Access-Control-Allow-Origin返回头的支持方案3.通…

【python】列表篇

文章目录 列表的索引与切片列表删除列表增加调整类型后转换为列表count和index函数 列表的索引与切片 # List [P, y, t, h, o, n] List list(map(str,Python)) print(List[3]) print(List[-4:])列表删除 Python 列表&#xff08;list&#xff09;中常用的删除元素的函数和方…

python前端开发

前端开发 快速网站开发 from flask import Flask appFlask(__name__) #创建网址/show/info 和函数index的对应关系&#xff0c; #访问网站&#xff0c;执行index()函数 app.route("/show/info") def index():return "中国联通" if __name__"__main_…

Three.js基础入门介绍——Three.js学习七【播放模型动画时模型沿着轨迹移动】

效果描述 在播放导入的模型动画同时&#xff0c;让模型沿着预定路径轨迹移动。例如导入一个会跑步动作的模型&#xff0c;让它沿着一条类似跑道的路径跑步移动。 实现流程 基本流程 1、搭建场景 2、添加模型和播放动画 3、添加路径和模型移动 工程文件 工程文件结构如下图&…

谁用过腾讯云轻量应用服务器2核2G3M配置,支持多少人在线?

腾讯云轻量应用服务器2核4G5M配置一年优惠价165元、252元15个月、三年756元&#xff0c;100%CPU性能&#xff0c;5M带宽下载速度640KB/秒&#xff0c;60GB SSD系统盘&#xff0c;月流量500GB&#xff0c;折合每天16.6GB流量&#xff0c;超出月流量包的流量按照0.8元每GB的价格支…

InheritableThreadLocal和ThreadLocal的区别和使用场景

快人快语&#xff0c;先说结论&#xff0c;InheritableThreadLocal 是 ThreadLocal 的一个子类&#xff0c;它包含ThreadLocal 的所有功能并且扩展了 ThreadLocal 的功能&#xff0c;允许父线程中的 InheritableThreadLocal 变量的值被子线程继承。这意味着&#xff0c;当创建一…

常用芯片学习——TP4057电源管理芯片

TP40578 500mA线性锂离子电池充电器 芯片介绍 TP4057是一款性能优异的单节锂离子电池恒流/恒压线性充电器。TP4057采用S0T23-6封装配合较少的外围原件使其非常适用于便携式产品&#xff0c;并且适合给USB电源以及适配器电源供电。 基于特殊的内部MOSFET架构以及防倒充电路&a…

Python实现一笔画游戏

Python实现一笔画游戏 关于一笔画介绍可参见“HTML5实现一笔画游戏”https://blog.csdn.net/cnds123/article/details/136669088 在Python中&#xff0c;Tkinter是一个广泛使用的标准GUI库&#xff0c;我们将使用它来实现这个游戏。 先给出效果图&#xff1a; 连接线段时&am…

MQL语言实现抽象工厂模式

文章目录 一、定义抽象产品接口二、定义抽象工厂接口三、定义具体产品四、定义具体工厂五、定义工厂客户端六、客户端调用工厂客户端七、抽象工厂模式的结构 一、定义抽象产品接口 //------------------------------------------------------------------ //| participants …

城乡居民基本医疗信息管理系统|基于Springboot的城乡居民基本医疗信息管理系统设计与实现(源码+数据库+文档)

城乡居民基本医疗信息管理系统目录 目录 基于Springboot的城乡居民基本医疗信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、病例管理 2、医院资讯信息管理 3、医院资讯类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、创建新的分包goods_list 二、将请求到的楼层数据url调整为本地的 可以看到上图是请求…

关于固件的简单解释

我不喜欢等人也不喜欢被别人等——赤砂之蝎 简而言之 固件是什么 固件&#xff08;Firmware&#xff09;是一种软件类型&#xff0c;它是嵌入式系统中的一部分&#xff0c;通常存储在设备的非易失性存储器中&#xff0c;如闪存或ROM&#xff08;只读存储器&#xff09;。与操作…

蓝桥杯---棋盘(典型的二维差分问题)

题目链接&#xff1a;棋盘 这道题真的是非常典型的二维差分问题了&#xff08;在我个人看来&#xff09;&#xff0c;题目中的0和1&#xff0c;我们直接让差分数组&#xff0c;偶数就是0&#xff0c;奇数就是1.初始化是0&#xff0c;是白子&#xff08;偶数&#xff09;&#x…

libevent中bufferevent事件及常用的API函数

自带buffer的事件-bufferevent bufferevent实际上也是一个event&#xff0c;只不过比普通的event高级&#xff0c;他的内部有两个缓冲区&#xff0c;以及一个文件描述符&#xff08;网络套接字&#xff09;。一个网络套接字有读写两个缓冲区&#xff0c;bufferevent同样也带有…