燕十八html教程网站建设,宿迁做网站电话,东莞企业网站定制设计,企业名字查重系统说明
从有这个想法#xff0c;到勉强完工#xff0c;整个过程还是持续了很长时间。主要原因有#xff1a;
1 去年12月没有遵守【原则】#xff0c;手工乱下了一堆单子#xff0c;然后满仓了。等这些单子“解冻”估计还要一阵子#xff0c;所以也没有很急。2 在做的过程…说明
从有这个想法到勉强完工整个过程还是持续了很长时间。主要原因有
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_sellfor 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,persistyes):# 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(varnamemonthly_quota)monthly_quota - self.data[buy_amt] self._set_global_control_meta(varnamemonthly_quota, valuemonthly_quota)return True# 卖def _sell(self, price None, dt None, fee0.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(varnamemonthly_quota)monthly_quota self.data[buy_amt] self._set_global_control_meta(varnamemonthly_quota, valuemonthly_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 unchangeself.trigger(the_event)return Trueif self.state.startswith(E):# 如果收到全局控制就不能再买入monthly_quota self._get_global_control_meta(varnamemonthly_quota)if monthly_quota per_order_amt:self.trigger(unchange)return True# 判断买卖if decision_score model_singal_score_buy:the_event buyself._buy(price close, dt dt )else:the_event unchangeself.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 upelif self.state in [HW1,HW2]:the_event downelse:the_event unchange elif rate -0.09 and rate -0.03:if self.state in [HB,HW1,HW2]:the_event downelif self.state in [HL2]:the_event upelse:the_event unchange elif rate -0.09:if self.state in [HL1,HB,HW1,HW2]:the_event downelse:the_event unchangeelif rate 0.03 and rate 0.09:if self. state in [HL2,HL1,HB]:the_event upelif self.state in [HW2]:the_event downelse:the_event unchangeelse:if self.state in [HL2,HL1,HB,HW1]:the_event upelse:the_event unchangeself.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 unchangereturn 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个点, B3pt 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(modelBackTest2, statesstates, transitionstransitions, initialInit)bt2 BackTest2(namebt2,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_listrec_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(datacur_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(varnamemonthly_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_listrec_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(datacur_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
d4 结论
整体上这个回测对象是可以使用的。
不必纠结于细节可以直接进入下一步工程主要是block规范的实现。数据的请求均是以block为单位通过block manager实现。所以回测对象还需要被上一层的对象调用形成worker - player模式。
另外可以假设日数据可用可盈利试着以日收盘为周期建模看效果。