├── Boll_Control_Proportion_vix.py ├── Boll_Std_Vix.py ├── README.md ├── __init__.py ├── atrstop_dc_strategy.py ├── atrstop_rsi_dc_strategy.py ├── back_testing_to_csv.py ├── backtesing_demo.ipynb ├── boll_control_dc_strategy.py ├── boll_kc_dc_combination_strategy.py ├── boll_kc_dc_simple_strategy.py ├── boll_kk_vix_simple_strategy.py ├── dualthrust_kk_strategy.py ├── mike_boll_strategy.py ├── mike_dc_strategy.py ├── super_trend_strategy.py ├── 使用plotly库显示的回测引擎.py ├── 回测引行.py ├── 回测过拟合概率定量计算.py ├── 多策略组合回测.py ├── 多进程回测引行.py ├── 测试.py ├── 遗传穷举算法回测引擎.py └── 遗传算法回测引擎.py /Boll_Control_Proportion_vix.py: -------------------------------------------------------------------------------- 1 | from vnpy.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder, 4 | TickData, 5 | BarData, 6 | TradeData, 7 | OrderData, 8 | BarGenerator, 9 | ArrayManager, 10 | ) 11 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 12 | 13 | class Boll_Control_Proportion_vix(CtaTemplate): 14 | """""" 15 | author = "yunya" 16 | 17 | win_open = 36 18 | boll_window = 24 19 | prop = 1.8 20 | atr_window = 30 21 | sl_multiplier = 4.4 22 | fixed_size = 1 23 | 24 | entry_crossover = 0 25 | atr_value = 0 26 | intra_trade_high = 0 27 | intra_trade_low = 0 28 | long_stop = 0 29 | short_stop = 0 30 | 31 | parameters = [ 32 | "win_open", 33 | "boll_window", 34 | "prop", 35 | "sl_multiplier", 36 | "fixed_size", 37 | ] 38 | 39 | variables = [ 40 | "entry_crossover", 41 | "atr_value", 42 | "intra_trade_high", 43 | "intra_trade_low", 44 | "long_stop", 45 | "short_stop" 46 | ] 47 | 48 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 49 | """""" 50 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 51 | 52 | self.bg = NewBarGenerator(self.on_bar, self.win_open, self.on_xmin_bar) 53 | self.am = ArrayManager(self.boll_window + 100) 54 | 55 | def on_init(self): 56 | """ 57 | Callback when strategy is inited. 58 | """ 59 | self.write_log("策略初始化") 60 | self.load_bar(10) 61 | 62 | def on_start(self): 63 | """ 64 | Callback when strategy is started. 65 | """ 66 | self.write_log("策略启动") 67 | self.put_event() 68 | 69 | def on_stop(self): 70 | """ 71 | Callback when strategy is stopped. 72 | """ 73 | self.write_log("策略停止") 74 | self.put_event() 75 | 76 | def on_tick(self, tick: TickData): 77 | """ 78 | Callback of new tick data update. 79 | """ 80 | self.bg.update_tick(tick) 81 | 82 | def on_bar(self, bar: BarData): 83 | """ 84 | Callback of new bar data update. 85 | """ 86 | self.bg.update_bar(bar) 87 | 88 | def on_xmin_bar(self, bar: BarData): 89 | """""" 90 | self.cancel_all() 91 | 92 | am = self.am 93 | am.update_bar(bar) 94 | if not am.inited: 95 | return 96 | 97 | # Calculate array 计算数组 98 | self.sma_array = am.sma(self.boll_window, True) 99 | std_array = am.sma(self.boll_window, True) 100 | dev = abs(self.am.close[:-1] - self.sma_array[:-1]) / std_array[:-1] 101 | dev_max = dev[-self.boll_window:].max() 102 | self.boll_up_array = self.sma_array + std_array * dev_max 103 | self.boll_down_array = self.sma_array - std_array * dev_max 104 | 105 | # Get current and last index 106 | current_sma = self.sma_array[-1] 107 | last_close = self.am.close[-2] 108 | currnet_boll_up = self.boll_up_array[-1] 109 | last_boll_up = self.boll_up_array[-2] 110 | current_boll_down = self.boll_down_array[-1] 111 | last_boll_down = self.boll_down_array[-2] 112 | up_limit = current_sma * (1 + self.prop / 100) 113 | down_limit = current_sma * (1 - self.prop / 100) 114 | 115 | # Get crossover 116 | if ( 117 | last_close <= last_boll_up 118 | and bar.close_price > currnet_boll_up 119 | and bar.close_price < up_limit 120 | ): 121 | self.entry_crossover = 1 122 | elif ( 123 | last_close >= last_boll_down 124 | and bar.close_price < current_boll_down 125 | and bar.close_price > down_limit 126 | ): 127 | self.entry_crossover = -1 128 | 129 | self.atr_value = am.atr(self.atr_window) 130 | 131 | if not self.pos: 132 | self.intra_trade_high = bar.high_price 133 | self.intra_trade_low = bar.low_price 134 | 135 | if self.entry_crossover > 0: 136 | self.buy(up_limit, self.fixed_size, True) 137 | 138 | elif self.entry_crossover < 0: 139 | self.short(down_limit, self.fixed_size, True) 140 | 141 | elif self.pos > 0: 142 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 143 | self.intra_trade_low = bar.low_price 144 | 145 | self.long_stop = self.intra_trade_high - self.atr_value * self.sl_multiplier 146 | self.sell(self.long_stop, abs(self.pos), True) 147 | 148 | elif self.pos < 0: 149 | self.intra_trade_high = bar.high_price 150 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 151 | 152 | self.short_stop = self.intra_trade_low + self.atr_value * self.sl_multiplier 153 | self.cover(self.short_stop, abs(self.pos), True) 154 | 155 | self.put_event() 156 | 157 | def on_order(self, order: OrderData): 158 | """ 159 | Callback of new order data update. 160 | """ 161 | self.put_event() 162 | pass 163 | 164 | def on_trade(self, trade: TradeData): 165 | """ 166 | Callback of new trade data update. 167 | """ 168 | self.put_event() 169 | 170 | def on_stop_order(self, stop_order: StopOrder): 171 | """ 172 | Callback of stop order update. 173 | """ 174 | self.put_event() 175 | pass 176 | 177 | 178 | -------------------------------------------------------------------------------- /Boll_Std_Vix.py: -------------------------------------------------------------------------------- 1 | from vnpy.app.cta_strategy import ( 2 | CtaTemplate, 3 | StopOrder, 4 | TickData, 5 | BarData, 6 | TradeData, 7 | OrderData, 8 | BarGenerator, 9 | ArrayManager, 10 | ) 11 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 12 | 13 | class Boll_Std_vix(CtaTemplate): 14 | """""" 15 | author = "yunya" 16 | 17 | win_open = 15 18 | boll_window = 80 19 | atr_window = 30 20 | sl_multiplier = 10.0 21 | fixed_size = 1 22 | 23 | entry_crossover = 0 24 | atr_value = 0 25 | intra_trade_high = 0 26 | intra_trade_low = 0 27 | long_stop = 0 28 | short_stop = 0 29 | 30 | parameters = [ 31 | "win_open", 32 | "boll_window", 33 | "sl_multiplier", 34 | "fixed_size", 35 | ] 36 | 37 | variables = [ 38 | "entry_crossover", 39 | "atr_value", 40 | "intra_trade_high", 41 | "intra_trade_low", 42 | "long_stop", 43 | "short_stop" 44 | ] 45 | 46 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 47 | """""" 48 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 49 | 50 | self.bg = NewBarGenerator(self.on_bar, self.win_open, self.on_xmin_bar) 51 | self.am = ArrayManager(self.boll_window + 100) 52 | 53 | def on_init(self): 54 | """ 55 | Callback when strategy is inited. 56 | """ 57 | self.write_log("策略初始化") 58 | self.load_bar(10) 59 | 60 | def on_start(self): 61 | """ 62 | Callback when strategy is started. 63 | """ 64 | self.write_log("策略启动") 65 | self.put_event() 66 | 67 | def on_stop(self): 68 | """ 69 | Callback when strategy is stopped. 70 | """ 71 | self.write_log("策略停止") 72 | self.put_event() 73 | 74 | def on_tick(self, tick: TickData): 75 | """ 76 | Callback of new tick data update. 77 | """ 78 | self.bg.update_tick(tick) 79 | 80 | def on_bar(self, bar: BarData): 81 | """ 82 | Callback of new bar data update. 83 | """ 84 | self.bg.update_bar(bar) 85 | 86 | def on_xmin_bar(self, bar: BarData): 87 | """""" 88 | self.cancel_all() 89 | 90 | am = self.am 91 | am.update_bar(bar) 92 | if not am.inited: 93 | return 94 | 95 | # Calculate array 计算数组 96 | self.sma_array = am.sma(self.boll_window, True) 97 | std_array = am.std(self.boll_window, True) 98 | dev = abs(self.am.close[:-1] - self.sma_array[:-1]) / std_array[:-1] 99 | dev_max = dev[-self.boll_window:].max() 100 | self.boll_up_array = self.sma_array + std_array * dev_max 101 | self.boll_down_array = self.sma_array - std_array * dev_max 102 | 103 | # Get current and last index 104 | current_sma = self.sma_array[-1] 105 | last_sma = self.sma_array[-2] 106 | last_close = self.am.close[-2] 107 | currnet_boll_up = self.boll_up_array[-1] 108 | last_boll_up = self.boll_up_array[-2] 109 | current_boll_down = self.boll_down_array[-1] 110 | last_boll_down = self.boll_down_array[-2] 111 | 112 | 113 | # Get crossover 114 | if (bar.close_price > currnet_boll_up and last_close <= last_boll_up): 115 | self.entry_crossover = 1 116 | 117 | elif(bar.close_price < current_boll_down and last_close >=last_boll_down): 118 | self.entry_crossover = -1 119 | 120 | self.atr_value = am.atr(self.atr_window) 121 | 122 | if not self.pos: 123 | self.intra_trade_high = bar.high_price 124 | self.intra_trade_low = bar.low_price 125 | 126 | if self.entry_crossover > 0: 127 | self.buy(bar.close_price, self.fixed_size, True) 128 | 129 | elif self.entry_crossover < 0: 130 | self.short(bar.close_price, self.fixed_size, True) 131 | 132 | elif self.pos > 0: 133 | 134 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 135 | self.intra_trade_low = bar.low_price 136 | 137 | self.long_stop = self.intra_trade_high - self.atr_value * self.sl_multiplier 138 | self.sell(self.long_stop, abs(self.pos), True) 139 | 140 | elif self.pos < 0: 141 | 142 | self.intra_trade_high = bar.high_price 143 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 144 | 145 | self.short_stop = self.intra_trade_low + self.atr_value * self.sl_multiplier 146 | self.cover(self.short_stop, abs(self.pos), True) 147 | 148 | self.put_event() 149 | 150 | def on_order(self, order: OrderData): 151 | """ 152 | Callback of new order data update. 153 | """ 154 | self.put_event() 155 | pass 156 | 157 | def on_trade(self, trade: TradeData): 158 | """ 159 | Callback of new trade data update. 160 | """ 161 | self.put_event() 162 | 163 | def on_stop_order(self, stop_order: StopOrder): 164 | """ 165 | Callback of stop order update. 166 | """ 167 | self.put_event() 168 | pass 169 | 170 | 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # master -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/15 11:06 5 | #文件名称 :__init__.py.py 6 | #开发工具 : PyCharm -------------------------------------------------------------------------------- /atrstop_dc_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/14 22:04 5 | #文件名称 :atrstop_dc_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | from typing import Any 9 | import numpy as np 10 | from vnpy.app.cta_strategy import ( 11 | CtaTemplate, 12 | BarGenerator, 13 | ArrayManager, 14 | TickData, 15 | OrderData, 16 | BarData, 17 | TradeData, 18 | StopOrder 19 | ) 20 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 21 | 22 | class AtrStop_Dc_Strategy(CtaTemplate): 23 | """""" 24 | author = "yunya" 25 | 26 | atrstop_window = 46 27 | open_window = 5 28 | distance_line = 2.0 29 | nloss_singnal = 2.7 30 | dc_length = 50 31 | fixd_size = 1 32 | atr_window = 30 33 | 34 | atr_entry = 0 35 | current_atr_stop = 0.0 36 | last_atr_stop = 0.0 37 | nloss_array = 0.0 38 | exit_short = 0 39 | exit_long = 0 40 | 41 | ask = 0 42 | bid = 0 43 | atr_value = 0 44 | 45 | parameters = [ 46 | "atrstop_window", 47 | "open_window", 48 | "nloss_singnal", 49 | "dc_length", 50 | "distance_line", 51 | "fixd_size", 52 | "atr_window" 53 | ] 54 | 55 | variables = [ 56 | "current_atr_stop", 57 | "last_atr_stop", 58 | "exit_short", 59 | "exit_long", 60 | "atr_entry", 61 | ] 62 | 63 | def __init__( 64 | self, 65 | cta_engine: Any, 66 | strategy_name: str, 67 | vt_symbol: str, 68 | setting: dict, 69 | ): 70 | """""" 71 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 72 | self.atr_stop_array = np.zeros(10) 73 | 74 | self.bg_xmin = NewBarGenerator( 75 | self.on_bar, 76 | window=self.atrstop_window, 77 | on_window_bar=self.on_xmin_bar 78 | ) 79 | self.am_xmin = ArrayManager() 80 | 81 | self.bg_5min = BarGenerator( 82 | self.on_bar, 83 | window=self.open_window, 84 | on_window_bar=self.on_5min_bar 85 | ) 86 | self.am_5min = ArrayManager() 87 | 88 | def on_init(self): 89 | """ 90 | Callback when strategy is inited. 91 | """ 92 | self.write_log("策略初始化。。") 93 | self.load_bar(10) 94 | 95 | def on_start(self): 96 | """ 97 | Callback when strategy is started. 98 | """ 99 | self.write_log("策略启动。。") 100 | 101 | def on_stop(self): 102 | """ 103 | Callback when strategy is stopped. 104 | """ 105 | self.write_log("策略停止。。") 106 | 107 | def on_tick(self, tick: TickData): 108 | """ 109 | Callback of new tick data update. 110 | """ 111 | self.bg_5min.update_tick(tick) 112 | 113 | def on_bar(self, bar: BarData): 114 | """ 115 | Callback of new bar data update. 116 | """ 117 | self.bg_xmin.update_bar(bar) 118 | self.bg_5min.update_bar(bar) 119 | 120 | def on_5min_bar(self, bar: BarData): 121 | 122 | self.cancel_all() 123 | self.am_5min.update_bar(bar) 124 | 125 | if not self.am_5min.inited or not self.am_xmin.inited: 126 | return 127 | if self.atr_stop_array[-3] == 0: 128 | return 129 | self.atr_value = self.am_5min.atr(self.atr_window) 130 | 131 | if not self.pos: 132 | self.intra_trade_high = bar.high_price 133 | self.intra_trade_low = bar.low_price 134 | 135 | up_limit = self.current_atr_stop * (1 + self.distance_line / 100) 136 | down_limit = self.current_atr_stop * (1 - self.distance_line / 100) 137 | 138 | if self.atr_entry > 0 and bar.close_price < up_limit: 139 | self.buy(self.current_atr_stop, self.fixd_size, True) 140 | 141 | elif self.atr_entry < 0 and bar.close_price > down_limit: 142 | self.short(self.current_atr_stop, self.fixd_size, True) 143 | 144 | elif self.pos > 0: 145 | self.sell(self.exit_long, abs(self.pos), True) 146 | 147 | elif self.pos < 0: 148 | self.cover(self.exit_short, abs(self.pos), True) 149 | self.put_event() 150 | 151 | def on_xmin_bar(self, bar: BarData): 152 | """""" 153 | am_xmin = self.am_xmin 154 | am_xmin.update_bar(bar) 155 | 156 | self.atr_stop_array[:-1] = self.atr_stop_array[1:] 157 | 158 | if not am_xmin.inited: 159 | return 160 | 161 | # 计算轨道线 nloss 162 | self.ema_array = am_xmin.ema(3, array=True) 163 | self.nloss_array = am_xmin.atr(16, array=True) * self.nloss_singnal 164 | 165 | # 计算轨道线 166 | self.atr_stop_array = self.atrstop( 167 | am_xmin.close, 168 | self.atr_stop_array, 169 | self.nloss_array 170 | ) 171 | 172 | # 初始化 173 | if self.atr_stop_array[-3] == 0: 174 | return 175 | 176 | self.current_atr_stop = self.atr_stop_array[-1] 177 | self.last_atr_stop = self.atr_stop_array[-2] 178 | current_ema = self.ema_array[-1] 179 | last_ema = self.ema_array[-2] 180 | 181 | if last_ema <= self.last_atr_stop and current_ema > self.current_atr_stop: 182 | self.atr_entry = 1 183 | elif last_ema >= self.last_atr_stop and current_ema < self.current_atr_stop: 184 | self.atr_entry = -1 185 | 186 | self.exit_short,self.exit_long = self.am_xmin.donchian(self.dc_length) 187 | self.put_event() 188 | 189 | def on_trade(self, trade: TradeData): 190 | """ 191 | 有成交时 192 | Callback of new trade data update. 193 | """ 194 | self.put_event() 195 | 196 | def on_order(self, order: OrderData): 197 | """ 198 | 订单更新回调 199 | Callback of new order data update. 200 | """ 201 | 202 | self.put_event() 203 | 204 | def on_stop_order(self, stop_order: StopOrder): 205 | """ 206 | Callback of stop order update. 207 | """ 208 | self.put_event() 209 | 210 | def atrstop(self, close, atrstop, nlossatr): 211 | 212 | # 计算轨道线 213 | if (close[-1] > atrstop[-2]) and (close[-2] > atrstop[-2]): 214 | atrstop[-1] = max(atrstop[-2], close[-1] - nlossatr[-1]) 215 | 216 | elif (close[-1] < atrstop[-2]) and (close[-2] < atrstop[-2]): 217 | atrstop[-1] = min(atrstop[-2], close[-1] + nlossatr[-1]) 218 | 219 | elif (close[-1] > atrstop[-2]): 220 | atrstop[-1] = (close[-1] - nlossatr[-1]) 221 | 222 | else: 223 | atrstop[-1] = (close[-1] + nlossatr[-1]) 224 | return atrstop 225 | 226 | 227 | -------------------------------------------------------------------------------- /atrstop_rsi_dc_strategy.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | import numpy as np 3 | import talib 4 | 5 | from vnpy.app.cta_strategy import ( 6 | CtaTemplate, 7 | BarGenerator, 8 | ArrayManager, 9 | TickData, 10 | OrderData, 11 | BarData, 12 | TradeData, 13 | StopOrder 14 | ) 15 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 16 | from vnpy.trader.constant import Interval 17 | 18 | 19 | class AtrStopRsiDcStrategy(CtaTemplate): 20 | """""" 21 | author = "yunya" 22 | 23 | hour_window = 1 24 | minute_window = 50 25 | open_window = 5 26 | rsi_length = 15 27 | distance_line = 2.0 28 | nloss_singnal = 3.1 29 | exit_dc_length = 30 30 | sl_multiplier = 8.0 31 | 32 | fixd_size = 1 33 | atr_window = 30 34 | 35 | exit_dowm = 0 36 | exit_up = 0 37 | atr_entry = 0 38 | rsi_entry = 0 39 | current_atr_stop = 0.0 40 | last_atr_stop = 0.0 41 | intra_trade_high = 0 42 | intra_trade_low = 0 43 | nloss_array = 0.0 44 | long_stop = 0 45 | short_stop = 0 46 | ask = 0 47 | bid = 0 48 | atr_value = 0 49 | 50 | parameters = [ 51 | "hour_window", 52 | "minute_window", 53 | "open_window", 54 | "nloss_singnal", 55 | "rsi_length", 56 | "exit_dc_length", 57 | "sl_multiplier", 58 | "distance_line", 59 | "fixd_size", 60 | "atr_window" 61 | ] 62 | 63 | variables = [ 64 | "current_atr_stop", 65 | "last_atr_stop", 66 | "long_stop", 67 | "short_stop", 68 | "atr_entry", 69 | "atr_value", 70 | "ask", 71 | "bid" 72 | ] 73 | 74 | def __init__( 75 | self, 76 | cta_engine: Any, 77 | strategy_name: str, 78 | vt_symbol: str, 79 | setting: dict, 80 | ): 81 | """""" 82 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 83 | self.atr_stop_array = np.zeros(10) 84 | 85 | self.bg_xhour= NewBarGenerator( 86 | on_bar= self.on_bar, 87 | window=self.hour_window, 88 | on_window_bar=self.on_xhour_bar, 89 | interval=Interval.HOUR 90 | ) 91 | self.am_hour = ArrayManager() 92 | 93 | self.bg_xminute = NewBarGenerator( 94 | on_bar=self.on_bar, 95 | window=self.minute_window, 96 | on_window_bar=self.on_xminute_bar 97 | ) 98 | self.am_xminute = ArrayManager() 99 | 100 | 101 | self.bg_open = NewBarGenerator( 102 | on_bar=self.on_bar, 103 | window=self.open_window, 104 | on_window_bar=self.on_5min_bar 105 | ) 106 | self.am_open = ArrayManager() 107 | 108 | def on_init(self): 109 | """ 110 | Callback when strategy is inited. 111 | """ 112 | self.write_log("策略初始化。。") 113 | self.load_bar(10) 114 | 115 | self.put_event() 116 | 117 | def on_start(self): 118 | """ 119 | Callback when strategy is started. 120 | """ 121 | self.write_log("策略启动。。") 122 | self.put_event() 123 | 124 | def on_stop(self): 125 | """ 126 | Callback when strategy is stopped. 127 | """ 128 | self.write_log("策略停止。。") 129 | self.put_event() 130 | 131 | def on_tick(self, tick: TickData): 132 | """ 133 | Callback of new tick data update. 134 | """ 135 | self.bg_open.update_tick(tick) 136 | self.ask = tick.ask_price_1 # 卖一价 137 | self.bid = tick.bid_price_1 # 买一价 138 | 139 | self.put_event() 140 | 141 | def on_bar(self, bar: BarData): 142 | """ 143 | Callback of new bar data update. 144 | """ 145 | self.bg_xhour.update_bar(bar) 146 | self.bg_xminute.update_bar(bar) 147 | self.bg_open.update_bar(bar) 148 | 149 | def on_5min_bar(self, bar: BarData): 150 | 151 | self.cancel_all() 152 | self.am_open.update_bar(bar) 153 | 154 | if not self.am_open.inited or not self.am_xminute.inited or not self.am_hour.inited: 155 | return 156 | 157 | self.atr_value = self.am_open.atr(self.atr_window) 158 | 159 | if not self.pos: 160 | self.intra_trade_high = bar.high_price 161 | self.intra_trade_low = bar.low_price 162 | 163 | up_limit = self.current_atr_stop * (1 + self.distance_line / 100) 164 | down_limit = self.current_atr_stop * (1 - self.distance_line / 100) 165 | 166 | if self.atr_entry > 0 and self.rsi_entry > 0 and bar.close_price < up_limit: 167 | 168 | self.buy(up_limit, self.fixd_size, True) 169 | 170 | elif self.atr_entry < 0 and self.rsi_entry < 0 and bar.close_price > down_limit: 171 | self.short(down_limit, self.fixd_size, True) 172 | 173 | elif self.pos > 0: 174 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 175 | self.intra_trade_low = bar.low_price 176 | long_stop_high = self.intra_trade_high - self.atr_value * self.sl_multiplier 177 | 178 | self.long_stop = max(self.exit_up,long_stop_high) 179 | self.sell(self.long_stop, abs(self.pos), True) 180 | 181 | elif self.pos < 0: 182 | self.intra_trade_high = bar.high_price 183 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 184 | short_stop_low = self.intra_trade_low + self.atr_value * self.sl_multiplier 185 | 186 | self.short_stop = min(self.exit_dowm,short_stop_low) 187 | self.cover(self.short_stop, abs(self.pos), True) 188 | 189 | self.put_event() 190 | 191 | def on_xminute_bar(self, bar: BarData): 192 | """ 193 | :param bar: 194 | :return: 195 | """ 196 | self.am_xminute.update_bar(bar) 197 | 198 | if not self.am_xminute.inited: 199 | return 200 | 201 | rsi_array = talib.RSI(self.am_xminute.close[:-1], self.rsi_length ) 202 | ema_array = talib.EMA(self.am_xminute.close,self.rsi_length) 203 | 204 | dev_array = abs(self.am_xminute.close[:-1] - ema_array[:-1]) / rsi_array 205 | 206 | rsi_up_array = rsi_array + rsi_array * dev_array 207 | rsi_dow_array = rsi_array - rsi_array * dev_array 208 | 209 | self.rsi_value = self.am_xminute.rsi(self.rsi_length,True) 210 | self.rsi_up = rsi_up_array[-1] 211 | self.rsi_dow = rsi_dow_array[-1] 212 | 213 | current_rsi_up = rsi_up_array[-1] 214 | last_rsi_up = rsi_up_array[-2] 215 | current_rsi_down = rsi_dow_array[-1] 216 | last_rsi_down = rsi_dow_array[-2] 217 | current_rsi_value = self.rsi_value[-1] 218 | last_rsi_value = self.rsi_value[-2] 219 | 220 | if (current_rsi_value > current_rsi_up) and (last_rsi_value <=last_rsi_up): 221 | self.rsi_entry = 1 222 | elif (current_rsi_value < current_rsi_down) and (last_rsi_value >= last_rsi_down): 223 | self.rsi_entry = -1 224 | else: 225 | self.rsi_entry = 0 226 | # print(self.rsi_entry) 227 | 228 | self.exit_dowm,self.exit_up = self.am_xminute.donchian(self.exit_dc_length) 229 | 230 | def on_xhour_bar(self, bar: BarData): 231 | """""" 232 | am_hour = self.am_hour 233 | am_hour.update_bar(bar) 234 | 235 | self.atr_stop_array[:-1] = self.atr_stop_array[1:] 236 | 237 | if not am_hour.inited: 238 | return 239 | 240 | # 计算轨道线 nloss 241 | self.ema_array = am_hour.ema(3, array=True) 242 | self.nloss_array = am_hour.atr(16, array=True) * self.nloss_singnal 243 | 244 | # 计算轨道线 245 | self.atr_stop_array = self.atrstop( 246 | am_hour.close, 247 | self.atr_stop_array, 248 | self.nloss_array 249 | ) 250 | # print(self.atr_stop_array) 251 | # 初始化 252 | if self.atr_stop_array[-3] == 0: 253 | return 254 | 255 | self.current_atr_stop = self.atr_stop_array[-1] 256 | self.last_atr_stop = self.atr_stop_array[-2] 257 | current_ema = self.ema_array[-1] 258 | last_ema = self.ema_array[-2] 259 | 260 | if current_ema > self.current_atr_stop and last_ema <= self.last_atr_stop: 261 | self.atr_entry = 1 262 | elif current_ema < self.current_atr_stop and last_ema >= self.last_atr_stop: 263 | self.atr_entry = -1 264 | 265 | self.put_event() 266 | 267 | def on_trade(self, trade: TradeData): 268 | """ 269 | 有成交时 270 | Callback of new trade data update. 271 | """ 272 | self.put_event() 273 | 274 | def on_order(self, order: OrderData): 275 | """ 276 | 订单更新回调 277 | Callback of new order data update. 278 | """ 279 | 280 | self.put_event() 281 | 282 | def on_stop_order(self, stop_order: StopOrder): 283 | """ 284 | Callback of stop order update. 285 | """ 286 | self.put_event() 287 | 288 | def atrstop(self, close, atrstop, nlossatr): 289 | 290 | # 计算轨道线 291 | if (close[-1] > atrstop[-2]) and (close[-2] > atrstop[-2]): 292 | atrstop[-1] = max(atrstop[-2], close[-1] - nlossatr[-1]) 293 | 294 | elif (close[-1] < atrstop[-2]) and (close[-2] < atrstop[-2]): 295 | atrstop[-1] = min(atrstop[-2], close[-1] + nlossatr[-1]) 296 | 297 | elif (close[-1] > atrstop[-2]): 298 | atrstop[-1] = (close[-1] - nlossatr[-1]) 299 | 300 | else: 301 | atrstop[-1] = (close[-1] + nlossatr[-1]) 302 | return atrstop 303 | 304 | -------------------------------------------------------------------------------- /back_testing_to_csv.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/16 21:04 5 | #文件名称 :back_testing_to_csv.py 6 | #开发工具 : PyCharm 7 | 8 | 9 | import pathlib 10 | from enum import Enum 11 | 12 | import pandas as pd 13 | pd.set_option('expand_frame_repr', False) 14 | import os 15 | 16 | 17 | class Backtest(Enum): 18 | """ 19 | Direction of order/trade/position. backtest 20 | """ 21 | DNA = "DNA" # 遗传算法类型 22 | EX = "EX" #"穷举多进程算法" 23 | 24 | 25 | 26 | 27 | def to_csv_result(result,class_name,signal_name,symbol,exchange,tag,description,backtest): 28 | df1 = pd.DataFrame() 29 | df2 = pd.DataFrame() 30 | df_ = pd.DataFrame() 31 | 32 | for d in result: 33 | a = d[0:2] 34 | c = d[2] 35 | de = pd.DataFrame([a], columns={'参数': 0, class_name: 1}) 36 | dc = pd.DataFrame([c]) 37 | df1 = df1.append(de) 38 | df2 = df2.append(dc) 39 | 40 | df_ = pd.concat([df1, df2], axis=1) 41 | 42 | # 通过 异常来解决遗传算法和穷举算法分别保存 43 | df = df_ 44 | try: 45 | df = df.rename(columns={ 46 | 'start_date': "首个交易日", 47 | 'end_date': '最后交易日', 48 | 'total_days': '总交易日', 49 | 'profit_days': '盈利交易日', 50 | 'loss_days': '亏损交易日', 51 | 'capital': '起始资金', 52 | 'end_balance': '结束资金', 53 | 'max_drawdown': '最大回撤', 54 | 'max_ddpercent': '百分比最大回撤', 55 | 'max_drawdown_duration': '最长回撤天数', 56 | 'total_net_pnl': '总盈亏', 57 | 'daily_net_pnl': '日均盈亏', 58 | 'total_commission': '总手续费', 59 | 'daily_commission': '日均手续费', 60 | 'total_slippage': '总滑点', 61 | 'daily_slippage': '日均滑点', 62 | 'total_turnover': '总成交金额', 63 | 'daily_turnover': '日均成交金额', 64 | 'total_trade_count': '总成交笔数', 65 | 'daily_trade_count': '日均成交笔数', 66 | 'total_return': '总收益率', 67 | 'annual_return': '年化收益', 68 | 'daily_return': '日均收益率', 69 | 'return_std': '收益标准差', 70 | 'sharpe_ratio': 'Sharpe Ratio', 71 | 'return_drawdown_ratio': '收益回撤比' 72 | }) 73 | 74 | df = df[["参数", '总盈亏', 'Sharpe Ratio', '收益回撤比', '年化收益', '收益标准差', '百分比最大回撤', '最长回撤天数', '日均成交笔数', '日均滑点']] 75 | 76 | except KeyError: 77 | df = df_ 78 | # 回测结果保存文件名 79 | file_name = '_'.join([ 80 | str(signal_name), 81 | str(symbol), 82 | str(exchange), 83 | str(class_name), 84 | str(tag), 85 | str(backtest), 86 | '.csv']) 87 | 88 | # 回测结果保存路径 89 | _ = os.path.abspath(os.path.dirname(__file__)) # 返回当前文件路径 90 | root_path = os.path.abspath(os.path.join(_, "data")) # 返回根目录文件夹 91 | result_path = root_path + "\\" + file_name 92 | 93 | # 可能因打开文件,忘记关闭。跑完算法因无法保存文件而浪费算力 94 | try: 95 | pd.DataFrame(columns=[description]).to_csv(result_path, index=False) 96 | df.to_csv(result_path, index=False, mode='a') 97 | print(f"保存成功,位置为:{root_path},文件名为:{file_name}") 98 | except IOError: 99 | print(f"同名文件被打开,以副本形式保存") 100 | file_name = '_'.join([ 101 | str(signal_name), 102 | str(symbol), 103 | str(exchange), 104 | str(class_name), 105 | str(tag), 106 | str("副本"), 107 | '.csv']) 108 | result_path = root_path + "\\" + file_name 109 | pd.DataFrame(columns=[description]).to_csv(result_path, index=False) 110 | df.to_csv(result_path, index=False, mode='a') 111 | print(f"保存成功,位置为:{root_path},文件被打开,文件以副本方式保存,文件名为:{file_name}") 112 | 113 | 114 | # 115 | # 116 | # def to_csv(result,signal_name,symbol,open_window,tag,description): 117 | # df1 = pd.DataFrame() 118 | # df2 = pd.DataFrame() 119 | # df = pd.DataFrame() 120 | # for d in result: 121 | # print(d) 122 | # a = d[0:2] 123 | # c = d[2] 124 | # de = pd.DataFrame([a], columns={'参数': 0, 'total_net_pnl_new': 1}) 125 | # dc = pd.DataFrame([c]) 126 | # df1 = df1.append(de) 127 | # df2 = df2.append(dc) 128 | # 129 | # df = pd.concat([df1, df2], axis=1) 130 | # df = df.rename(columns={ 131 | # 'start_date': "首个交易日", 132 | # 'end_date': '最后交易日', 133 | # 'total_days': '总交易日', 134 | # 'profit_days': '盈利交易日', 135 | # 'loss_days': '亏损交易日', 136 | # 'capital': '起始资金', 137 | # 'end_balance': '结束资金', 138 | # 'max_drawdown': '最大回撤', 139 | # 'max_ddpercent': '百分比最大回撤', 140 | # 'max_drawdown_duration': '最长回撤天数', 141 | # 'total_net_pnl': '总盈亏', 142 | # 'daily_net_pnl': '日均盈亏', 143 | # 'total_commission': '总手续费', 144 | # 'daily_commission': '日均手续费', 145 | # 'total_slippage': '总滑点', 146 | # 'daily_slippage': '日均滑点', 147 | # 'total_turnover': '总成交金额', 148 | # 'daily_turnover': '日均成交金额', 149 | # 'total_trade_count': '总成交笔数', 150 | # 'daily_trade_count': '日均成交笔数', 151 | # 'total_return': '总收益率', 152 | # 'annual_return': '年化收益', 153 | # 'daily_return': '日均收益率', 154 | # 'return_std': '收益标准差', 155 | # 'sharpe_ratio': 'Sharpe Ratio', 156 | # 'return_drawdown_ratio': '收益回撤比' 157 | # }) 158 | # 159 | # df = df[["参数", '总盈亏', 'Sharpe Ratio', '收益回撤比', '年化收益', '收益标准差', '百分比最大回撤', '最长回撤天数', '日均成交笔数', '日均滑点']] 160 | # print(df) 161 | # 162 | # # ===存储参数数据 163 | # p = os.path.join(root_path, 164 | # '%s-%s-%s-%s.csv' % (signal_name, symbol,open_window, tag)) 165 | # pd.DataFrame(columns=[description]).to_csv(p, index=False) 166 | # df.to_csv(p, index=False, mode='a') 167 | # 168 | -------------------------------------------------------------------------------- /backtesing_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#%%\n", 10 | "from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting\n", 11 | "from vnpy.app.cta_strategy.strategies.(策略名) import (\n", 12 | " 策略类,\n", 13 | ")#将策略放置到vnpy官方文件夹内的cta_strategy才可以调用该策略,不是放在本地用户文件夹内\n", 14 | "from datetime import datetime" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "#%%\n", 24 | "engine = BacktestingEngine()\n", 25 | "engine.set_parameters(\n", 26 | " vt_symbol=\"XBTUSD.BITMEX\",\n", 27 | " interval=\"1m\",\n", 28 | " start=datetime(2016, 12, 1),\n", 29 | " end=datetime(2019, 4, 30),\n", 30 | " rate=3.5/10000,\n", 31 | " slippage=0.5,\n", 32 | " size=1,\n", 33 | " pricetick=0.5,\n", 34 | " capital=100000,\n", 35 | " inverse=False #正反向合约\n", 36 | ")\n", 37 | "engine.add_strategy(策略类, {})" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "#%%\n", 47 | "engine.load_data()\n", 48 | "engine.run_backtesting()\n", 49 | "df = engine.calculate_result()\n", 50 | "engine.calculate_statistics()\n", 51 | "engine.show_chart()" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "setting = OptimizationSetting()\n", 61 | "#\"sharpe_ratio , total_return , return_drawdown_ratio\" #自己需要自由度跑的结果在回测引擎的cta_strategy的back_testing.py文件里查找\n", 62 | "setting.set_target(\"total_return\")\n", 63 | "setting.add_parameter(\"\", 14, 18, 1)\n", 64 | "setting.add_parameter(\"\", 8, 10, 1)\n", 65 | "setting.add_parameter(\"\", 12, 14, 1)\n", 66 | "setting.add_parameter(\"\", 16, 32, 2)\n", 67 | "setting.add_parameter(\"\", 7, 11, 1)\n", 68 | "setting.add_parameter(\"\", 1, 3, 1)\n", 69 | "setting.add_parameter(\"atr_window\", 1, 3, 1)\n", 70 | "#setting.add_parameter(\"risk_level\", 0.2, 0.2, 0.1)\n", 71 | "setting.add_parameter(\"trailing_tax\", 0.7, 2.5, 0.1)\n", 72 | "\n", 73 | "\n", 74 | "engine.run_ga_optimization(setting)#遗传算法\n", 75 | "engine.run_optimization(setting)#多进程算法\n" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [] 112 | } 113 | ], 114 | "metadata": { 115 | "kernelspec": { 116 | "display_name": "Python 3", 117 | "language": "python", 118 | "name": "python3" 119 | }, 120 | "language_info": { 121 | "codemirror_mode": { 122 | "name": "ipython", 123 | "version": 3 124 | }, 125 | "file_extension": ".py", 126 | "mimetype": "text/x-python", 127 | "name": "python", 128 | "nbconvert_exporter": "python", 129 | "pygments_lexer": "ipython3", 130 | "version": "3.7.1" 131 | } 132 | }, 133 | "nbformat": 4, 134 | "nbformat_minor": 4 135 | } 136 | -------------------------------------------------------------------------------- /boll_control_dc_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/17 21:49 5 | #文件名称 :boll_control_dc_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | from vnpy.app.cta_strategy import ( 9 | CtaTemplate, 10 | StopOrder, 11 | TickData, 12 | BarData, 13 | TradeData, 14 | OrderData, 15 | BarGenerator, 16 | ArrayManager, 17 | ) 18 | from vnpy.trader.object import Direction 19 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 20 | 21 | class Boll_Control_Dcs_trategy(CtaTemplate): 22 | """""" 23 | author = "yunya" 24 | 25 | open_window = 36 26 | boll_length = 24 27 | prop = 1.8 28 | atr_window = 30 29 | sl_multiplier = 0.2 30 | dc_length = 20 31 | fixed_size = 1 32 | 33 | entry_crossover = 0 34 | atr_value = 0 35 | intra_trade_high = 0 36 | intra_trade_low = 0 37 | long_stop_trade = 0 38 | short_stop_trade = 0 39 | long_stop = 0 40 | short_stop = 0 41 | exit_short = 0 42 | exit_long = 0 43 | entry_ema = 0 44 | 45 | 46 | parameters = [ 47 | "open_window", 48 | "boll_length", 49 | "dc_length", 50 | "sl_multiplier", 51 | "prop", 52 | "fixed_size", 53 | ] 54 | 55 | variables = [ 56 | "entry_crossover", 57 | "long_stop", 58 | "short_stop" 59 | ] 60 | 61 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 62 | """""" 63 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 64 | 65 | self.bg = NewBarGenerator(self.on_bar, self.open_window, self.on_xmin_bar) 66 | self.am = ArrayManager(int(self.boll_length) + 100) 67 | 68 | def on_init(self): 69 | """ 70 | Callback when strategy is inited. 71 | """ 72 | self.write_log("策略初始化") 73 | self.load_bar(10) 74 | 75 | def on_start(self): 76 | """ 77 | Callback when strategy is started. 78 | """ 79 | self.write_log("策略启动") 80 | self.put_event() 81 | 82 | def on_stop(self): 83 | """ 84 | Callback when strategy is stopped. 85 | """ 86 | self.write_log("策略停止") 87 | self.put_event() 88 | 89 | def on_tick(self, tick: TickData): 90 | """ 91 | Callback of new tick data update. 92 | """ 93 | self.bg.update_tick(tick) 94 | 95 | def on_bar(self, bar: BarData): 96 | """ 97 | Callback of new bar data update. 98 | """ 99 | self.bg.update_bar(bar) 100 | 101 | def on_xmin_bar(self, bar: BarData): 102 | """""" 103 | self.cancel_all() 104 | 105 | am = self.am 106 | am.update_bar(bar) 107 | if not am.inited: 108 | return 109 | 110 | # Calculate array 计算数组 111 | self.sma_array = am.sma(self.boll_length, True) 112 | std_array = am.sma(self.boll_length, True) 113 | dev = abs(self.am.close[:-1] - self.sma_array[:-1]) / std_array[:-1] 114 | dev_max = dev[-self.boll_length:].max() 115 | self.boll_up_array = self.sma_array + std_array * dev_max 116 | self.boll_down_array = self.sma_array - std_array * dev_max 117 | 118 | # Get current and last index 119 | current_sma = self.sma_array[-1] 120 | last_sma = self.sma_array[-2] 121 | last_close = self.am.close[-2] 122 | currnet_boll_up = self.boll_up_array[-1] 123 | last_boll_up = self.boll_up_array[-2] 124 | current_boll_down = self.boll_down_array[-1] 125 | last_boll_down = self.boll_down_array[-2] 126 | up_limit = current_sma * (1 + self.prop / 100) 127 | down_limit = current_sma * (1 - self.prop / 100) 128 | 129 | boll_width = currnet_boll_up - current_boll_down 130 | 131 | # Get crossover 132 | if ( 133 | last_close <= last_boll_up 134 | and bar.close_price > currnet_boll_up 135 | and bar.close_price < up_limit 136 | ): 137 | self.entry_crossover = 1 138 | elif ( 139 | last_close >= last_boll_down 140 | and bar.close_price < current_boll_down 141 | and bar.close_price > down_limit 142 | ): 143 | self.entry_crossover = -1 144 | 145 | if(last_close <=last_sma 146 | and bar.close_price > current_sma): 147 | self.entry_ema = -1 148 | elif (last_close >= last_sma 149 | and bar.close_price < current_sma): 150 | self.entry_ema = 1 151 | else: 152 | self.entry_ema = 0 153 | 154 | self.atr_value = am.atr(self.atr_window) 155 | self.exit_short, self.exit_long = self.am.donchian(self.dc_length) 156 | 157 | if not self.pos: 158 | self.intra_trade_high = bar.high_price 159 | self.intra_trade_low = bar.low_price 160 | 161 | if self.entry_crossover > 0: 162 | self.buy(up_limit, self.fixed_size, True) 163 | 164 | elif self.entry_crossover < 0: 165 | self.short(down_limit, self.fixed_size, True) 166 | 167 | elif self.pos > 0: 168 | if self.entry_ema > 0: 169 | self.sell((bar.close_price - 5), abs(self.pos)) 170 | 171 | # 最高价回撤比例、固定止损、唐安奇下轨中的最大值为止损位 172 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 173 | self.intra_trade_low = bar.low_price 174 | 175 | long_stop_high = self.intra_trade_high - boll_width * self.sl_multiplier 176 | long_high_trade = max(long_stop_high,self.long_stop_trade) 177 | self.long_stop = max(self.exit_long,long_high_trade) 178 | 179 | self.sell(self.long_stop, abs(self.pos), True) 180 | 181 | elif self.pos < 0: 182 | if self.entry_ema < 0: 183 | self.cover((bar.close_price + 5), abs(self.pos)) 184 | else: 185 | # 最低价回撤比例、固定止损、唐安奇上轨中的最小值为止损位 186 | self.intra_trade_high = bar.high_price 187 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 188 | 189 | short_stop_low = self.intra_trade_low + boll_width * self.sl_multiplier 190 | short_low_trade = min(short_stop_low,self.short_stop_trade) 191 | self.short_stop = min(short_low_trade,self.exit_short) 192 | 193 | self.cover(self.short_stop, abs(self.pos), True) 194 | 195 | self.put_event() 196 | 197 | def on_order(self, order: OrderData): 198 | """ 199 | Callback of new order data update. 200 | """ 201 | 202 | self.put_event() 203 | pass 204 | 205 | def on_trade(self, trade: TradeData): 206 | """ 207 | Callback of new trade data update. 208 | """ 209 | if trade.direction == Direction.LONG: 210 | self.long_entry = trade.price # 成交最高价 211 | self.long_stop_trade = self.long_entry - 2 * self.atr_value 212 | else: 213 | self.short_entry = trade.price 214 | self.short_stop_trade = self.short_entry + 2 * self.atr_value 215 | 216 | self.sync_data() 217 | self.put_event() 218 | 219 | def on_stop_order(self, stop_order: StopOrder): 220 | """ 221 | Callback of stop order update. 222 | """ 223 | self.put_event() 224 | pass 225 | 226 | 227 | -------------------------------------------------------------------------------- /boll_kc_dc_combination_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | # 开发团队 :yunya 3 | # 开发人员 :Administrator 4 | # 开发时间 : 2020/6/11 19:11 5 | # 文件名称 :boll_kc_dc_combination _strategy.py 6 | # 开发工具 : PyCharm 7 | 8 | 9 | import talib 10 | 11 | from vnpy.app.cta_strategy import ( 12 | CtaTemplate, 13 | StopOrder, 14 | Direction, 15 | TickData, 16 | BarData, 17 | TradeData, 18 | OrderData, 19 | BarGenerator, 20 | ArrayManager, 21 | ) 22 | from vnpy.trader.constant import Interval 23 | 24 | 25 | class Boll_Kc_Dc_CombinationStrategy(CtaTemplate): 26 | """""" 27 | author = "yunya" 28 | open_window = 2 29 | xsmall_window = 15 30 | xbig_window = 15 31 | com_length = 250 32 | boll_dev = 2.0 33 | kk_dev = 2.0 34 | trading_size = 1 35 | 36 | xsmall_up_min = 0 37 | xsmall_down_min = 0 38 | xsmall_up_max = 0 39 | xsmall_down_max = 0 40 | xsmall_ema_mid = 0 41 | xsmall_com_width = 0 42 | 43 | xbig_up_min = 0 44 | xbig_down_min = 0 45 | xbig_up_max = 0 46 | xbig_down_max = 0 47 | xbig_ema_mid = 0 48 | xbig_com_width = 0 49 | 50 | long_entry = 0 51 | short_entry = 0 52 | long_stop = 0 53 | short_stop = 0 54 | exit_up = 0 55 | exit_down = 0 56 | 57 | # entry_window = 28 58 | # exit_window = 7 59 | # atr_window = 4 60 | # risk_level = 0.2 61 | # 62 | # trading_size = 0 63 | # entry_up = 0 64 | # entry_down = 0 65 | # exit_up = 0 66 | # exit_down = 0 67 | # atr_value = 0 68 | # 69 | 70 | 71 | parameters = ["entry_window", "exit_window", "atr_window", "risk_level"] 72 | variables = [ 73 | "entry_up", "entry_down", "exit_up", 74 | "exit_down", "trading_size", "atr_value" 75 | ] 76 | 77 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 78 | """""" 79 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 80 | 81 | self.bg_xsmall = BarGenerator( 82 | on_bar=self.on_bar, 83 | window=self.xsmall_window, 84 | on_window_bar=self.on_xsmall_bar, 85 | interval=Interval.MINUTE 86 | ) 87 | self.am_xsmall = ArrayManager(self.com_length + 10) 88 | self.bg_xbig = BarGenerator( 89 | on_bar=self.on_bar, 90 | window=self.xbig_window, 91 | on_window_bar=self.on_xbig_bar, 92 | interval=Interval.MINUTE 93 | ) 94 | self.am_xbig = ArrayManager(self.com_length + 10) 95 | 96 | self.bg = BarGenerator(self.on_bar, self.open_window, self.on_open_bar) 97 | self.am = ArrayManager() 98 | 99 | def on_init(self): 100 | """ 101 | Callback when strategy is inited. 102 | """ 103 | self.write_log("策略初始化") 104 | self.load_bar(10) 105 | 106 | def on_start(self): 107 | """ 108 | Callback when strategy is started. 109 | """ 110 | self.write_log("策略启动") 111 | 112 | def on_stop(self): 113 | """ 114 | Callback when strategy is stopped. 115 | """ 116 | self.write_log("策略停止") 117 | 118 | def on_tick(self, tick: TickData): 119 | """ 120 | Callback of new tick data update. 121 | """ 122 | self.bg_xsmall.update_tick(tick) 123 | 124 | def on_bar(self, bar: BarData): 125 | """ 126 | Callback of new bar data update. 127 | """ 128 | self.bg.update_bar(bar) 129 | self.bg_xsmall.update_bar(bar) 130 | self.bg_xbig.update_bar(bar) 131 | 132 | def on_open_bar(self,bar:BarData): 133 | """ 134 | :param bar: 135 | :return: 136 | """ 137 | # 先使用挂单全撤的粗化订单管理 138 | self.cancel_all() 139 | 140 | self.am.update_bar(bar) 141 | if not self.am_xsmall.inited or not self.am_xbig.inited or not self.am.inited: 142 | return 143 | 144 | if self.pos == 0: 145 | # 如果价格突破 xsmall_up_min 线时,在xsmall_up_max 价格挂停止单 146 | if self.am_xsmall.close[-1] >= self.xsmall_up_min and self.am.close[-1] >=self.xsmall_up_min: 147 | self.buy(self.xsmall_up_max,self.trading_size,True) 148 | 149 | # 如果价格突破 xsmall_down_min 线时,在xsmall_down_max 价格挂停止单 150 | elif self.am_xsmall.close[-1] <= self.xsmall_down_min and self.am.close[-1] <= self.xsmall_down_min: 151 | self.short(self.xsmall_down_max,self.trading_size,True) 152 | 153 | elif self.pos > 0: 154 | # 成交价固定止损位与中轨中最大值为当前止损位 155 | self.exit_up = max(self.xsmall_ema_mid,self.long_stop) 156 | self.sell(self.exit_up,abs(self.pos),True) 157 | 158 | elif self.pos < 0: 159 | # 成交价固定止损位与中轨中最小值为当前止损位 160 | self.exit_down = min(self.xsmall_ema_mid,self.short_stop) 161 | self.cover(self.exit_down,abs(self.pos),True) 162 | 163 | 164 | self.sync_data() 165 | self.put_event() 166 | 167 | def on_xsmall_bar(self, bar: BarData): 168 | """ 169 | :param bar: 170 | :return: 171 | """ 172 | # x分钟 多策略合合成的通道线 173 | self.am_xsmall.update_bar(bar) 174 | if not self.am_xsmall.inited or not self.am_xbig.inited: 175 | return 176 | 177 | self.xsmall_ema_mid,self.xsmall_com_width,self.xsmall_up_min, self.xsmall_down_min,\ 178 | self.xsmall_up_max, self.xsmall_down_max = self.boll_kc_dc_combination( 179 | high=self.am_xsmall.high[:-1], 180 | low=self.am_xsmall.low[:-1], 181 | close=self.am_xsmall.close[:-1], 182 | boll_dev=self.boll_dev, 183 | kk_dev=self.kk_dev, 184 | com_length=self.com_length 185 | ) 186 | 187 | print(f"xsmall: mid:{self.xsmall_ema_mid},width:{self.xsmall_com_width},upmin:{self.xsmall_up_min},\ 188 | downmin:{self.xsmall_down_min},upmax:{self.xsmall_up_max},downmax:{self.xsmall_down_max}" + "\n") 189 | 190 | self.atr_value = self.am_xsmall.atr(self.com_length) 191 | 192 | self.sync_data() 193 | self.put_event() 194 | 195 | def on_xbig_bar(self,bar:BarData): 196 | """ 197 | :param bar: 198 | :return: 199 | """ 200 | # x分钟 多策略合合成的通道线 201 | self.am_xbig.update_bar(bar) 202 | if not self.am_xbig.inited: 203 | return 204 | 205 | self.xbig_ema_mid,self.xbig_com_width,self.xbig_up_min,self.xbig_down_min,\ 206 | self.xbig_up_max,self.xbig_down_max = self.boll_kc_dc_combination( 207 | high=self.am_xbig.high[:-1], 208 | close=self.am_xbig.close[:-1], 209 | low=self.am_xbig.low[:-1], 210 | boll_dev=self.boll_dev, 211 | kk_dev=self.kk_dev, 212 | com_length=self.com_length 213 | ) 214 | print(f"xbig:mid:{self.xbig_ema_mid},width:{self.xbig_com_width},upmin:{self.xbig_up_min},\ 215 | downmin:{self.xbig_down_min},upmax:{self.xbig_up_max},downmax:{self.xbig_down_max}" + "\n") 216 | 217 | self.sync_data() 218 | self.put_event() 219 | 220 | def on_order(self, order: OrderData): 221 | """ 222 | Callback of new order data update. 223 | """ 224 | pass 225 | def on_trade(self, trade: TradeData): 226 | """ 227 | Callback of new trade data update. 228 | """ 229 | if trade.direction == Direction.LONG: 230 | self.long_entry = trade.price # 成交最高价 231 | self.long_stop = self.long_entry - 2 * self.atr_value 232 | else: 233 | self.short_entry = trade.price 234 | self.short_stop = self.short_entry + 2 * self.atr_value 235 | 236 | self.sync_data() 237 | 238 | def on_stop_order(self, stop_order: StopOrder): 239 | """ 240 | Callback of stop order update. 241 | """ 242 | pass 243 | 244 | def boll_kc_dc_combination(self, high, close, low, boll_dev, kk_dev, com_length): 245 | 246 | # 计算组合均线 247 | ema_com = talib.EMA(close, com_length) 248 | 249 | # 计算布林带 250 | boll_std = talib.STDDEV(close, com_length) 251 | boll_up = ema_com + boll_dev * boll_std 252 | boll_down = ema_com - boll_dev * boll_std 253 | 254 | # 计算肯特通道 255 | kc_atr = talib.ATR(high, low, close, com_length) 256 | kc_up = ema_com + kc_atr * kk_dev 257 | kc_dowm = ema_com - kc_atr * kk_dev 258 | 259 | # 计算唐安奇通道 260 | dc_up = talib.MAX(high, com_length) 261 | dc_down = talib.MIN(low, com_length) 262 | 263 | # 计算轨道 因kc通道是直接,最小值大概率是直接,所以去除 264 | pass_up_min = min(dc_up[-1], boll_up[-1]) 265 | pass_down_min = max(dc_down[-1], boll_down[-1]) 266 | 267 | pass_up_max = max(kc_up[-1], dc_up[-1], boll_up[-1]) 268 | pass_down_max = min(kc_dowm[-1], dc_down[-1], boll_down[-1]) 269 | ema_mid = ema_com[-1] 270 | 271 | com_width = abs(pass_up_max - pass_down_max) 272 | 273 | return ema_mid, com_width, pass_up_min, pass_down_min, pass_up_max, pass_down_max 274 | -------------------------------------------------------------------------------- /boll_kc_dc_simple_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | # 开发团队 :yunya 3 | # 开发人员 :Administrator 4 | # 开发时间 : 2020/6/11 19:11 5 | # 文件名称 :boll_kc_dc_combination _strategy.py 6 | # 开发工具 : PyCharm 7 | 8 | 9 | import talib 10 | 11 | from vnpy.app.cta_strategy import ( 12 | CtaTemplate, 13 | StopOrder, 14 | Direction, 15 | TickData, 16 | BarData, 17 | TradeData, 18 | OrderData, 19 | BarGenerator, 20 | ArrayManager, 21 | ) 22 | from vnpy.trader.constant import Interval 23 | 24 | 25 | class Boll_Kc_Dc_Reverse_Strategy(CtaTemplate): 26 | """ 27 | 本策略为反向策略,币本位 Reverse 反向 28 | """ 29 | 30 | author = "yunya" 31 | open_window = 2 32 | xsmall_window = 15 33 | com_length = 250 34 | boll_kk_dev = 2.0 35 | kk_atr_length = 30 36 | sl_multiplier = 0.5 37 | risk_level = 10000 38 | 39 | trading_size = 0 40 | xsmall_up_min = 0 41 | xsmall_down_min = 0 42 | xsmall_up_max = 0 43 | xsmall_down_max = 0 44 | xsmall_ema_mid = 0 45 | xsmall_com_width = 0 46 | long_entry = 0 47 | short_entry = 0 48 | long_stop = 0 49 | short_stop = 0 50 | exit_up = 0 51 | exit_down = 0 52 | atr_value = 0 53 | 54 | intra_trade_high = 0 55 | intra_trade_low = 0 56 | 57 | parameters = [ 58 | "open_window", 59 | "xsmall_window", 60 | "com_length", 61 | "boll_kk_dev", 62 | "kk_atr_length", 63 | "sl_multiplier", 64 | "risk_level", 65 | ] 66 | variables = [ 67 | "trading_size", 68 | "xsmall_up_min", 69 | "xsmall_down_min", 70 | "xsmall_up_max", 71 | "xsmall_down_max", 72 | "xsmall_ema_mid", 73 | "xsmall_com_width", 74 | "long_entry", 75 | "short_entry", 76 | "long_stop", 77 | "short_stop", 78 | "exit_up", 79 | "exit_down", 80 | "atr_value", 81 | ] 82 | 83 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 84 | """""" 85 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 86 | 87 | self.bg_xsmall = BarGenerator( 88 | on_bar=self.on_bar, 89 | window=self.xsmall_window, 90 | on_window_bar=self.on_xsmall_bar, 91 | interval=Interval.MINUTE 92 | ) 93 | self.am_xsmall = ArrayManager(self.com_length + 10) 94 | self.bg = BarGenerator(self.on_bar, self.open_window, self.on_open_bar) 95 | self.am = ArrayManager() 96 | 97 | def on_init(self): 98 | """ 99 | Callback when strategy is inited. 100 | """ 101 | self.write_log("策略初始化") 102 | self.load_bar(10) 103 | 104 | def on_start(self): 105 | """ 106 | Callback when strategy is started. 107 | """ 108 | self.write_log("策略启动") 109 | 110 | def on_stop(self): 111 | """ 112 | Callback when strategy is stopped. 113 | """ 114 | self.write_log("策略停止") 115 | 116 | def on_tick(self, tick: TickData): 117 | """ 118 | Callback of new tick data update. 119 | """ 120 | self.bg_xsmall.update_tick(tick) 121 | 122 | def on_bar(self, bar: BarData): 123 | """ 124 | Callback of new bar data update. 125 | """ 126 | self.bg.update_bar(bar) 127 | self.bg_xsmall.update_bar(bar) 128 | 129 | def on_open_bar(self,bar:BarData): 130 | """ 131 | :param bar: 132 | :return: 133 | """ 134 | # 先使用挂单全撤的粗化订单管理 135 | self.cancel_all() 136 | 137 | self.am.update_bar(bar) 138 | if not self.am_xsmall.inited or not self.am.inited: 139 | return 140 | 141 | if self.pos == 0: 142 | # 根据布林带宽度动态调整仓位大小 143 | self.trading_size = max(int(self.risk_level / self.xsmall_com_width), 1) 144 | self.intra_trade_high = bar.high_price 145 | self.intra_trade_low = bar.low_price 146 | 147 | # 如果价格突破 xsmall_up_min 线时,在xsmall_up_max 价格挂停止单 148 | if self.am_xsmall.close[-1] >= self.xsmall_up_min and self.am.close[-1] >=self.xsmall_up_min: 149 | self.buy(self.xsmall_up_max,self.trading_size,True) 150 | 151 | # 如果价格突破 xsmall_down_min 线时,在xsmall_down_max 价格挂停止单 152 | elif self.am_xsmall.close[-1] <= self.xsmall_down_min and self.am.close[-1] <= self.xsmall_down_min: 153 | self.short(self.xsmall_down_max,self.trading_size,True) 154 | 155 | elif self.pos > 0: 156 | # 成交价固定止损位与中轨中最大值为当前止损位 157 | # self.exit_up = max(self.xsmall_ema_mid,self.long_stop) 158 | 159 | # 成交价回定止损 与最高价回撤一定比例通道宽度值 160 | self.intra_trade_high = max(self.intra_trade_high,bar.high_price) 161 | self.intra_trade_low = bar.low_price 162 | 163 | exit_long_stop = self.intra_trade_high - self.xsmall_com_width * self.sl_multiplier 164 | self.exit_up = max(exit_long_stop,self.long_stop) 165 | self.sell(self.exit_up,abs(self.pos),True) 166 | 167 | elif self.pos < 0: 168 | # 成交价固定止损位与中轨中最小值为当前止损位 169 | # self.exit_down = min(self.xsmall_ema_mid,self.short_stop) 170 | 171 | # 成交价回定止损 与最高价回撤一定比例通道宽度值 172 | self.intra_trade_high = bar.high_price 173 | self.intra_trade_low = min(self.intra_trade_low,bar.low_price) 174 | 175 | exit_short_stop = self.intra_trade_low - self.xsmall_com_width * self.sl_multiplier 176 | self.exit_down = min(exit_short_stop,self.short_stop) 177 | self.cover(self.exit_down,abs(self.pos),True) 178 | 179 | self.sync_data() 180 | self.put_event() 181 | 182 | def on_xsmall_bar(self, bar: BarData): 183 | """ 184 | :param bar: 185 | :return: 186 | """ 187 | # x分钟 多策略合合成的通道线 188 | self.am_xsmall.update_bar(bar) 189 | if not self.am_xsmall.inited : 190 | return 191 | 192 | self.xsmall_ema_mid,self.xsmall_com_width,self.xsmall_up_min, self.xsmall_down_min,\ 193 | self.xsmall_up_max, self.xsmall_down_max = self.boll_kc_dc_combination( 194 | high=self.am_xsmall.high[:-1], 195 | low=self.am_xsmall.low[:-1], 196 | close=self.am_xsmall.close[:-1], 197 | boll_kk_dev=self.boll_kk_dev, 198 | kk_atr_length=self.kk_atr_length, 199 | com_length=self.com_length 200 | ) 201 | 202 | # print(f"xsmall: mid:{self.xsmall_ema_mid},width:{self.xsmall_com_width},upmin:{self.xsmall_up_min},\ 203 | # downmin:{self.xsmall_down_min},upmax:{self.xsmall_up_max},downmax:{self.xsmall_down_max}" + "\n") 204 | 205 | self.atr_value = self.am_xsmall.atr(self.kk_atr_length) 206 | 207 | self.sync_data() 208 | self.put_event() 209 | 210 | 211 | def on_order(self, order: OrderData): 212 | """ 213 | Callback of new order data update. 214 | """ 215 | pass 216 | def on_trade(self, trade: TradeData): 217 | """ 218 | Callback of new trade data update. 219 | """ 220 | if trade.direction == Direction.LONG: 221 | self.long_entry = trade.price # 成交最高价 222 | self.long_stop = self.long_entry - 2 * self.atr_value 223 | else: 224 | self.short_entry = trade.price 225 | self.short_stop = self.short_entry + 2 * self.atr_value 226 | 227 | self.sync_data() 228 | 229 | def on_stop_order(self, stop_order: StopOrder): 230 | """ 231 | Callback of stop order update. 232 | """ 233 | pass 234 | 235 | def boll_kc_dc_combination(self, high, close, low, boll_kk_dev,kk_atr_length,com_length): 236 | 237 | # 计算组合均线 238 | ema_com = talib.EMA(close, com_length) 239 | 240 | # 计算布林带 241 | boll_std = talib.STDDEV(close, com_length) 242 | boll_up = ema_com + boll_kk_dev * boll_std 243 | boll_down = ema_com - boll_kk_dev * boll_std 244 | 245 | # 计算肯特通道 246 | kc_atr = talib.ATR(high, low, close, kk_atr_length) 247 | kc_up = ema_com + kc_atr * boll_kk_dev 248 | kc_dowm = ema_com - kc_atr * boll_kk_dev 249 | 250 | # 计算唐安奇通道 251 | dc_up = talib.MAX(high, com_length) 252 | dc_down = talib.MIN(low, com_length) 253 | 254 | # 计算轨道 因kc通道是直接,最小值大概率是直接,所以去除 255 | pass_up_min = min(dc_up[-1], boll_up[-1]) 256 | pass_down_min = max(dc_down[-1], boll_down[-1]) 257 | 258 | pass_up_max = max(kc_up[-1], dc_up[-1], boll_up[-1]) 259 | pass_down_max = min(kc_dowm[-1], dc_down[-1], boll_down[-1]) 260 | ema_mid = ema_com[-1] 261 | 262 | com_width = abs(pass_up_max - pass_down_max) 263 | 264 | return ema_mid, com_width, pass_up_min, pass_down_min, pass_up_max, pass_down_max 265 | -------------------------------------------------------------------------------- /boll_kk_vix_simple_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | # 开发团队 :yunya 3 | # 开发人员 :Administrator 4 | # 开发时间 : 2020/6/14 14:11 5 | # 文件名称 :boll_kc_dc_combination _strategy.py 6 | # 开发工具 : PyCharm 7 | 8 | 9 | import talib 10 | 11 | from vnpy.app.cta_strategy import ( 12 | CtaTemplate, 13 | StopOrder, 14 | Direction, 15 | TickData, 16 | BarData, 17 | TradeData, 18 | OrderData, 19 | BarGenerator, 20 | ArrayManager, 21 | ) 22 | from vnpy.trader.constant import Interval 23 | import numpy as np 24 | 25 | 26 | class Boll_kk_vix_simple_Strategy(CtaTemplate): 27 | """ 28 | 本策略为反向策略,币本位 Reverse 反向 29 | """ 30 | 31 | author = "yunya" 32 | 33 | open_window = 5 34 | xminute_window = 15 35 | com_length = 450 36 | exit_dc_length = 10 37 | fast_sma_length = 45 38 | slow_sma_length = 110 39 | cci_length = 30 40 | cci_exit = 26 41 | sl_multiplier = 8.0 42 | fixed_size = 1 43 | 44 | 45 | bollkk_ema = 0 46 | bollkk_up = 0 47 | bollkk_down = 0 48 | bollkk_width = 0 49 | cci_vlue = 0 50 | long_stop = 0 51 | short_stop = 0 52 | exit_up = 0 53 | exit_down = 0 54 | atr_value = 0 55 | long_entry = 0 56 | short_entry = 0 57 | ma_trend = 0 58 | exit_dc_long =0 59 | exit_dc_short = 0 60 | intra_trade_high = 0 61 | intra_trade_low = 0 62 | 63 | 64 | 65 | parameters = [ 66 | "open_window", 67 | "xminute_window", 68 | "com_length", 69 | "fast_sma_length", 70 | "slow_sma_length", 71 | "cci_length", 72 | "cci_exit", 73 | "exit_dc_length", 74 | "sl_multiplier", 75 | "fixed_size", 76 | ] 77 | 78 | variables = [ 79 | "bollkk_ema", 80 | "bollkk_up", 81 | "bollkk_down", 82 | "bollkk_width", 83 | "cci_vlue", 84 | "long_stop", 85 | "short_stop", 86 | "exit_up", 87 | "exit_down", 88 | "atr_value", 89 | "long_entry", 90 | "short_entry", 91 | "ma_trend", 92 | ] 93 | 94 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 95 | """""" 96 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 97 | 98 | self.bg_xminute = BarGenerator( 99 | on_bar=self.on_bar, 100 | window=self.xminute_window, 101 | on_window_bar=self.on_xminute_bar, 102 | interval=Interval.MINUTE 103 | ) 104 | self.am_xminute = ArrayManager(self.com_length + 10) 105 | self.bg = BarGenerator(self.on_bar, self.open_window, self.on_open_bar) 106 | self.am = ArrayManager() 107 | 108 | def on_init(self): 109 | """ 110 | Callback when strategy is inited. 111 | """ 112 | self.write_log("策略初始化") 113 | self.load_bar(10) 114 | 115 | def on_start(self): 116 | """ 117 | Callback when strategy is started. 118 | """ 119 | self.write_log("策略启动") 120 | 121 | def on_stop(self): 122 | """ 123 | Callback when strategy is stopped. 124 | """ 125 | self.write_log("策略停止") 126 | 127 | def on_tick(self, tick: TickData): 128 | """ 129 | Callback of new tick data update. 130 | """ 131 | self.bg_xminute.update_tick(tick) 132 | 133 | def on_bar(self, bar: BarData): 134 | """ 135 | Callback of new bar data update. 136 | """ 137 | self.bg.update_bar(bar) 138 | self.bg_xminute.update_bar(bar) 139 | 140 | def on_open_bar(self, bar: BarData): 141 | """ 142 | :param bar: 143 | :return: 144 | """ 145 | # 先使用挂单全撤的粗化订单管理 146 | self.cancel_all() 147 | 148 | self.am.update_bar(bar) 149 | if not self.am_xminute.inited or not self.am.inited: 150 | return 151 | 152 | if self.pos == 0: 153 | # 根据布林带宽度动态调整仓位大小 154 | # self.trading_size = max(int(self.risk_level / self.xminute_com_width), 1) 155 | self.intra_trade_high = bar.high_price 156 | self.intra_trade_low = bar.low_price 157 | if self.cci_value > self.cci_exit and self.ma_trend > 0: 158 | self.buy(self.bollkk_up,self.fixed_size, True) 159 | 160 | elif self.cci_value < -self.cci_exit and self.ma_trend < 0: 161 | self.short(self.bollkk_down, self.fixed_size, True) 162 | 163 | elif self.pos > 0: 164 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 165 | self.intra_trade_low = bar.low_price 166 | 167 | exit_long_stop = self.intra_trade_high - self.bollkk_width * self.sl_multiplier 168 | exit_long_dc = max(exit_long_stop,self.exit_dc_long) 169 | self.exit_up = max(exit_long_dc, self.long_stop) 170 | self.sell(self.exit_up, abs(self.pos), True) 171 | 172 | elif self.pos < 0: 173 | self.intra_trade_high = bar.high_price 174 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 175 | 176 | exit_short_stop = self.intra_trade_low + self.bollkk_width * self.sl_multiplier 177 | exit_shout_dc = min(exit_short_stop,self.exit_dc_short) 178 | self.exit_down = min(exit_shout_dc, self.short_stop) 179 | self.cover(self.exit_down, abs(self.pos), True) 180 | 181 | self.put_event() 182 | self.sync_data() 183 | 184 | def on_xminute_bar(self, bar: BarData): 185 | """ 186 | :param bar: 187 | :return: 188 | """ 189 | # x分钟 多策略合合成的通道线 190 | self.am_xminute.update_bar(bar) 191 | 192 | if not self.am_xminute.inited: 193 | return 194 | 195 | bollkk_ema_value,self.bollkk_up,self.bollkk_down,= self.boll_kk_combination( 196 | high=self.am_xminute.high[:-1], 197 | close=self.am_xminute.close[:-1], 198 | low=self.am_xminute.low[:-1], 199 | com_length=self.com_length 200 | ) 201 | 202 | # 计算开平信号 203 | self.current_close = self.am_xminute.close[-1] 204 | self.last_close = self.am_xminute.close[-2] 205 | self.bollkk_ema = bollkk_ema_value[-1] 206 | 207 | self.bollkk_width = abs(self.bollkk_up - self.bollkk_down) 208 | 209 | self.cci_value = self.am_xminute.cci(self.cci_length) 210 | 211 | 212 | 213 | self.fast_ma = self.am_xminute.sma(self.fast_sma_length) 214 | self.slow_ma = self.am_xminute.sma(self.slow_sma_length) 215 | 216 | if self.fast_ma > self.slow_ma: 217 | self.ma_trend = 1 218 | else: 219 | self.ma_trend = -1 220 | self.atr_value = self.am_xminute.atr(30) 221 | self.exit_dc_short,self.exit_dc_long = self.am_xminute.donchian(self.exit_dc_length) 222 | self.sync_data() 223 | self.put_event() 224 | 225 | def on_order(self, order: OrderData): 226 | """ 227 | Callback of new order data update. 228 | """ 229 | pass 230 | 231 | def on_trade(self, trade: TradeData): 232 | """ 233 | Callback of new trade data update. 234 | """ 235 | if trade.direction == Direction.LONG: 236 | self.long_entry = trade.price # 成交最高价 237 | self.long_stop = self.long_entry - 2 * self.atr_value 238 | else: 239 | self.short_entry = trade.price 240 | self.short_stop = self.short_entry + 2 * self.atr_value 241 | 242 | self.sync_data() 243 | 244 | def on_stop_order(self, stop_order: StopOrder): 245 | """ 246 | Callback of stop order update. 247 | """ 248 | pass 249 | 250 | def boll_kk_combination(self,high,close,low,com_length): 251 | """ 252 | 通过计算收盘价与收盘价均线之间的倍数,来自动调整boll 、kk 的通过宽度 253 | """ 254 | 255 | # 计算组合均线 256 | bollkk_ema = talib.EMA(close, com_length) 257 | 258 | # 计算自适布林带 259 | boll_std = talib.STDDEV(close, com_length) 260 | boll_dev = abs(close - bollkk_ema) / boll_std 261 | 262 | boll_up = bollkk_ema + boll_dev * boll_std 263 | boll_down = bollkk_ema - boll_dev * boll_std 264 | 265 | # 计算自适肯特通道 266 | kk_atr = talib.ATR(high, low, close, com_length) 267 | kk_dev = abs(close - bollkk_ema) / kk_atr 268 | 269 | kk_up = bollkk_ema + kk_atr * kk_dev 270 | kk_down = bollkk_ema - kk_atr * kk_dev 271 | 272 | bollkk_up = max(boll_up[-1],kk_up[-1]) 273 | bollkk_down = min(boll_down[-1],kk_down[-1]) 274 | 275 | 276 | return bollkk_ema,bollkk_up,bollkk_down, 277 | -------------------------------------------------------------------------------- /dualthrust_kk_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/13 15:44 5 | #文件名称 :dualthrust_dc_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | 9 | from vnpy.app.cta_strategy import ( 10 | CtaTemplate, 11 | StopOrder, 12 | TickData, 13 | BarData, 14 | TradeData, 15 | OrderData, 16 | BarGenerator, 17 | ArrayManager, 18 | ) 19 | import talib 20 | from vnpy.trader.constant import Interval, Direction 21 | from vnpy.app.cta_strategy.new_strategy import NewBarGenerator 22 | class DudlThrustKkStrategy(CtaTemplate): 23 | """""" 24 | author = "yunyu" 25 | 26 | 27 | xminute_window = 1 28 | rolling_period = 70 29 | upper_open = 0.2 30 | lower_open = 0.2 31 | cci_window = 30 32 | keltner_window = 24 33 | keltner_dev = 1 34 | fixed_size = 1 35 | 36 | cci_value = 0 37 | exit_kk_up = 0 38 | exit_kk_down = 0 39 | dualthrust_up = 0 40 | dualthrust_down = 0 41 | 42 | ask = 0 43 | bid = 0 44 | 45 | parameters = [ 46 | 47 | "xminute_window", 48 | "rolling_period", 49 | "upper_open", 50 | "lower_open", 51 | "cci_window", 52 | "keltner_window", 53 | "keltner_dev", 54 | "fixed_size", 55 | ] 56 | 57 | variables = [ 58 | "dualthrust_up", 59 | "dualthrust_down", 60 | "cci_value", 61 | "exit_kk_up", 62 | "exit_kk_down", 63 | ] 64 | 65 | def __init__(self, cta_engine, strategy_name, vt_symbol, setting): 66 | """""" 67 | super().__init__(cta_engine, strategy_name, vt_symbol, setting) 68 | 69 | self.bg = NewBarGenerator(on_bar=self.on_bar,window=self.xminute_window,on_window_bar=self.on_min_bar,interval=Interval.MINUTE) 70 | self.am = ArrayManager() 71 | 72 | 73 | def on_init(self): 74 | """ 75 | Callback when strategy is inited. 76 | """ 77 | self.write_log("策略初始化") 78 | self.load_bar(10) 79 | 80 | def on_start(self): 81 | """ 82 | Callback when strategy is started. 83 | """ 84 | self.write_log("策略启动") 85 | 86 | def on_stop(self): 87 | """ 88 | Callback when strategy is stopped. 89 | """ 90 | self.write_log("策略停止") 91 | 92 | def on_tick(self, tick: TickData): 93 | """ 94 | Callback of new tick data update. 95 | """ 96 | self.bg.update_tick(tick) 97 | self.ask = tick.ask_price_1 # 卖一价 98 | self.bid = tick.bid_price_1 # 买一价 99 | 100 | 101 | 102 | def on_bar(self, bar: BarData): 103 | """ 104 | Callback of new bar data update. 105 | """ 106 | self.bg.update_bar(bar) 107 | 108 | 109 | def on_min_bar(self, bar: BarData): 110 | """ 111 | Callback of new bar data update. 112 | """ 113 | self.am.update_bar(bar) 114 | self.cancel_all() 115 | if not self.am.inited: 116 | return 117 | 118 | self.dualthrust_up, self.dualthrust_down = self.dualthrust( 119 | self.am.high, 120 | self.am.low, 121 | self.am.close, 122 | self.am.open, 123 | self.rolling_period, 124 | self.upper_open, 125 | self.lower_open 126 | ) 127 | self.cci_value = self.am.cci(self.cci_window) 128 | print(self.cci_value) 129 | self.keltner_up, self.keltner_down = self.am.keltner( 130 | self.keltner_window, self.keltner_dev) 131 | 132 | if self.pos == 0: 133 | if self.cci_value > 0: 134 | self.buy(self.dualthrust_up,self.fixed_size,True) 135 | 136 | elif self.cci_value < 0: 137 | self.short(self.dualthrust_down,self.fixed_size,True) 138 | 139 | elif self.pos > 0: 140 | self.sell(self.exit_kk_down,self.fixed_size,True) 141 | 142 | elif self.pos < 0: 143 | self.cover(self.exit_kk_up,self.fixed_size,True) 144 | 145 | self.put_event() 146 | self.sync_data() 147 | self.put_event() 148 | def on_order(self, order: OrderData): 149 | """ 150 | Callback of new order data update. 151 | """ 152 | pass 153 | 154 | def on_trade(self, trade: TradeData): 155 | """ 156 | Callback of new trade data update. 157 | """ 158 | self.put_event() 159 | 160 | def on_stop_order(self, stop_order: StopOrder): 161 | """ 162 | Callback of stop order update. 163 | """ 164 | pass 165 | # self.put_event() 166 | 167 | def market_order(self): 168 | """""" 169 | pass 170 | # self.buy(self.last_tick.limit_up, 1) 171 | # self.write_log("执行市价单测试") 172 | 173 | def limit_order(self): 174 | """""" 175 | pass 176 | # self.buy(self.last_tick.limit_down, 1) 177 | # self.write_log("执行限价单测试") 178 | 179 | def stop_order(self): 180 | """""" 181 | pass 182 | # self.buy(self.last_tick.ask_price_1, 1, True) 183 | # self.write_log("执行停止单测试") 184 | 185 | def dualthrust(self,high,low,close,open,n,k1,k2): 186 | """ 187 | :param high: 188 | :param low: 189 | :param close: 190 | :return: 191 | """ 192 | #计算N日最高价的最高价,收盘价的最高价、最低价,最低价的最低价 193 | hh = high[-n:].max() 194 | lc = close[-n:].min() 195 | hc = close[-n:].max() 196 | ll = low[-n:].min() 197 | 198 | #计算range,上下轨的距离前一根K线开盘价的距离 199 | range = max(hh - lc,hc - ll) 200 | 201 | up = open[-2] + k1 * range 202 | down = open[-2] - k2 * range 203 | 204 | return up,down 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /mike_boll_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/12 14:34 5 | #文件名称 :mike_boll_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | 9 | from vnpy.app.cta_strategy import ( 10 | CtaTemplate, 11 | StopOrder, 12 | TickData, 13 | BarData, 14 | TradeData, 15 | OrderData, 16 | BarGenerator, 17 | ArrayManager, 18 | ) 19 | 20 | from vnpy.trader.constant import Direction ,Interval 21 | 22 | 23 | class Mike_Boll_Strategy(CtaTemplate): 24 | """""" 25 | 26 | author = "yunya" 27 | 28 | open_window = 5 29 | xminute_window = 15 30 | mike_window = 2 31 | boll_length = 80 32 | boll_dev = 2.0 33 | mike_length = 20 34 | sl_multiplier = 0.81 35 | sl_trade = 2 36 | fixed_size = 1 37 | 38 | ask = 0 39 | bid = 0 40 | 41 | ema_mid = 0 42 | ema_hh = 0 43 | ema_ll = 0 44 | 45 | ema_wr = 0 #初级压力线 46 | ema_mr = 0 #中级压力线 47 | ema_sr = 0 #高级压力线 48 | 49 | ema_ws = 0 #初级支撑线 50 | ema_ms = 0 #中级支撑线 51 | ema_ss = 0 #高级支撑线 52 | 53 | boll_up = 0 54 | boll_down = 0 55 | long_stop = 0 56 | short_stop = 0 57 | ema_entry_crossover = 0 58 | boll_entry_crossover = 0 59 | boll_width = 0 60 | 61 | parameters = [ 62 | "open_window", 63 | "xminute_window", 64 | "mike_window", 65 | "boll_length", 66 | "boll_dev", 67 | "mike_length", 68 | "sl_multiplier", 69 | "sl_trade", 70 | "fixed_size" 71 | ] 72 | 73 | variables = [ 74 | "long_stop", 75 | "short_stop", 76 | "ema_entry_crossover", 77 | "boll_entry_crossover", 78 | "boll_width", 79 | "ema_mid", 80 | "ema_hh", 81 | "ema_ll", 82 | "ema_wr", 83 | "ema_mr", 84 | "ema_sr", 85 | "ema_ws", 86 | "ema_ms", 87 | "ema_ss", 88 | ] 89 | 90 | def __init__(self, cta_engine, strategy_nam_xhoure, vt_symbol, setting): 91 | """""" 92 | super().__init__(cta_engine, strategy_nam_xhoure, vt_symbol, setting) 93 | 94 | self.bg_open = BarGenerator(self.on_bar, self.open_window, self.on_open_bar) 95 | self.am_open = ArrayManager() 96 | 97 | self.bg_xminute = BarGenerator( 98 | on_bar=self.on_bar, 99 | window=self.xminute_window, 100 | on_window_bar=self.on_xminute_bar, 101 | interval=Interval.MINUTE 102 | ) 103 | self.am_xminute = ArrayManager() 104 | 105 | self.bg_xhour = BarGenerator( 106 | on_bar=self.on_bar, 107 | window=self.mike_window, 108 | on_window_bar=self.on_hour_bar, 109 | interval=Interval.HOUR 110 | ) 111 | self.am_xhour = ArrayManager(self.mike_length + 5) 112 | 113 | def on_init(self): 114 | """ 115 | Callback when strategy is inited. 116 | """ 117 | self.write_log("策略初始化") 118 | self.load_bar(10) 119 | 120 | def on_start(self): 121 | """ 122 | Callback when strategy is started. 123 | """ 124 | self.write_log("策略启动") 125 | 126 | def on_stop(self): 127 | """ 128 | Callback when strategy is stopped. 129 | """ 130 | self.write_log("策略停止") 131 | 132 | def on_tick(self, tick: TickData): 133 | """ 134 | Callback of new tick data update. 135 | """ 136 | self.bg_xhour.update_tick(tick) 137 | self.ask = tick.ask_price_1 # 卖一价 138 | self.bid = tick.bid_price_1 # 买一价 139 | 140 | def on_bar(self, bar: BarData): 141 | """ 142 | Callback of new bar data update. 143 | """ 144 | self.bg_xhour.update_bar(bar) 145 | self.bg_xminute.update_bar(bar) 146 | self.bg_open.update_bar(bar) 147 | 148 | def on_open_bar(self, bar: BarData): 149 | """ 150 | 开单窗口 151 | """ 152 | self.cancel_all() 153 | 154 | self.am_open.update_bar(bar) 155 | if not self.am_xhour.inited or not self.am_xminute.inited or not self.am_open.inited : 156 | return 157 | 158 | if self.pos == 0: 159 | self.intra_trade_high = bar.high_price 160 | self.intra_trade_low = bar.low_price 161 | 162 | if self.ema_entry_crossover > 0 : 163 | self.buy(self.boll_up, self.fixed_size,True) 164 | 165 | elif self.ema_entry_crossover < 0 : 166 | self.short(self.boll_down, self.fixed_size,True) 167 | 168 | elif self.pos > 0: 169 | if self.ema_entry_crossover < 0: 170 | self.sell(bar.close_price - 5, abs(self.pos)) 171 | 172 | else: 173 | self.intra_trade_high = max(self.intra_trade_high, bar.high_price) 174 | self.intra_trade_low = bar.low_price 175 | 176 | # 使用布林宽度止损 177 | self.long_stop = self.intra_trade_high - self.boll_width * self.sl_multiplier 178 | self.sell(self.long_stop, abs(self.pos), True) 179 | 180 | elif self.pos < 0: 181 | if self.ema_entry_crossover > 0: 182 | self.cover(bar.close_price + 5, abs(self.pos)) 183 | 184 | else: 185 | self.intra_trade_high = bar.high_price 186 | self.intra_trade_low = min(self.intra_trade_low, bar.low_price) 187 | 188 | self.short_stop = self.intra_trade_low + self.boll_width * self.sl_multiplier 189 | self.cover(self.short_stop, abs(self.pos), True) 190 | 191 | self.sync_data() 192 | self.put_event() 193 | 194 | def on_xminute_bar(self,bar:BarData): 195 | """ 196 | :param_xhour bar: 197 | :return: 198 | """ 199 | # 计算布林线指标 200 | self.am_xminute.update_bar(bar) 201 | 202 | if not self.am_xminute.inited and not self.am_xhour.inited: 203 | return 204 | 205 | boll_up_array,boll_down_array = self.am_xminute.boll(self.boll_length,self.boll_dev,True) 206 | 207 | self.boll_up = boll_up_array[-1] 208 | self.boll_down = boll_down_array[-1] 209 | self.boll_width = self.boll_up - self.boll_down 210 | 211 | self.put_event() 212 | def on_hour_bar(self, bar: BarData): 213 | """ 214 | 计算 mike 指标线 215 | :param_xhour bar: 216 | :return: 217 | """ 218 | 219 | self.am_xhour.update_bar(bar) 220 | if not self.am_xhour.inited: 221 | return 222 | 223 | # 计算mike压力支撑线 224 | ema_array = (self.am_xhour.close[:-1] + self.am_xhour.high[:-1] + self.am_xhour.low[:-1]) / 3 225 | self.ema_mid = ema_array[-1] 226 | self.ema_hh = self.am_xhour.high[-self.mike_length:-1].max() 227 | self.ema_ll = self.am_xhour.low[-self.mike_length:-1].min() 228 | 229 | self.ema_wr = self.ema_mid + (self.ema_mid - self.ema_ll) 230 | self.ema_mr = self.ema_mid + (self.ema_hh - self.ema_ll) 231 | self.ema_sr = 2 * self.ema_hh - self.ema_ll 232 | 233 | self.ema_ws = self.ema_mid - (self.ema_hh - self.ema_mid) 234 | self.ema_ms = self.ema_mid - (self.ema_hh - self.ema_ll) 235 | self.ema_ss = 2 * self.ema_ll - self.ema_hh 236 | 237 | if (self.am_xhour.close[-1] > self.ema_sr) or (self.ema_ms < self.am_xhour.close[-1] < self.ema_ws): 238 | self.ema_entry_crossover = 1 239 | 240 | elif (self.am_xhour.close[-1] < self.ema_ss) or (self.ema_mr > self.am_xhour.close[-1] > self.ema_wr): 241 | self.ema_entry_crossover = -1 242 | self.put_event() 243 | 244 | def on_order(self, order: OrderData): 245 | """ 246 | Callback of new order data update. 247 | """ 248 | pass 249 | 250 | # self.write_log(f"on_order: status:{order.status}, orderid: {order.vt_orderid}, offset:{order.offset}, price:{order.price}, volume:{order.volume}, traded: {order.traded}") 251 | # self.put_event() 252 | 253 | def on_trade(self, trade: TradeData): 254 | """ 255 | Callback of new trade data update. 256 | """ 257 | self.put_event() 258 | 259 | def on_stop_order(self, stop_order: StopOrder): 260 | """ 261 | Callback of stop order update. 262 | """ 263 | pass 264 | 265 | 266 | -------------------------------------------------------------------------------- /mike_dc_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/14 8:34 5 | #文件名称 :mike_dc_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | from vnpy.app.cta_strategy import ( 9 | CtaTemplate, 10 | StopOrder, 11 | TickData, 12 | BarData, 13 | TradeData, 14 | OrderData, 15 | BarGenerator, 16 | ArrayManager, 17 | ) 18 | 19 | from vnpy.trader.constant import Direction ,Interval,Exchange 20 | 21 | 22 | class Mike_Dc_Strategy(CtaTemplate): 23 | """""" 24 | 25 | author = "yunya" 26 | 27 | exchange : Exchange = "" 28 | mike_window = 1 29 | mike_length = 30 30 | dc_length = 10 31 | kk_length = 20 32 | kk_dev = 2.0 33 | sl_trade = 2 34 | fixed_size = 1 35 | 36 | ask = 0 37 | bid = 0 38 | 39 | ema_mid = 0 40 | ema_hh = 0 41 | ema_ll = 0 42 | 43 | ema_wr = 0 #初级压力线 44 | ema_mr = 0 #中级压力线 45 | ema_sr = 0 #高级压力线 46 | 47 | ema_ws = 0 #初级支撑线 48 | ema_ms = 0 #中级支撑线 49 | ema_ss = 0 #高级支撑线 50 | 51 | dc_up = 0 52 | dc_down = 0 53 | kk_up = 0 54 | kk_down = 0 55 | 56 | atr_value = 0 57 | long_stop = 0 58 | short_stop = 0 59 | long_stop_trade = 0 60 | short_stop_trade = 0 61 | long_enrty = 0 62 | short_enrty = 0 63 | ema_entry_crossover = 0 64 | boll_entry_crossover = 0 65 | boll_width = 0 66 | 67 | 68 | parameters = [ 69 | "exchange", 70 | "mike_window", 71 | "mike_length", 72 | "dc_length", 73 | "kk_length", 74 | "kk_dev", 75 | "sl_trade", 76 | "fixed_size", 77 | ] 78 | 79 | variables = [ 80 | "long_stop", 81 | "short_stop", 82 | "ema_entry_crossover", 83 | "boll_entry_crossover", 84 | "boll_width", 85 | "ema_mid", 86 | "ema_hh", 87 | "ema_ll", 88 | "ema_wr", 89 | "ema_mr", 90 | "ema_sr", 91 | "ema_ws", 92 | "ema_ms", 93 | "ema_ss", 94 | ] 95 | 96 | def __init__(self, cta_engine, strategy_nam_xhoure, vt_symbol, setting): 97 | """""" 98 | super().__init__(cta_engine, strategy_nam_xhoure, vt_symbol, setting) 99 | 100 | self.bg_xhour = BarGenerator( 101 | on_bar=self.on_bar, 102 | window=self.mike_window, 103 | on_window_bar=self.on_hour_bar, 104 | interval=Interval.HOUR 105 | ) 106 | self.am_xhour = ArrayManager(max(self.dc_length ,self.kk_length) + 10) 107 | 108 | def on_init(self): 109 | """ 110 | Callback when strategy is inited. 111 | """ 112 | self.write_log("策略初始化") 113 | self.exchange_load_bar(self.exchange) 114 | 115 | def on_start(self): 116 | """ 117 | Callback when strategy is started. 118 | """ 119 | self.write_log("策略启动") 120 | 121 | def on_stop(self): 122 | """ 123 | Callback when strategy is stopped. 124 | """ 125 | self.write_log("策略停止") 126 | 127 | def on_tick(self, tick: TickData): 128 | """ 129 | Callback of new tick data update. 130 | """ 131 | self.bg_xhour.update_tick(tick) 132 | self.ask = tick.ask_price_1 # 卖一价 133 | self.bid = tick.bid_price_1 # 买一价 134 | 135 | def on_bar(self, bar: BarData): 136 | """ 137 | Callback of new bar data update. 138 | """ 139 | self.bg_xhour.update_bar(bar) 140 | 141 | def on_hour_bar(self, bar: BarData): 142 | """ 143 | 计算 mike 指标线 144 | :param_xhour bar: 145 | :return: 146 | """ 147 | 148 | self.am_xhour.update_bar(bar) 149 | if not self.am_xhour.inited: 150 | return 151 | 152 | # 计算mike压力支撑线 153 | ema_array = (self.am_xhour.close[:-1] + self.am_xhour.high[:-1] + self.am_xhour.low[:-1]) / 3 154 | self.ema_mid = ema_array[-1] 155 | self.ema_hh = self.am_xhour.high[-self.mike_length:-1].max() 156 | self.ema_ll = self.am_xhour.low[-self.mike_length:-1].min() 157 | 158 | self.ema_wr = self.ema_mid + (self.ema_mid - self.ema_ll) 159 | self.ema_mr = self.ema_mid + (self.ema_hh - self.ema_ll) 160 | self.ema_sr = 2 * self.ema_hh - self.ema_ll 161 | 162 | self.ema_ws = self.ema_mid - (self.ema_hh - self.ema_mid) 163 | self.ema_ms = self.ema_mid - (self.ema_hh - self.ema_ll) 164 | self.ema_ss = 2 * self.ema_ll - self.ema_hh 165 | 166 | if (self.am_xhour.close[-1] > self.ema_sr) or (self.ema_ms < self.am_xhour.close[-1] < self.ema_ws): 167 | self.ema_entry_crossover = 1 168 | 169 | elif (self.am_xhour.close[-1] < self.ema_ss) or (self.ema_mr > self.am_xhour.close[-1] > self.ema_wr): 170 | self.ema_entry_crossover = -1 171 | 172 | self.kk_up,self.kk_down = self.am_xhour.keltner(self.kk_length,self.kk_dev) 173 | 174 | self.dc_up,self.dc_down = self.am_xhour.donchian(self.dc_length) 175 | 176 | if self.pos == 0: 177 | self.atr_value = self.am_xhour.atr(30) 178 | 179 | if self.ema_entry_crossover > 0 : 180 | self.buy(self.kk_up, self.fixed_size,True) 181 | print(self.kk_up) 182 | 183 | elif self.ema_entry_crossover < 0 : 184 | self.short(self.kk_down, self.fixed_size,True) 185 | 186 | elif self.pos > 0: 187 | 188 | self.long_stop = max(self.dc_down,self.long_stop_trade) 189 | self.sell(self.long_stop, abs(self.pos), True) 190 | 191 | elif self.pos < 0: 192 | 193 | self.short_stop = min(self.dc_up,self.short_stop_trade) 194 | self.cover(self.short_stop, abs(self.pos), True) 195 | 196 | self.sync_data() 197 | self.put_event() 198 | 199 | def on_order(self, order: OrderData): 200 | """ 201 | Callback of new order data update. 202 | """ 203 | pass 204 | 205 | # self.write_log(f"on_order: status:{order.status}, orderid: {order.vt_orderid}, offset:{order.offset}, price:{order.price}, volume:{order.volume}, traded: {order.traded}") 206 | # self.put_event() 207 | 208 | def on_trade(self, trade: TradeData): 209 | """ 210 | Callback of new trade data update. 211 | """ 212 | # if trade.direction == Direction.LONG: 213 | # self.long_enrty = trade.price 214 | # self.long_stop_trade = self.long_enrty - 2 * self.atr_value 215 | # 216 | # else: 217 | # self.short_enrty = trade.price 218 | # self.short_stop_trade = self.short_enrty + 2 * self.atr_value 219 | 220 | self.put_event() 221 | 222 | def on_stop_order(self, stop_order: StopOrder): 223 | """ 224 | Callback of stop order update. 225 | """ 226 | pass 227 | 228 | def exchange_load_bar(self,exchange:Exchange): 229 | """ 230 | 如果是火币,ok 交易所,就从数据库获取初始化数据 231 | """ 232 | if exchange == Exchange.OKEX or exchange == Exchange.HUOBI: 233 | self.load_bar(days=10,use_database=True) 234 | else: 235 | self.load_bar(10) 236 | 237 | -------------------------------------------------------------------------------- /super_trend_strategy.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/14 20:25 5 | #文件名称 :super_trend_strategy.py 6 | #开发工具 : PyCharm 7 | 8 | import talib 9 | 10 | from vnpy.app.cta_strategy import ( 11 | CtaTemplate, 12 | StopOrder, 13 | TickData, 14 | BarData, 15 | TradeData, 16 | OrderData, 17 | BarGenerator, 18 | ArrayManager, 19 | ) 20 | from vnpy.trader.constant import Interval 21 | 22 | 23 | class KeltnerBanditStrategy(CtaTemplate): 24 | """""" 25 | author = "用Python的交易员" 26 | 27 | open_window = 5 28 | minute_window = 15 29 | hour_window = 1 30 | atr_length = 30 31 | atr_multiplier = 2.0 32 | 33 | kk_window = 20 34 | kk_dev = 2.0 35 | cci_window = 10 36 | cci_stop = 44 37 | atr_window = 10 38 | risk_level = 5000 39 | 40 | trading_size = 0 41 | kk_up = 0 42 | kk_down = 0 43 | cci_value = 0 44 | atr_value = 0 45 | 46 | param_houreters = [ 47 | "kk_window", "kk_dev", "cci_window", 48 | "cci_stop", "atr_window", "risk_level" 49 | ] 50 | variables = [ 51 | "trading_size", "kk_up", "kk_down", 52 | "cci_value", "atr_value" 53 | ] 54 | 55 | def __init__(self, cta_engine, strategy_nam_houre, vt_symbol, setting): 56 | """""" 57 | super().__init__(cta_engine, strategy_nam_houre, vt_symbol, setting) 58 | 59 | self.bg_hour = BarGenerator( 60 | self.on_bar, self.hour_window, self.on_hour_bar, interval=Interval.HOUR) 61 | self.am_hour = ArrayManager() 62 | 63 | self.bg_minute = BarGenerator( 64 | self.on_bar,self.minute_window,self.on_xminute_bar,interval=Interval.MINUTE 65 | ) 66 | self.am_minute = ArrayManager() 67 | 68 | def on_init(self): 69 | """ 70 | Callback when strategy is inited. 71 | """ 72 | self.write_log("策略初始化") 73 | self.load_bar(10) 74 | 75 | def on_start(self): 76 | """ 77 | Callback when strategy is started. 78 | """ 79 | self.write_log("策略启动") 80 | 81 | def on_stop(self): 82 | """ 83 | Callback when strategy is stopped. 84 | """ 85 | self.write_log("策略停止") 86 | 87 | def on_tick(self, tick: TickData): 88 | """ 89 | Callback of new tick data update. 90 | """ 91 | self.bg_hour.update_tick(tick) 92 | 93 | def on_bar(self, bar: BarData): 94 | """ 95 | Callback of new bar data update. 96 | """ 97 | self.bg_hour.update_bar(bar) 98 | self.bg_minute.update_bar(bar) 99 | 100 | def on_xminute_bar(self,bar:BarData): 101 | """ 102 | 核心未完成,等前面三个指标有满意结果后做回测。 103 | 104 | super_trend 逻辑 是kc通道的变种,取原版kc上轨最小值为压力线,取原版下轨最大值为支撑线。 105 | 突破 上轨压力线做多,下穿下轨压力线做空。 106 | 107 | 过度信号: 108 | 109 | 110 | """ 111 | self.cancel_all() 112 | 113 | am_minute = self.am_minute 114 | am_minute.update_bar(bar) 115 | if not am_minute.inited: 116 | return 117 | 118 | self.kk_up, self.kk_down = am_minute.keltner(self.kk_window, self.kk_dev) 119 | self.cci_value = am_minute.cci(self.cci_window) 120 | 121 | if self.pos == 0: 122 | if self.supertrend_entry > 0: 123 | self.buy(self.kk_up, self.trading_size, True) 124 | elif self.supertrend_entry < 0: 125 | self.short(self.kk_down, self.trading_size, True) 126 | 127 | elif self.pos > 0: 128 | if self.cci_value < - self.cci_stop: 129 | self.sell(bar.close_price - 10, abs(self.pos), False) 130 | 131 | elif self.pos < 0: 132 | if self.cci_value > self.cci_stop: 133 | self.cover(bar.close_price + 10, abs(self.pos), False) 134 | 135 | self.put_event() 136 | 137 | def on_hour_bar(self, bar: BarData): 138 | """""" 139 | am_hour = self.am_hour 140 | am_hour.update_bar(bar) 141 | if not am_hour.inited: 142 | return 143 | 144 | up,dowm = self.supertrend( 145 | self.am_hour.close,self.am_hour.high,self.am_hour.low, 146 | self.atr_length,self.atr_multiplier 147 | ) 148 | 149 | if self.am_hour.close[-1] > up: 150 | self.supertrend_entry = 1 151 | 152 | elif self.am_hour.close[-1] < dowm: 153 | self.supertrend_entry = -1 154 | 155 | def on_order(self, order: OrderData): 156 | """ 157 | Callback of new order data update. 158 | """ 159 | pass 160 | 161 | def on_trade(self, trade: TradeData): 162 | """ 163 | Callback of new trade data update. 164 | """ 165 | self.put_event() 166 | 167 | def on_stop_order(self, stop_order: StopOrder): 168 | """ 169 | Callback of stop order update. 170 | """ 171 | pass 172 | def supertrend(self,close,high,low,atr_length,multiplier ): 173 | """ 174 | atr= changeATR ? atr(Periods) : atr2 175 | up=src-(Multiplier*atr) 176 | up1 = nz(up[1],up) 177 | up := close[1] > up1 ? max(up,up1) : up 178 | dn=src+(Multiplier*atr) 179 | dn1 = nz(dn[1], dn) 180 | dn := close[1] < dn1 ? min(dn, dn1) : dn 181 | """ 182 | ema_value = (close + high + low) / 3 183 | atr_value = talib.ATR(high,low,close,atr_length) 184 | 185 | down_array = ema_value - (atr_value * multiplier ) 186 | up_array = ema_value + (atr_value * multiplier) 187 | 188 | if close[-2] > down_array[-2]: 189 | up = max(down_array[-1,down_array[-2]]) 190 | else: 191 | up = down_array[-1] 192 | 193 | if close[-2] < up_array[-2]: 194 | down = min(up_array[-1],up_array[-2]) 195 | else: 196 | down = up_array[-1] 197 | 198 | return up,down -------------------------------------------------------------------------------- /使用plotly库显示的回测引擎.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/18 20:57 5 | #文件名称 :使用plotly库显示的回测引擎.py 6 | #开发工具 : PyCharm 7 | 8 | 9 | from plotly.subplots import make_subplots 10 | import plotly.graph_objects as go 11 | 12 | from vnpy.trader.constant import Interval 13 | from datetime import datetime 14 | import pandas as pd 15 | from vnpy.app.cta_strategy.base import BacktestingMode 16 | 17 | # 使用原版回测引行 18 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine ,OptimizationSetting 19 | 20 | # 使用自动修改的回测引行 21 | # from vnpy.app.cta_strategy.Newbacktesting import 22 | 23 | 24 | 25 | 26 | pd.set_option('expand_frame_repr', False) 27 | 28 | #导入策略 29 | 30 | from vnpy.huicheshuju.strategy.boll_control_dc_strategy import Boll_Control_Dcs_trategy 31 | 32 | 33 | 34 | if __name__ == '__main__': 35 | 36 | # 回测引擎初始化 37 | engine = BacktestingEngine() 38 | 39 | # 设置交易对产品的参数 40 | engine.set_parameters( 41 | vt_symbol="btcusdt.BINANCE", #交易的标的 42 | interval=Interval.MINUTE, 43 | start=datetime(2020,3,1), # 开始时间 44 | end=datetime(2020, 5, 1), # 结束时间 45 | rate= 2/1000, # 手续费 46 | slippage=0.5, #交易滑点 47 | size=1, # 合约乘数 48 | pricetick=0.5, # 8500.5 8500.01 49 | capital= 100000, # 初始资金 50 | mode= BacktestingMode.BAR 51 | ) 52 | 53 | 54 | # 添加策略 55 | # engine.add_strategy(EmaHlc3, {}) 56 | # engine.add_strategy(Atr_Stop, { "max_window" :35, 57 | # "min_window" :10, 58 | # "open_window" :5, 59 | # "art_leng" :14, 60 | # "nloss_singnal" :3.8, 61 | # "ema_window" :19, 62 | # "tr_leng" :34, 63 | # "sl_multiplier" :2.5, 64 | # "fixd_size" : 1 65 | # }) 66 | 67 | # 传入参数,实例化一个策略,相当于执行了DoubleMaStrategy(strategy_name,vt_symbol, setting) 68 | engine.add_strategy(Boll_Control_Dcs_trategy,{}) 69 | 70 | # 加载 最终的结果是通过数据库的ORM取出DbBarData,遍历DbBarData,通过to_tick或to_bar方法生成tick或Bar, 71 | # 最终得到self.history_data(里面保存tick或bar) 72 | engine.load_data() 73 | 74 | # 运行回测 75 | engine.run_backtesting() 76 | 77 | # 统计结果 78 | df = engine.calculate_result() 79 | 80 | print(df) 81 | 82 | # 计算策略的统计指标 Sharp ratio, drawdown 83 | de = engine.calculate_statistics() 84 | de = pd.DataFrame([de]) 85 | # print(de) 86 | de = de.rename(columns = { 87 | 'start_date': "首个交易日", 88 | 'end_date': '最后交易日', 89 | 'total_days': '总交易日', 90 | 'profit_days': '盈利交易日', 91 | 'loss_days': '亏损交易日', 92 | 'capital': '起始资金', 93 | 'end_balance': '结束资金', 94 | 'max_drawdown': '最大回撤', 95 | 'max_ddpercent': '百分比最大回撤', 96 | 'max_drawdown_duration': '最长回撤天数', 97 | 'total_net_pnl': '总盈亏', 98 | 'daily_net_pnl': '日均盈亏', 99 | 'total_commission': '总手续费', 100 | 'daily_commission': '日均手续费', 101 | 'total_slippage': '总滑点', 102 | 'daily_slippage': '日均滑点', 103 | 'total_turnover': '总成交金额', 104 | 'daily_turnover': '日均成交金额', 105 | 'total_trade_count': '总成交笔数', 106 | 'daily_trade_count': '日均成交笔数', 107 | 'total_return': '总收益率', 108 | 'annual_return': '年化收益', 109 | 'daily_return': '日均收益率', 110 | 'return_std': '收益标准差', 111 | 'sharpe_ratio': 'Sharpe Ratio', 112 | 'return_drawdown_ratio': '收益回撤比' 113 | }) 114 | de = de[['首个交易日','最后交易日','总交易日','盈利交易日', 115 | '亏损交易日','起始资金','结束资金','总收益率','年化收益', 116 | '最大回撤','百分比最大回撤','最长回撤天数','总盈亏','总手续费', 117 | '总滑点','总成交金额','总成交笔数','日均盈亏','日均手续费','日均滑点', 118 | '日均成交金额','日均成交笔数','日均收益率','收益标准差', 119 | 'Sharpe Ratio','收益回撤比']] 120 | 121 | # de = pd.DataFrame(pd.Series(de)) 122 | 123 | print(de) 124 | 125 | # 创建一个4行、1列的带子图绘图区域,并分别给子图加上标题 126 | fig = make_subplots(rows=4, cols=1, subplot_titles=["Balance", "Drawdown", "Daily Pnl", "Pnl Distribution"], 127 | vertical_spacing=0.06) 128 | 129 | # 第一张:账户净值子图,用折线图来绘制 130 | fig.add_trace(go.Line(x=df.index, y=df["balance"], name="Balance"), row=1, col=1) 131 | 132 | # 第二张:最大回撤子图,用面积图来绘制 133 | fig.add_trace( 134 | go.Scatter(x=df.index, y=df["drawdown"], fillcolor="red", fill='tozeroy', line={"width": 0.5, "color": "red"}, 135 | name="Drawdown"), row=2, col=1) 136 | 137 | # 第三张:每日盈亏子图,用柱状图来绘制 138 | fig.add_trace(go.Bar(y=df["net_pnl"], name="Daily Pnl"), row=3, col=1) 139 | 140 | # 第四张:盈亏分布子图,用直方图来绘制 141 | fig.add_trace(go.Histogram(x=df["net_pnl"], nbinsx=100, name="Days"), row=4, col=1) 142 | 143 | # 把图表放大些,默认小了点 144 | fig.update_layout(height=1000, width=1000) 145 | 146 | # 将绘制完的图表,正式显示出来 147 | fig.show() 148 | # 绘制图表 149 | # engine.show_chart() 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /回测引行.py: -------------------------------------------------------------------------------- 1 | 2 | from vnpy.trader.constant import Interval 3 | from datetime import datetime 4 | import pandas as pd 5 | from vnpy.app.cta_strategy.base import BacktestingMode 6 | 7 | # 使用原版回测引行 8 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine ,OptimizationSetting 9 | 10 | # 使用自动修改的回测引行 11 | # from vnpy.app.cta_strategy.Newbacktesting import 12 | 13 | 14 | 15 | 16 | pd.set_option('expand_frame_repr', False) 17 | 18 | #导入策略 19 | 20 | from vnpy.huicheshuju.strategy.atrstop_rsi_dc_strategy import AtrStopRsiDcStrategy 21 | from vnpy.huicheshuju.strategy.boll_kk_vix_simple_strategy import Boll_kk_vix_simple_Strategy 22 | 23 | from vnpy.huicheshuju.strategy.boll_control_dc_strategy import Boll_Control_Dcs_trategy 24 | 25 | 26 | 27 | if __name__ == '__main__': 28 | 29 | # 回测引擎初始化 30 | engine = BacktestingEngine() 31 | 32 | # 设置交易对产品的参数 33 | engine.set_parameters( 34 | vt_symbol="btcusdt.BINANCE", #交易的标的 35 | interval=Interval.MINUTE, 36 | start=datetime(2020,3,1), # 开始时间 37 | end=datetime(2020, 5, 1), # 结束时间 38 | rate= 2/1000, # 手续费 39 | slippage=0.5, #交易滑点 40 | size=1, # 合约乘数 41 | pricetick=0.5, # 8500.5 8500.01 42 | capital= 100000, # 初始资金 43 | mode= BacktestingMode.BAR 44 | ) 45 | 46 | 47 | # 添加策略 48 | # engine.add_strategy(EmaHlc3, {}) 49 | # engine.add_strategy(Atr_Stop, { "max_window" :35, 50 | # "min_window" :10, 51 | # "open_window" :5, 52 | # "art_leng" :14, 53 | # "nloss_singnal" :3.8, 54 | # "ema_window" :19, 55 | # "tr_leng" :34, 56 | # "sl_multiplier" :2.5, 57 | # "fixd_size" : 1 58 | # }) 59 | 60 | # 传入参数,实例化一个策略,相当于执行了DoubleMaStrategy(strategy_name,vt_symbol, setting) 61 | engine.add_strategy(Boll_Control_Dcs_trategy,{}) 62 | 63 | # 加载 最终的结果是通过数据库的ORM取出DbBarData,遍历DbBarData,通过to_tick或to_bar方法生成tick或Bar, 64 | # 最终得到self.history_data(里面保存tick或bar) 65 | engine.load_data() 66 | 67 | # 运行回测 68 | engine.run_backtesting() 69 | 70 | # 统计结果 71 | df = engine.calculate_result() 72 | 73 | print(df) 74 | 75 | # 计算策略的统计指标 Sharp ratio, drawdown 76 | de = engine.calculate_statistics() 77 | de = pd.DataFrame([de]) 78 | # print(de) 79 | de = de.rename(columns = { 80 | 'start_date': "首个交易日", 81 | 'end_date': '最后交易日', 82 | 'total_days': '总交易日', 83 | 'profit_days': '盈利交易日', 84 | 'loss_days': '亏损交易日', 85 | 'capital': '起始资金', 86 | 'end_balance': '结束资金', 87 | 'max_drawdown': '最大回撤', 88 | 'max_ddpercent': '百分比最大回撤', 89 | 'max_drawdown_duration': '最长回撤天数', 90 | 'total_net_pnl': '总盈亏', 91 | 'daily_net_pnl': '日均盈亏', 92 | 'total_commission': '总手续费', 93 | 'daily_commission': '日均手续费', 94 | 'total_slippage': '总滑点', 95 | 'daily_slippage': '日均滑点', 96 | 'total_turnover': '总成交金额', 97 | 'daily_turnover': '日均成交金额', 98 | 'total_trade_count': '总成交笔数', 99 | 'daily_trade_count': '日均成交笔数', 100 | 'total_return': '总收益率', 101 | 'annual_return': '年化收益', 102 | 'daily_return': '日均收益率', 103 | 'return_std': '收益标准差', 104 | 'sharpe_ratio': 'Sharpe Ratio', 105 | 'return_drawdown_ratio': '收益回撤比' 106 | }) 107 | de = de[['首个交易日','最后交易日','总交易日','盈利交易日', 108 | '亏损交易日','起始资金','结束资金','总收益率','年化收益', 109 | '最大回撤','百分比最大回撤','最长回撤天数','总盈亏','总手续费', 110 | '总滑点','总成交金额','总成交笔数','日均盈亏','日均手续费','日均滑点', 111 | '日均成交金额','日均成交笔数','日均收益率','收益标准差', 112 | 'Sharpe Ratio','收益回撤比']] 113 | 114 | # de = pd.DataFrame(pd.Series(de)) 115 | 116 | print(de) 117 | 118 | # 绘制图表 119 | engine.show_chart() 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | # de = pd.DataFrame({ 132 | # 'start_date': [0], 133 | # 'end_date': [1], 134 | # 'total_days': [2], 135 | # 'profit_days': [3], 136 | # 'loss_days': [4], 137 | # 'capital': [5], 138 | # 'end_balance': [6], 139 | # 'max_drawdown': [7], 140 | # 'max_ddpercent': [8], 141 | # 'max_drawdown_duration': [9], 142 | # 'total_net_pnl': [10], 143 | # 'daily_net_pnl': [11], 144 | # 'total_commission': [12], 145 | # 'daily_commission': [13], 146 | # 'total_slippage': [14], 147 | # 'daily_slippage': [15], 148 | # 'total_turnover': [16], 149 | # 'daily_turnover': [17], 150 | # 'total_trade_count': [18], 151 | # 'daily_trade_count': [19], 152 | # 'total_return': [20], 153 | # 'annual_return': [21], 154 | # 'daily_return': [22], 155 | # 'return_std': [23], 156 | # 'sharpe_ratio': [24], 157 | # 'return_drawdown_ratio': [25] 158 | # 159 | # }) # 'A'是columns,对应的是list 160 | # 161 | # 162 | 163 | -------------------------------------------------------------------------------- /回测过拟合概率定量计算.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | from tqdm import tqdm 3 | 4 | def pbo_cal(opr,S=10,ind='sortino'): 5 | ''' 6 | 【回测过拟合概率计算】 7 | opr:参数组收益序列表 8 | S:划分的时间序列子集数 9 | ind:指定夏普率或索迪诺比率作为评价指标 [sharpe, sortino] 10 | ''' 11 | 12 | assert S%2 == 0,'划分的时间序列子集数必须为偶数' 13 | 14 | Snum = opr.shape[0] // S # 得到每份子集长度 15 | sub_oprs = [opr.iloc[Snum*i:Snum*(i+1)] for i in range(S)] # 划分得到子集list,多余的部分舍去 16 | logits = [] # 初始化logit变量集 17 | 18 | for c in tqdm(list(combinations(list(range(S)), S//2))): 19 | # 遍历每种组合:抽取一半的子集 20 | c_ = set(range(S)) - set(c) 21 | train = pd.concat([sub_oprs[i] for i in c]) 22 | test = pd.concat([sub_oprs[i] for i in c_]) 23 | # 一半子集组合为训练集,另一半补集组合为测试集 24 | 25 | if ind == 'sharpe': 26 | train_ratio = train.mean() / train.std() 27 | test_ratio = test.mean() / test.std() 28 | 29 | if ind == 'sortino': 30 | train_ratio = train.mean() / train.mask(train>0,0).std() 31 | test_ratio = test.mean() / test.mask(test>0,0).std() 32 | # 计算训练集及测试集,每组参数对应的评价指标 33 | 34 | w = test_ratio.rank()[train_ratio.idxmax()] / (test_ratio.shape[0] + 1) 35 | # 训练集最优参数组在测试集的相对排名 36 | 37 | logits.append(np.log(w/(1-w))) 38 | 39 | logits = pd.DataFrame(sorted(logits),columns=['logit']) 40 | logits['cum_prob'] = (logits.index + 1) / logits.shape[0] 41 | # 计算logit变量经验分布 42 | 43 | return logits.loc[logits['logit']<=0,'cum_prob'].max() 44 | # 返回pbo,pbo越小策略越有效 -------------------------------------------------------------------------------- /多策略组合回测.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/5/14 16:50 5 | #文件名称 :多策略组合回测.py 6 | #开发工具 : PyCharm 7 | 8 | """ 9 | jupyter notebook 10 | """ 11 | 12 | 13 | 14 | # 导入库 15 | from importlib import reload 16 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting 17 | from datetime import datetime 18 | 19 | 20 | # 定义使用的函数 21 | def run_backtesting(strategy_class, setting, vt_symbol, interval, start, end, rate, slippage, size, pricetick, capital): 22 | engine = BacktestingEngine() 23 | engine.set_parameters( 24 | vt_symbol=vt_symbol, 25 | interval=interval, 26 | start=start, 27 | end=end, 28 | rate=rate, 29 | slippage=slippage, 30 | size=size, 31 | pricetick=pricetick, 32 | capital=capital 33 | ) 34 | engine.add_strategy(strategy_class, setting) 35 | engine.load_data() 36 | engine.run_backtesting() 37 | df = engine.calculate_result() 38 | return df 39 | 40 | # 运行策略函数 41 | def show_portafolio(df): 42 | engine = BacktestingEngine() 43 | engine.calculate_statistics(df) 44 | engine.show_chart(df) 45 | 46 | 47 | # 导入第一个策略 48 | from vnpy.huicheshuju.class_strategy.AtrStop_UT import AtrStop_Ut 49 | import AtrStop_UT 50 | from vnpy.huicheshuju.class_strategy.RSI_Vix_Dc import RsiVixDcStrategy 51 | from vnpy.huicheshuju.class_strategy.Vix_Keltnerl import VixKeltnerl 52 | reload(AtrStop_UT) 53 | 54 | df1 = run_backtesting( 55 | strategy_class=AtrStop_Ut, 56 | setting={ 57 | "atrstop_window":36, 58 | "nloss_singnal":2.6, 59 | "sl_multiplier":10.0, 60 | "distance_line":1.4, 61 | "fixd_size":1, 62 | "atr_window":30 63 | }, 64 | vt_symbol="BTCUSD.BINANCE", 65 | interval="1m", 66 | start=datetime(2017,1,1), 67 | end=datetime(2020,5,30), 68 | rate= 2/1000, 69 | slippage=0.5, 70 | size=1, 71 | pricetick=0.5, 72 | capital=10000, 73 | ) 74 | 75 | # 运行第一个策略 76 | show_portafolio(df1) 77 | 78 | 79 | # 导入第二个策略 80 | from vnpy.huicheshuju.class_strategy.Vix_Keltnerl import VixKeltnerl 81 | import Boll_Control_Proportion_vix 82 | reload(Boll_Control_Proportion_vix) 83 | 84 | setting={ 85 | "win_open":10, 86 | "kk_window":50, 87 | "sl_multiplier":13, 88 | "fixed_size":1} 89 | 90 | 91 | setting={ 92 | "win_open":36, 93 | "boll_window":24, 94 | "prop":1.8, 95 | "sl_multiplier":4.4, 96 | "fixed_size":1,} 97 | df2 = run_backtesting( 98 | strategy_class=Boll_Control_Proportion_vix.Boll_Control_Proportion_vix, 99 | setting={}, 100 | vt_symbol="BTCUSD.BINANCE", 101 | interval="1m", 102 | start=datetime(2017, 1, 1), 103 | end=datetime(2020, 5, 30), 104 | rate=0.75/10000, 105 | slippage=0.5, 106 | size=10, 107 | pricetick=0.5, 108 | capital=1_000_000, 109 | ) 110 | 111 | # 运行第二个策略 112 | show_portafolio(df2) 113 | 114 | # 显示两个策略的结果 115 | dfp = df1 + df2 116 | dfp =dfp.dropna() 117 | show_portafolio(dfp) -------------------------------------------------------------------------------- /多进程回测引行.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/5/5 22:10 5 | #文件名称 :多进程回测引行.py 6 | #开发工具 : PyCharm 7 | 8 | from vnpy.huicheshuju.backtestingengine.back_testing_to_csv import to_csv_result 9 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine ,OptimizationSetting 10 | from vnpy.trader.constant import Interval 11 | from datetime import datetime 12 | import pandas as pd 13 | pd.set_option('expand_frame_repr', False) 14 | 15 | #导入策略 16 | 17 | 18 | from vnpy.huicheshuju.strategy.boll_control_dc_strategy import Boll_Control_Dcs_trategy 19 | 20 | 21 | ############################## 修改内容 #################################### 22 | 23 | # 定义使用的函数 24 | def run_backtesting(strategy_class, setting, vt_symbol, interval, start, end, rate, slippage, size, pricetick, capital,inverse): 25 | engine.set_parameters( 26 | vt_symbol=vt_symbol, 27 | interval=interval, 28 | start=start, 29 | end=end, 30 | rate=rate, 31 | slippage=slippage, 32 | size=size, 33 | pricetick=pricetick, 34 | capital=capital, 35 | inverse=False # 正反向合约 36 | ) 37 | engine.add_strategy(strategy_class, setting) 38 | engine.load_data() 39 | 40 | 41 | 42 | 43 | 44 | ##################################################################################### 45 | 版本号 = 1.0 46 | 升级内容 = "测试回测系统" 47 | 48 | 49 | strategy_class = Boll_Control_Dcs_trategy # 策略名称 50 | exchange ="BINANCE" 51 | symbol = "btcusdt" 52 | start = datetime(2017, 4, 3) # 开始时间 53 | end = datetime(2019, 8, 1) # 结束时间 54 | rate= 10 / 10000 # 手续费 55 | slippage = 0.5 # 滑点 56 | size = 1 # 合约乘数 57 | pricetick = 0.5 # 一跳 58 | inverse = False # 正反向合约 59 | interval = "1m" # k线周期 60 | capital = 10000 # 初始资金 61 | vt_symbol = symbol + "." + exchange # 交易对 62 | 63 | class_name = "total_net_pnl" # 总收益 64 | #"sharpe_ratio , total_return , return_drawdown_ratio" 65 | # #自己需要自由度跑的结果在回测引擎的cta_strategy的back_testing.py文件里查找 66 | backtest = "EX" # 穷举算法类型 Exhaustion 67 | 68 | description = f"回测数据基于{exchange}交易所的{symbol}的{interval}分钟数据,开始时间为:{start},结束时间为:{end},'\n'" \ 69 | f"交易手续费设置为:{rate},滑点为:{slippage},合约乘数为:{size},盘口一跳为:{pricetick},合约方向:{inverse}(注:F 正向,T 反向)。'\n'" \ 70 | f"策略名为:{strategy_class.__qualname__},回测指标为:{class_name}。'\n'" \ 71 | f"回测时间为:{datetime.now().strftime('%Y-%m-%d')} ,算法类型:{backtest}(DNA:遗传 EX:穷举)" '\n'\ 72 | f"版本号:{版本号},本次版本升级内容为:{升级内容} '\n" 73 | 74 | 75 | 76 | 77 | 78 | if __name__ == '__main__': 79 | 80 | # 回测引擎初始化 81 | engine = BacktestingEngine() 82 | 83 | # 设置交易对产品的参数 84 | # 设置交易对产品的参数 85 | run_backtesting( 86 | strategy_class=strategy_class, 87 | setting={}, 88 | vt_symbol=vt_symbol, 89 | interval=interval, 90 | start=start , 91 | end=end, 92 | rate= rate, 93 | slippage=slippage, 94 | size=size, 95 | pricetick=pricetick, 96 | capital=capital, 97 | inverse=inverse, # 正反向合约 98 | ) 99 | # 优化函数 100 | setting = OptimizationSetting() 101 | 102 | # 优化指示名称 如果夏普率 103 | setting.set_target(f"{class_name}") 104 | setting.add_parameter("open_window", 15, 15, 5) # 开始值,结束值,步长 105 | setting.add_parameter("boll_length", 60, 100, 2) 106 | setting.add_parameter("sl_multiplier", 0.6, 0.8, 0.01) 107 | setting.add_parameter("dc_length", 70, 100, 5) 108 | setting.add_parameter("prop", 1.8, 1.8, 0.2) 109 | 110 | # 多进程穷举优化 111 | result = engine.run_optimization(setting) 112 | 113 | to_csv_result( 114 | result=result, 115 | signal_name = str(strategy_class.__qualname__), # 策略名称 116 | class_name=class_name, # 计算指标类型 117 | symbol =symbol, # 交易对 118 | exchange = exchange, # 交易所 119 | tag=datetime.now().strftime('%Y-%m-%d'), # 当前时间 120 | description=description, # 说明 121 | backtest = backtest # 算法类型 122 | ) 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /测试.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/13 12:00 5 | #文件名称 :测试.py 6 | #开发工具 : PyCharm -------------------------------------------------------------------------------- /遗传穷举算法回测引擎.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/16 15:49 5 | #文件名称 :遗传算法回测引擎.py 6 | #开发工具 : PyCharm 7 | import pathlib 8 | from datetime import datetime 9 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting 10 | 11 | import pandas as pd 12 | pd.set_option('expand_frame_repr', False) 13 | from vnpy.huicheshuju.backtestingengine.back_testing_to_csv import to_csv_result,Backtest 14 | 15 | 16 | # 导入策略 17 | 18 | from vnpy.huicheshuju.strategy.boll_control_dc_strategy import Boll_Control_Dcs_trategy 19 | 20 | 21 | 22 | ############################################################################## 23 | def backtests(backtest:Backtest,setting): 24 | if backtest == "DNA": 25 | result = engine.run_ga_optimization(setting) # 遗传算法 26 | elif backtest == "EX": 27 | result = engine.run_optimization(setting) # 穷举算法 28 | else: 29 | print("算法类型不对!!") 30 | exit() 31 | return result 32 | 33 | # 定义使用的函数 34 | def run_backtesting(strategy_class, setting, vt_symbol, interval, start, end, rate, slippage, size, pricetick, capital,inverse): 35 | engine.set_parameters( 36 | vt_symbol=vt_symbol, 37 | interval=interval, 38 | start=start, 39 | end=end, 40 | rate=rate, 41 | slippage=slippage, 42 | size=size, 43 | pricetick=pricetick, 44 | capital=capital, 45 | inverse=False # 正反向合约 46 | ) 47 | engine.add_strategy(strategy_class, setting) 48 | engine.load_data() 49 | 50 | 51 | 52 | ##################################################################################### 53 | 54 | backtest = "DNA" # 遗传算法类型 55 | # backtest = "EX" # 穷举算法类型 56 | 57 | class_name = "total_net_pnl" # 总收益 58 | #"sharpe_ratio , total_return , return_drawdown_ratio" 59 | # #自己需要自由度跑的结果在回测引擎的cta_strategy的back_testing.py文件里查找 60 | 61 | ##################################################################################### 62 | 63 | 版本号 = 1.1 64 | 升级内容 = "在原版布林vix上增加了固定止损和移动唐安寄止损" 65 | 66 | strategy_class = Boll_Control_Dcs_trategy # 策略名称 67 | exchange ="BINANCE" 68 | symbol = "btcusdt" 69 | start = datetime(2017, 4, 3) # 开始时间 70 | end = datetime(2019, 8, 1) # 结束时间 71 | rate= 10 / 10000 # 手续费 72 | slippage = 0.5 # 滑点 73 | size = 1 # 合约乘数 74 | pricetick = 0.5 # 一跳 75 | inverse = False # 正反向合约 76 | interval = "1m" # k线周期 77 | capital = 100000 # 初始资金 78 | vt_symbol = symbol + "." + exchange # 交易对 79 | 80 | 81 | description = f"回测数据基于{exchange}交易所的{symbol}的{interval}分钟数据,开始时间为:{start},结束时间为:{end},'\n'" \ 82 | f"交易手续费设置为:{rate},滑点为:{slippage},合约乘数为:{size},盘口一跳为:{pricetick},合约方向:{inverse}(注:F 正向,T 反向)。'\n'" \ 83 | f"策略名为:{strategy_class.__qualname__},回测指标为:{class_name}。'\n'" \ 84 | f"回测时间为:{datetime.now().strftime('%Y-%m-%d')} ,算法类型:{backtest}" '\n'\ 85 | f"版本号:{版本号},本次版本升级内容为:{升级内容} '\n" 86 | 87 | ##################################################################################### 88 | 89 | if __name__ == '__main__': 90 | 91 | engine = BacktestingEngine() 92 | 93 | # 设置交易对产品的参数 94 | run_backtesting( 95 | strategy_class=strategy_class, 96 | setting={}, 97 | vt_symbol=vt_symbol, 98 | interval=interval, 99 | start=start , 100 | end=end, 101 | rate= rate, 102 | slippage=slippage, 103 | size=size, 104 | pricetick=pricetick, 105 | capital=capital, 106 | inverse=inverse, # 正反向合约 107 | ) 108 | 109 | 110 | setting = OptimizationSetting() 111 | setting.set_target(f"{class_name}") 112 | setting.add_parameter("open_window", 10, 15, 5) # 开始值,结束值,步长 113 | setting.add_parameter("boll_length", 10, 500, 10) 114 | setting.add_parameter("sl_multiplier", 0.3, 1, 0.02) 115 | setting.add_parameter("dc_length", 10, 200, 5) 116 | setting.add_parameter("prop", 1, 2, 0.2) 117 | # setting.add_parameter("", 1, 3, 1) 118 | # setting.add_parameter("atr_window", 1, 3, 1) 119 | #setting.add_parameter("risk_level", 0.2, 0.2, 0.1) 120 | # setting.add_parameter("trailing_tax", 0.7, 2.5, 0.1) 121 | 122 | # 调用算法类型(遗传,穷举) 123 | result = backtests(backtest,setting) 124 | 125 | to_csv_result( 126 | result=result, 127 | signal_name = str(strategy_class.__qualname__), # 策略名称 128 | class_name=class_name, # 计算指标类型 129 | symbol =symbol, # 交易对 130 | exchange = exchange, # 交易所 131 | tag=datetime.now().strftime('%Y-%m-%d'), # 当前时间 132 | description=description, # 说明 133 | backtest = backtest # 算法类型 134 | ) 135 | -------------------------------------------------------------------------------- /遗传算法回测引擎.py: -------------------------------------------------------------------------------- 1 | # _*_coding : UTF-8 _*_ 2 | #开发团队 :yunya 3 | #开发人员 :Administrator 4 | #开发时间 : 2020/6/16 15:49 5 | #文件名称 :遗传算法回测引擎.py 6 | #开发工具 : PyCharm 7 | import pathlib 8 | from datetime import datetime 9 | from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting 10 | 11 | from vnpy.trader.constant import Interval 12 | import pandas as pd 13 | pd.set_option('expand_frame_repr', False) 14 | from vnpy.huicheshuju.backtestingengine.back_testing_to_csv import to_csv_result 15 | 16 | 17 | # 导入策略 18 | 19 | from vnpy.huicheshuju.class_strategy.AtrStop_UT import AtrStop_Ut 20 | 21 | 22 | 23 | ############################################################################## 24 | 25 | # 定义使用的函数 26 | def run_backtesting(strategy_class, setting, vt_symbol, interval, start, end, rate, slippage, size, pricetick, capital,inverse): 27 | engine.set_parameters( 28 | vt_symbol=vt_symbol, 29 | interval=interval, 30 | start=start, 31 | end=end, 32 | rate=rate, 33 | slippage=slippage, 34 | size=size, 35 | pricetick=pricetick, 36 | capital=capital, 37 | inverse=False # 正反向合约 38 | ) 39 | engine.add_strategy(strategy_class, setting) 40 | engine.load_data() 41 | 42 | 43 | 44 | ##################################################################################### 45 | 版本号 = 1.0 46 | 升级内容 = "测试回测系统" 47 | 48 | 49 | strategy_class = AtrStop_Ut # 策略名称 50 | exchange ="BINANCE" 51 | symbol = "btcusdt" 52 | start = datetime(2020, 4, 3) # 开始时间 53 | end = datetime(2020, 5, 3) # 结束时间 54 | rate= 10 / 10000 # 手续费 55 | slippage = 0.5 # 滑点 56 | size = 1 # 合约乘数 57 | pricetick = 0.5 # 一跳 58 | inverse = False # 正反向合约 59 | interval = "1m" # k线周期 60 | capital = 10000 # 初始资金 61 | vt_symbol = symbol + "." + exchange # 交易对 62 | 63 | class_name = "total_net_pnl" # 总收益 64 | #"sharpe_ratio , total_return , return_drawdown_ratio" 65 | # #自己需要自由度跑的结果在回测引擎的cta_strategy的back_testing.py文件里查找 66 | backtest = "DNA" # 遗传算法类型 67 | 68 | description = f"回测数据基于{exchange}交易所的{symbol}的{interval}分钟数据,开始时间为:{start},结束时间为:{end},'\n'" \ 69 | f"交易手续费设置为:{rate},滑点为:{slippage},合约乘数为:{size},盘口一跳为:{pricetick},合约方向:{inverse}(注:F 正向,T 反向)。'\n'" \ 70 | f"策略名为:{strategy_class.__qualname__},回测指标为:{class_name}。'\n'" \ 71 | f"回测时间为:{datetime.now().strftime('%Y-%m-%d')} ,算法类型:{backtest}" '\n'\ 72 | f"版本号:{版本号},本次版本升级内容为:{升级内容} '\n" 73 | 74 | 75 | 76 | 77 | 78 | if __name__ == '__main__': 79 | 80 | engine = BacktestingEngine() 81 | 82 | # 设置交易对产品的参数 83 | run_backtesting( 84 | strategy_class=strategy_class, 85 | setting={}, 86 | vt_symbol=vt_symbol, 87 | interval=interval, 88 | start=start , 89 | end=end, 90 | rate= rate, 91 | slippage=slippage, 92 | size=size, 93 | pricetick=pricetick, 94 | capital=capital, 95 | inverse=inverse, # 正反向合约 96 | ) 97 | 98 | 99 | setting = OptimizationSetting() 100 | setting.set_target(f"{class_name}") 101 | setting.add_parameter("open_window", 5, 5, 1) 102 | setting.add_parameter("nloss_singnal", 2.5, 4, 0.1) 103 | setting.add_parameter("atrstop_window", 40, 60, 5) 104 | # setting.add_parameter("", 16, 32, 2) 105 | # setting.add_parameter("", 7, 11, 1) 106 | # setting.add_parameter("", 1, 3, 1) 107 | # setting.add_parameter("atr_window", 1, 3, 1) 108 | #setting.add_parameter("risk_level", 0.2, 0.2, 0.1) 109 | # setting.add_parameter("trailing_tax", 0.7, 2.5, 0.1) 110 | 111 | result = engine.run_ga_optimization(setting)#遗传算法 112 | 113 | to_csv_result( 114 | result=result, 115 | signal_name = str(strategy_class.__qualname__), # 策略名称 116 | class_name=class_name, # 计算指标类型 117 | symbol =symbol, # 交易对 118 | exchange = exchange, # 交易所 119 | tag=datetime.now().strftime('%Y-%m-%d'), # 当前时间 120 | description=description, # 说明 121 | backtest = backtest # 算法类型 122 | ) 123 | 124 | --------------------------------------------------------------------------------