diff --git a/vnpy/app/algo_trading/engine.py b/vnpy/app/algo_trading/engine.py index 97365b55..9e22d5d0 100644 --- a/vnpy/app/algo_trading/engine.py +++ b/vnpy/app/algo_trading/engine.py @@ -63,6 +63,7 @@ class AlgoEngine(BaseEngine): from .algos.dma_algo import DmaAlgo from .algos.arbitrage_algo import ArbitrageAlgo from .algos.spread_algo_v2 import SpreadAlgoV2 + from .algos.autostopwin_algo import AutoStopWinAlgo self.add_algo_template(TwapAlgo) self.add_algo_template(IcebergAlgo) @@ -73,6 +74,7 @@ class AlgoEngine(BaseEngine): self.add_algo_template(DmaAlgo) self.add_algo_template(ArbitrageAlgo) self.add_algo_template(SpreadAlgoV2) + self.add_algo_template(AutoStopWinAlgo) def add_algo_template(self, template: AlgoTemplate): """""" @@ -180,7 +182,8 @@ class AlgoEngine(BaseEngine): def stop_all(self): """""" for algo_name in list(self.algos.keys()): - self.stop_algo(algo_name) + if self.stop_algo(algo_name): + self.main_engine.write_log(f'{algo_name}已停止') def subscribe(self, algo: AlgoTemplate, vt_symbol: str): """""" @@ -215,7 +218,7 @@ class AlgoEngine(BaseEngine): contract = self.main_engine.get_contract(vt_symbol) if not contract: self.write_log(f'委托下单失败,找不到合约:{vt_symbol}', algo_name=algo.algo_name) - return + return [] volume = round_to(volume, contract.min_volume) if not volume: diff --git a/vnpy/app/cta_option/engine.py b/vnpy/app/cta_option/engine.py index 58e52938..b6c45416 100644 --- a/vnpy/app/cta_option/engine.py +++ b/vnpy/app/cta_option/engine.py @@ -104,12 +104,10 @@ STOP_STATUS_MAP = { # 假期,后续可以从cta_option_config.json文件中获取更新 holiday_dict = { # 放假第一天:放假最后一天 - "2000124": "20200130", - "20200501": "20200505", - "20201001": "20201008", - "20210211": "20210217", - "20210501": "20210505", - "20211001": "20211007", + "20220131": "20220206", + "20220430": "20220504", + "20221001": "20221007", + # updated by 黄健威 2022.01.07 } diff --git a/vnpy/app/cta_strategy_pro/back_testing.py b/vnpy/app/cta_strategy_pro/back_testing.py index 935a261a..64a0a478 100644 --- a/vnpy/app/cta_strategy_pro/back_testing.py +++ b/vnpy/app/cta_strategy_pro/back_testing.py @@ -142,9 +142,9 @@ class BackTestingEngine(object): self.holdings = {} # 多空持仓 - # 当前最新数据,用于模拟成交用 self.gateway_name = u'BackTest' + # 当前最新数据,用于模拟成交用 self.last_bar = {} # 最新的bar self.last_tick = {} # 最新tick self.last_dt = None # 最新时间 @@ -158,7 +158,7 @@ class BackTestingEngine(object): # 回测计算相关 self.use_margin = True # 使用保证金模式(期货使用,计算保证金时,按照开仓价计算。股票是按照当前价计算) - + self.force_cross = False # 强制撮合成功,不使用价格比对,一般用于模拟市价成交 self.init_capital = 1000000 # 期初资金 self.cur_capital = self.init_capital # 当前资金净值 self.net_capital = self.init_capital # 实时资金净值(每日根据capital和持仓浮盈计算) @@ -604,6 +604,11 @@ class BackTestingEngine(object): # 缺省使用保证金方式。(期货使用保证金/股票不使用保证金) self.use_margin = test_setting.get('use_margin', True) + if 'force_cross' in test_setting: + self.force_cross = test_setting.get('force_cross', False) + if self.force_cross: + self.write_log(f'使用强制撮合模式!') + # 设置最大资金使用比例 if 'percent_limit' in test_setting: self.write_log(u'设置最大资金使用比例:{}%'.format(test_setting.get('percent_limit'))) @@ -645,6 +650,7 @@ class BackTestingEngine(object): # 创建资金K线 self.create_fund_kline(self.test_name, use_renko=test_setting.get('use_renko', False)) + # 自动输出日线净值曲线图png文件 self.is_plot_daily = test_setting.get('is_plot_daily', False) # 加载所有本地策略class @@ -1293,8 +1299,8 @@ class BackTestingEngine(object): sell_best_cross_price = tick.last_price # 判断是否会成交 - buy_cross = order.direction == Direction.LONG and order.price >= buy_cross_price - sell_cross = order.direction == Direction.SHORT and order.price <= sell_cross_price + buy_cross = order.direction == Direction.LONG and (order.price >= buy_cross_price or self.force_cross) + sell_cross = order.direction == Direction.SHORT and (order.price <= sell_cross_price or self.force_cross) # 如果发生了成交 if buy_cross or sell_cross: @@ -1321,9 +1327,9 @@ class BackTestingEngine(object): # 2. 假设在上一根K线结束(也是当前K线开始)的时刻,策略发出的委托为限价105 # 3. 则在实际中的成交价会是100而不是105,因为委托发出时市场的最优价格是100 if buy_cross: - trade_price = min(order.price, buy_best_cross_price) + trade_price = min(order.price, buy_best_cross_price) if not self.force_cross else order.price else: - trade_price = max(order.price, sell_best_cross_price) + trade_price = max(order.price, sell_best_cross_price) if not self.force_cross else order.price # renko bar较为特殊,使用委托价进行成交 if trade.vt_symbol.startswith('future_renko'): diff --git a/vnpy/app/cta_strategy_pro/portfolio_testing.py b/vnpy/app/cta_strategy_pro/portfolio_testing.py index c527a22e..31f268b5 100644 --- a/vnpy/app/cta_strategy_pro/portfolio_testing.py +++ b/vnpy/app/cta_strategy_pro/portfolio_testing.py @@ -160,6 +160,12 @@ class PortfolioTestingEngine(BackTestingEngine): self.use_pkb2 = test_setting.get('use_pkb2', True) if self.use_tq: self.use_pkb2 = False + self.output(f'使用天勤数据') + + if self.use_pkb2: + self.output(f'使用pkb2压缩格式') + else: + self.output(f'使用csv文件格式') def prepare_data(self, data_dict): """ @@ -282,6 +288,7 @@ class PortfolioTestingEngine(BackTestingEngine): bar.low_time = bar_data.get('low_time', None) # 最后一次进入低位区域的时间 bar.high_time = bar_data.get('high_time', None) # 最后一次进入高位区域的时间 else: + # 读取的bar是以bar结束时间作为datetime,vnpy是以bar开始时间作为bar datetime bar_datetime = dt - timedelta(seconds=self.bar_interval_seconds) bar = BarData( @@ -400,7 +407,7 @@ class PortfolioTestingEngine(BackTestingEngine): return None df = pd.read_csv(file_path, encoding='gbk', parse_dates=False) - df.columns = ['date', 'time', 'last_price', 'volume', 'last_volume', 'open_interest', + df.columns = ['date', 'time', 'last_price', 'last_volume', 'volume', 'open_interest', 'bid_price_1', 'bid_volume_1', 'bid_price_2', 'bid_volume_2', 'bid_price_3', 'bid_volume_3', 'ask_price_1', 'ask_volume_1', 'ask_price_2', 'ask_volume_2', 'ask_price_3', 'ask_volume_3', 'BS'] diff --git a/vnpy/app/cta_strategy_pro/spread_testing.py b/vnpy/app/cta_strategy_pro/spread_testing.py index 8792c25f..e4ea6021 100644 --- a/vnpy/app/cta_strategy_pro/spread_testing.py +++ b/vnpy/app/cta_strategy_pro/spread_testing.py @@ -323,7 +323,7 @@ class SpreadTestingEngine(BackTestingEngine): symbol, exchange = extract_vt_symbol(vt_symbol) underly_symbol = get_underlying_symbol(symbol) - exchange_folder = VN_EXCHANGE_TICKFOLDER_MAP.get(exchange.value) + exchange_folder = VN_EXCHANGE_TICKFOLDER_MAP.get(exchange.value,'LOCAL') if exchange == Exchange.INE: file_path = os.path.abspath( diff --git a/vnpy/component/cta_utility.py b/vnpy/component/cta_utility.py index 55f3537a..45585eb2 100644 --- a/vnpy/component/cta_utility.py +++ b/vnpy/component/cta_utility.py @@ -627,6 +627,16 @@ def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): if bi_9.high > gg > zg > bi_9.low > zd: return ChanSignals.Q2L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二买', v2='九笔') + if min_low == bi_7.low and max_high == bi_1.high and bi_6.high < bi_2.low: # 前7笔构成向下类趋势 + zd = max([x.low for x in [bi_5, bi_6]]) + zg = min([x.high for x in [bi_4, bi_6]]) + gg = max([x.high for x in [bi_4, bi_6]]) + if zg > zd and bi_8.high > gg == bi_4.high : # 456构成中枢,7离开中枢,8反包且8的高点大于gg,4高 >6高点 + atan = (bi_4.high - bi_6.high) / (bi_5.bars + bi_6.bars - 1) + p = bi_6.high - atan * (bi_7.bars + bi_8.bars + bi_9.bars - 2) + if zd > bi_9.low > p: + return ChanSignals.Q2L0.value + elif direction == 1: # 倒9笔是最高点,倒一笔是最低点 @@ -688,8 +698,8 @@ def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): # 参考双重顶或者圆弧顶, 在 bi_5.low => bi_7.low => p点 形成一条斜线,如果bi_9.high 在斜线之下,就是三卖 if dd == bi_5.low and zd == bi_7.low: - atan = (zd - dd) / (bi_5.bars + bi_6.bars) - p = bi_7.low + atan * (bi_7.bars + bi_8.bars + bi_9.bars) + atan = (zd - dd) / (bi_5.bars + bi_6.bars - 1) + p = bi_7.low + atan * (bi_7.bars + bi_8.bars + bi_9.bars - 2) if bi_9.high < p: return ChanSignals.Q3S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三卖', v2='九笔ZD三卖') @@ -697,6 +707,16 @@ def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): if dd < zd <= bi_9.high < zg: return ChanSignals.Q2S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二卖', v2='九笔') + if min_low == bi_1.low and max_high == bi_7.high and bi_2.high < bi_6.low: # 前7笔形成上涨趋势 + zd = max([x.low for x in [bi_4, bi_6]]) + zg = min([x.high for x in [bi_4, bi_6]]) + dd = min([x.low for x in [bi_4, bi_6]]) + if zg > zd and bi_8.low < dd == bi_4.low: # 456构成中枢,7离开中枢,8反包且8的低点小于dd,4低点< 6低点 + atan = (bi_6.low - bi_4.low) / (bi_5.bars + bi_6.bars -1) + p = bi_6.low + atan * (bi_7.bars + bi_8.bars + bi_9.bars - 2) + if zg < bi_9.high < p: + return ChanSignals.Q2S0.value + return v diff --git a/vnpy/trader/object.py b/vnpy/trader/object.py index a418d70e..dcf55c88 100644 --- a/vnpy/trader/object.py +++ b/vnpy/trader/object.py @@ -337,6 +337,7 @@ class ContractData(BaseData): option_underlying: str = "" # vt_symbol of underlying contract option_type: OptionType = None option_expiry: datetime = None + option_portfolio: str = "" option_index: str = "" # vt_symbol mapping cur option def __post_init__(self):