From 91823b8c71515f93b3cf51315cc0866d251f2f6a Mon Sep 17 00:00:00 2001 From: msincenselee Date: Wed, 1 Apr 2020 14:26:43 +0800 Subject: [PATCH] =?UTF-8?q?[bug=20fix]=20=E4=BF=AE=E5=A4=8Dctp=E5=AF=B9FAK?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E7=BD=91=E6=A0=BC=E7=BB=84=E4=BB=B6=E5=8A=A0=E8=BD=BD=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=BD=AC=E6=8D=A2=E6=8C=81?= =?UTF-8?q?=E4=BB=93=E5=AF=B9=E9=9D=9E=E9=94=81=E5=AE=9A=E5=93=81=E7=A7=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=82=A1=E7=A5=A8?= =?UTF-8?q?=E5=8F=AF=E8=BD=AC=E5=80=BA=E4=BB=B7=E6=A0=BC=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0utility=E5=8E=8B=E7=BC=A9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Q_n_A.md | 23 ++++++ vnpy/app/cta_crypto/engine.py | 2 +- vnpy/app/cta_crypto/template.py | 1 + vnpy/app/cta_strategy_pro/engine.py | 10 ++- vnpy/app/cta_strategy_pro/template.py | 99 ++++++++++++++-------- vnpy/component/cta_grid_trade.py | 4 +- vnpy/component/cta_line_bar.py | 41 ++++----- vnpy/data/tdx/future_contracts.json | 90 ++++++++++---------- vnpy/data/tdx/tdx_common.py | 72 ++++++++++++++++ vnpy/data/tdx/tdx_future_data.py | 3 +- vnpy/data/tdx/tdx_stock_data.py | 114 ++++++++++++++++---------- vnpy/gateway/ctp/ctp_gateway.py | 24 +++++- vnpy/trader/converter.py | 8 +- vnpy/trader/utility.py | 19 ++++- 14 files changed, 363 insertions(+), 147 deletions(-) diff --git a/Q_n_A.md b/Q_n_A.md index dab68975..807bd658 100644 --- a/Q_n_A.md +++ b/Q_n_A.md @@ -133,3 +133,26 @@ index-url=http://pypi.douban.com/simple [install] trusted-host=pypi.douban.com + +13. 升级anaconda + + + anaconda用法: + 查看已经安装的包: + pip list 或者 conda list + + 安装和更新: + pip install requests + pip install requests --upgrade + 或者 + conda install requests + conda update requests + + 更新所有库 + conda update --all + + 更新 conda 自身 + conda update conda + + 更新 anaconda 自身 + conda update anaconda diff --git a/vnpy/app/cta_crypto/engine.py b/vnpy/app/cta_crypto/engine.py index e0aebb0f..02617421 100644 --- a/vnpy/app/cta_crypto/engine.py +++ b/vnpy/app/cta_crypto/engine.py @@ -1238,7 +1238,7 @@ class CtaEngine(BaseEngine): try: # 5.保存策略切片 snapshot = strategy.get_klines_snapshot() - if len(snapshot) == 0: + if not snapshot: self.write_log(f'{strategy_name}返回得K线切片数据为空') return diff --git a/vnpy/app/cta_crypto/template.py b/vnpy/app/cta_crypto/template.py index beda0145..53d5ac51 100644 --- a/vnpy/app/cta_crypto/template.py +++ b/vnpy/app/cta_crypto/template.py @@ -789,6 +789,7 @@ class CtaFutureTemplate(CtaTemplate): self.write_log(f'剩余委托单号:{grid.order_ids}') + self.gt.save() # 在策略得活动订单中,移除 self.active_orders.pop(order.vt_orderid, None) diff --git a/vnpy/app/cta_strategy_pro/engine.py b/vnpy/app/cta_strategy_pro/engine.py index a7eb2055..d238fd7e 100644 --- a/vnpy/app/cta_strategy_pro/engine.py +++ b/vnpy/app/cta_strategy_pro/engine.py @@ -793,6 +793,10 @@ class CtaEngine(BaseEngine): def get_price(self, vt_symbol: str): """查询合约的最新价格""" + price = self.main_engine.get_price(vt_symbol) + if price: + return price + tick = self.main_engine.get_tick(vt_symbol) if tick: return tick.last_price @@ -819,6 +823,10 @@ class CtaEngine(BaseEngine): def get_position(self, vt_symbol: str, direction: Direction, gateway_name: str = ''): """ 查询合约在账号的持仓,需要指定方向""" + if len(gateway_name) == 0: + contract = self.main_engine.get_contract(vt_symbol) + if contract and contract.gateway_name: + gateway_name = contract.gateway_name vt_position_id = f"{gateway_name}.{vt_symbol}.{direction.value}" return self.main_engine.get_position(vt_position_id) @@ -1178,7 +1186,7 @@ class CtaEngine(BaseEngine): try: # 5.保存策略切片 snapshot = strategy.get_klines_snapshot() - if len(snapshot) == 0: + if not snapshot: self.write_log(f'{strategy_name}返回得K线切片数据为空') return diff --git a/vnpy/app/cta_strategy_pro/template.py b/vnpy/app/cta_strategy_pro/template.py index a5981116..d3f9f238 100644 --- a/vnpy/app/cta_strategy_pro/template.py +++ b/vnpy/app/cta_strategy_pro/template.py @@ -1246,6 +1246,8 @@ class CtaProFutureTemplate(CtaProTemplate): self.write_log(f'剩余委托单号:{grid.order_ids}') + self.gt.save() + # 在策略得活动订单中,移除 self.active_orders.pop(order.vt_orderid, None) @@ -1274,6 +1276,16 @@ class CtaProFutureTemplate(CtaProTemplate): old_order['traded'] = order.traded order_vt_symbol = copy(old_order['vt_symbol']) order_volume = old_order['volume'] - old_order['traded'] + + + order_price = old_order['price'] + order_type = old_order.get('order_type', OrderType.LIMIT) + order_retry = old_order.get('retry', 0) + grid = old_order.get('grid', None) + if grid: + if order.vt_orderid in grid.order_ids: + grid.order_ids.remove(order.vt_orderid) + if order_volume <= 0: msg = u'{} {}{}需重新开仓数量为{},不再开仓' \ .format(self.strategy_name, @@ -1286,10 +1298,6 @@ class CtaProFutureTemplate(CtaProTemplate): self.active_orders.pop(order.vt_orderid, None) return - order_price = old_order['price'] - order_type = old_order.get('order_type', OrderType.LIMIT) - order_retry = old_order.get('retry', 0) - grid = old_order.get('grid', None) if order_retry > 20: # 这里超过20次尝试失败后,不再尝试,发出告警信息 msg = u'{} {}/{}手, 重试开仓次数{}>20' \ @@ -1299,15 +1307,9 @@ class CtaProFutureTemplate(CtaProTemplate): order_retry) self.write_error(msg) self.send_wechat(msg) - - if grid: - if order.vt_orderid in grid.order_ids: - grid.order_ids.remove(order.vt_orderid) - - # 网格的所有委托单已经执行完毕 - if len(grid.order_ids) == 0: - grid.order_status = False - + # 网格的所有委托单已经执行完毕 + if len(grid.order_ids) == 0: + grid.order_status = False self.gt.save() self.write_log(u'网格信息更新:{}'.format(grid.__dict__)) @@ -1319,8 +1321,18 @@ class CtaProFutureTemplate(CtaProTemplate): # FAK 重新开单 if old_order['direction'] == Direction.LONG and order_type == OrderType.FAK: - # 更新网格交易器 + # 删除旧的委托记录 + self.write_log(u'移除旧的委托记录:{}'.format(order.vt_orderid)) + self.active_orders.pop(order.vt_orderid, None) + + if order.traded > 0: + old_traded_volume = grid.traded_volume + grid.traded_volume += order.traded + self.write_log(f'{grid.direction.value}单部分{order.offset}仓,' + + f'网格volume:{grid.volume}, traded_volume:{old_traded_volume}=>{grid.traded_volume}') + + # 更新网格交易器 self.write_log(u'FAK模式,需要重新发送buy委托.grid:{}'.format(grid.__dict__)) # 更新委托平仓价 buy_price = max(self.cur_mi_tick.ask_price_1, self.cur_mi_tick.last_price, order_price) + self.price_tick @@ -1350,11 +1362,18 @@ class CtaProFutureTemplate(CtaProTemplate): info.update({'retry': order_retry}) self.gt.save() + + + elif old_order['direction'] == Direction.SHORT and order_type == OrderType.FAK: # 删除旧的委托记录 self.write_log(u'移除旧的委托记录:{}'.format(order.vt_orderid)) self.active_orders.pop(order.vt_orderid, None) - elif old_order['direction'] == Direction.SHORT and order_type == OrderType.FAK: + if order.traded > 0: + old_traded_volume = grid.traded_volume + grid.traded_volume += order.traded + self.write_log(f'{grid.direction.value}单部分{order.offset}仓,' + + f'网格volume:{grid.volume}, traded_volume:{old_traded_volume}=>{grid.traded_volume}') self.write_log(u'FAK模式,需要重新发送short委托.grid:{}'.format(grid.__dict__)) short_price = min(self.cur_mi_tick.bid_price_1, self.cur_mi_tick.last_price, order_price) - self.price_tick @@ -1385,9 +1404,7 @@ class CtaProFutureTemplate(CtaProTemplate): info.update({'retry': order_retry}) self.gt.save() - # 删除旧的委托记录 - self.write_log(u'移除旧的委托记录:{}'.format(order.vt_orderid)) - self.active_orders.pop(order.vt_orderid, None) + else: pre_status = old_order.get('status', Status.NOTTRADED) old_order.update({'status': Status.CANCELLED}) @@ -1423,6 +1440,15 @@ class CtaProFutureTemplate(CtaProTemplate): # order_time = old_order['order_time'] order_vt_symbol = copy(old_order['vt_symbol']) order_volume = old_order['volume'] - old_order['traded'] + + order_price = old_order['price'] + order_type = old_order.get('order_type', OrderType.LIMIT) + order_retry = old_order.get('retry', 0) + grid = old_order.get('grid', None) + if grid: + if order.vt_orderid in grid.order_ids: + grid.order_ids.remove(order.vt_orderid) + if order_volume <= 0: msg = u'{} {}{}重新平仓数量为{},不再平仓' \ .format(self.strategy_name, order.vt_orderid, order_vt_symbol, order_volume) @@ -1432,20 +1458,15 @@ class CtaProFutureTemplate(CtaProTemplate): self.active_orders.pop(order.vt_orderid, None) return - order_price = old_order['price'] - order_type = old_order.get('order_type', OrderType.LIMIT) - order_retry = old_order.get('retry', 0) - grid = old_order.get('grid', None) + if order_retry > 20: msg = u'{} 平仓撤单 {}/{}手, 重试平仓次数{}>20' \ .format(self.strategy_name, order_vt_symbol, order_volume, order_retry) self.write_error(msg) self.send_wechat(msg) - if grid: - if order.vt_orderid in grid.order_ids: - grid.order_ids.remove(order.vt_orderid) - if not grid.order_ids: - grid.order_status = False + + if not grid.order_ids: + grid.order_status = False self.gt.save() self.write_log(u'更新网格=>{}'.format(grid.__dict__)) @@ -1456,6 +1477,14 @@ class CtaProFutureTemplate(CtaProTemplate): order_retry += 1 if old_order['direction'] == Direction.LONG and order_type == OrderType.FAK: + self.write_log(u'移除活动订单:{}'.format(order.vt_orderid)) + self.active_orders.pop(order.vt_orderid, None) + if order.traded > 0: + old_traded_volume = grid.traded_volume + grid.traded_volume += order.traded + self.write_log(f'{grid.direction.value}单部分{order.offset}仓,' + + f'网格volume:{grid.volume}, traded_volume:{old_traded_volume}=>{grid.traded_volume}') + self.write_log(u'FAK模式,需要重新发送cover委托.grid:{}'.format(grid.__dict__)) # 更新委托平仓价 cover_tick = self.tick_dict.get(order_vt_symbol, self.cur_mi_tick) @@ -1484,10 +1513,17 @@ class CtaProFutureTemplate(CtaProTemplate): info.update({'retry': order_retry}) self.gt.save() - self.write_log(u'移除活动订单:{}'.format(order.vt_orderid)) - self.active_orders.pop(order.vt_orderid, None) + elif old_order['direction'] == Direction.SHORT and order_type == OrderType.FAK: + self.write_log(u'移除活动订单:{}'.format(order.vt_orderid)) + self.active_orders.pop(order.vt_orderid, None) + if order.traded > 0: + old_traded_volume = grid.traded_volume + grid.traded_volume += order.traded + self.write_log(f'{grid.direction.value}单部分{order.offset}仓,' + + f'网格volume:{grid.volume}, traded_volume:{old_traded_volume}=>{grid.traded_volume}') + self.write_log(u'FAK模式,需要重新发送sell委托.grid:{}'.format(grid.__dict__)) sell_tick = self.tick_dict.get(order_vt_symbol, self.cur_mi_tick) sell_price = min(sell_tick.bid_price_1, sell_tick.last_price, order_price) - self.price_tick @@ -1518,9 +1554,6 @@ class CtaProFutureTemplate(CtaProTemplate): self.gt.save() - self.write_log(u'移除活动订单:{}'.format(order.vt_orderid)) - self.active_orders.pop(order.vt_orderid, None) - else: pre_status = old_order.get('status', Status.NOTTRADED) old_order.update({'status': Status.CANCELLED}) @@ -1583,6 +1616,8 @@ class CtaProFutureTemplate(CtaProTemplate): if order_grid: if vt_orderid in order_grid.order_ids: order_grid.order_ids.remove(vt_orderid) + if len(order_grid.order_ids) == 0: + order_grid.order_status = False continue # 处理状态为‘撤销’的委托单 diff --git a/vnpy/component/cta_grid_trade.py b/vnpy/component/cta_grid_trade.py index 2e45f25c..1cc50969 100644 --- a/vnpy/component/cta_grid_trade.py +++ b/vnpy/component/cta_grid_trade.py @@ -123,9 +123,9 @@ class CtaGrid(object): def to_str(self): """转换字符串显示内容""" - str = u'o:{}/{};c:{}/{},r:{}/opentime:{}/ordertime:{}' \ + str = u'o:{}/{};c:{}/{},r:{}/opentime:{}/ordertime:{},v:{}' \ .format(self.open_price, self.open_status, self.close_price, - self.close_status, self.order_ids, self.open_time, self.order_time) + self.close_status, self.order_ids, self.open_time, self.order_time, self.volume) if len(self.vt_symbol) > 0: return u'{} {}'.format(self.vt_symbol, str) else: diff --git a/vnpy/component/cta_line_bar.py b/vnpy/component/cta_line_bar.py index 311518fa..d67c60be 100644 --- a/vnpy/component/cta_line_bar.py +++ b/vnpy/component/cta_line_bar.py @@ -373,7 +373,7 @@ class CtaLineBar(object): self.para_boll2_len = 0 # 第二条布林的计算K线周期 self.para_boll2_tb_len = 0 # 第二跳布林的计算K线周期( 适用于TB的计算方式) - self.para_boll2_std_sate = 2 # 第二条布林标准差(缺省2倍) + self.para_boll2_std_rate = 2 # 第二条布林标准差(缺省2倍) self.para_kdj_len = 0 # KDJ指标的长度,缺省是9 self.para_kdj_tb_len = 0 # KDJ指标的长度,缺省是9 ( for TB) @@ -2059,9 +2059,9 @@ class CtaLineBar(object): boll2Len = min(self.bar_len, self.para_boll2_len) # 不包含当前最新的Bar - upper_list, middle_list, lower_list = ta.BBANDS(self.close_array[-2 * self.para_boll2_len], - timeperiod=boll2Len, nbdevup=self.para_boll2_std_sate, - nbdevdn=self.para_boll2_std_sate, matype=0) + upper_list, middle_list, lower_list = ta.BBANDS(self.close_array, + timeperiod=boll2Len, nbdevup=self.para_boll2_std_rate, + nbdevdn=self.para_boll2_std_rate, matype=0) if len(self.line_boll2_upper) > self.max_hold_bars: del self.line_boll2_upper[0] if len(self.line_boll2_middle) > self.max_hold_bars: @@ -2072,7 +2072,7 @@ class CtaLineBar(object): del self.line_boll2_std[0] # 1标准差 - std = (upper_list[-1] - lower_list[-1]) / (self.para_boll2_std_sate * 2) + std = (upper_list[-1] - lower_list[-1]) / (self.para_boll2_std_rate * 2) self.line_boll2_std.append(std) upper = round(upper_list[-1], self.round_n) @@ -2188,15 +2188,15 @@ class CtaLineBar(object): middle = np.mean(self.close_array[-2 * boll2Len:]) self.line_boll2_middle.append(middle) # 中轨 - self.cur_middle2 = middle - middle % self.price_tick # 中轨取整 + self.cur_middle2 = middle # 中轨取整 - upper = middle + self.para_boll2_std_sate * std + upper = middle + self.para_boll2_std_rate * std self.line_boll2_upper.append(upper) # 上轨 - self.cur_upper2 = upper - upper % self.price_tick # 上轨取整 + self.cur_upper2 = upper # 上轨取整 - lower = middle - self.para_boll2_std_sate * std + lower = middle - self.para_boll2_std_rate * std self.line_boll2_lower.append(lower) # 下轨 - self.cur_lower2 = lower - lower % self.price_tick # 下轨取整 + self.cur_lower2 = lower # 下轨取整 # 计算斜率 if len(self.line_boll2_upper) > 2 and self.line_boll2_upper[-2] != 0: @@ -2229,6 +2229,8 @@ class CtaLineBar(object): if not (boll_01_len > 0 or boll_02_len > 0): # 不计算 return + rt_close_array = np.append(self.close_array, [self.line_bar[-1].close_price]) + if boll_01_len > 0: if self.bar_len < min(14, boll_01_len) + 1: return @@ -2236,7 +2238,7 @@ class CtaLineBar(object): bollLen = min(boll_01_len, self.bar_len) if self.para_boll_tb_len == 0: - upper_list, middle_list, lower_list = ta.BBANDS(self.close_array[-bollLen:], + upper_list, middle_list, lower_list = ta.BBANDS(rt_close_array, timeperiod=bollLen, nbdevup=self.para_boll_std_rate, nbdevdn=self.para_boll_std_rate, matype=0) @@ -2247,8 +2249,8 @@ class CtaLineBar(object): self._rt_lower = round(lower_list[-1], self.round_n) else: # 1标准差 - std = np.std(np.append(self.close_array[-boll_01_len:], [self.line_bar[-1].close]), ddof=1) - middle = np.mean(np.append(self.close_array[-boll_01_len:], [self.line_bar[-1].close])) + std = np.std(rt_close_array[-boll_01_len:]) + middle = np.mean(rt_close_array[-boll_01_len:]) self._rt_middle = round(middle, self.round_n) upper = middle + self.para_boll_std_rate * std self._rt_upper = round(upper, self.round_n) @@ -2275,19 +2277,20 @@ class CtaLineBar(object): bollLen = min(boll_02_len, self.bar_len) if self.para_boll2_tb_len == 0: - upper_list, middle_list, lower_list = ta.BBANDS(self.close_array[-bollLen:], - timeperiod=bollLen, nbdevup=self.para_boll_std_rate, - nbdevdn=self.para_boll_std_rate, matype=0) + upper_list, middle_list, lower_list = ta.BBANDS( + rt_close_array, + timeperiod=bollLen, nbdevup=self.para_boll2_std_rate, + nbdevdn=self.para_boll2_std_rate, matype=0) # 1标准差 - std = (upper_list[-1] - lower_list[-1]) / (self.para_boll2_std_sate * 2) + std = (upper_list[-1] - lower_list[-1]) / (self.para_boll2_std_rate * 2) self._rt_upper2 = round(upper_list[-1], self.round_n) self._rt_middle2 = round(middle_list[-1], self.round_n) self._rt_lower2 = round(lower_list[-1], self.round_n) else: # 1标准差 - std = np.std(np.append(self.close_array[-boll_02_len:], [self.line_bar[-1].close]), ddof=1) - middle = np.mean(np.append(self.close_array[-boll_02_len:], [self.line_bar[-1].close])) + std = np.std(rt_close_array[-bollLen:], ddof=1) + middle = np.mean(rt_close_array[-bollLen:]) self._rt_middle2 = round(middle, self.round_n) upper = middle + self.para_boll_std_rate * std self._rt_upper2 = round(upper, self.round_n) diff --git a/vnpy/data/tdx/future_contracts.json b/vnpy/data/tdx/future_contracts.json index c2ea4d10..f25bbc6a 100644 --- a/vnpy/data/tdx/future_contracts.json +++ b/vnpy/data/tdx/future_contracts.json @@ -4,7 +4,7 @@ "mi_symbol": "a2005", "full_symbol": "A2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.06, "symbol_size": 10, "price_tick": 1.0 }, @@ -13,7 +13,7 @@ "mi_symbol": "ag2007", "full_symbol": "AG2007", "exchange": "SHFE", - "margin_rate": 0.08, + "margin_rate": 0.09, "symbol_size": 15, "price_tick": 1.0 }, @@ -31,7 +31,7 @@ "mi_symbol": "AP005", "full_symbol": "AP2005", "exchange": "CZCE", - "margin_rate": 0.07, + "margin_rate": 0.08, "symbol_size": 10, "price_tick": 1.0 }, @@ -67,7 +67,7 @@ "mi_symbol": "bu2006", "full_symbol": "BU2006", "exchange": "SHFE", - "margin_rate": 0.1, + "margin_rate": 0.11, "symbol_size": 10, "price_tick": 2.0 }, @@ -85,7 +85,7 @@ "mi_symbol": "CF009", "full_symbol": "CF2009", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 5.0 }, @@ -112,7 +112,7 @@ "mi_symbol": "cu2004", "full_symbol": "CU2004", "exchange": "SHFE", - "margin_rate": 0.09, + "margin_rate": 0.1, "symbol_size": 5, "price_tick": 10.0 }, @@ -121,7 +121,7 @@ "mi_symbol": "CY005", "full_symbol": "CY2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 5.0 }, @@ -130,7 +130,7 @@ "mi_symbol": "eb2005", "full_symbol": "EB2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.11, "symbol_size": 5, "price_tick": 1.0 }, @@ -139,7 +139,7 @@ "mi_symbol": "eg2005", "full_symbol": "EG2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.11, "symbol_size": 10, "price_tick": 1.0 }, @@ -148,9 +148,9 @@ "mi_symbol": "fb2005", "full_symbol": "FB2005", "exchange": "DCE", - "margin_rate": 0.2, - "symbol_size": 500, - "price_tick": 0.05 + "margin_rate": 0.1, + "symbol_size": 10, + "price_tick": 0.5 }, "FG": { "underlying_symbol": "FG", @@ -166,7 +166,7 @@ "mi_symbol": "fu2005", "full_symbol": "FU2005", "exchange": "SHFE", - "margin_rate": 0.2, + "margin_rate": 0.11, "symbol_size": 10, "price_tick": 1.0 }, @@ -175,7 +175,7 @@ "mi_symbol": "hc2005", "full_symbol": "HC2005", "exchange": "SHFE", - "margin_rate": 0.1, + "margin_rate": 0.09, "symbol_size": 10, "price_tick": 1.0 }, @@ -184,7 +184,7 @@ "mi_symbol": "i2005", "full_symbol": "I2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.08, "symbol_size": 100, "price_tick": 0.5 }, @@ -220,7 +220,7 @@ "mi_symbol": "j2005", "full_symbol": "J2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.08, "symbol_size": 100, "price_tick": 0.5 }, @@ -229,7 +229,7 @@ "mi_symbol": "jd2005", "full_symbol": "JD2005", "exchange": "DCE", - "margin_rate": 0.08, + "margin_rate": 0.09, "symbol_size": 10, "price_tick": 1.0 }, @@ -238,7 +238,7 @@ "mi_symbol": "jm2005", "full_symbol": "JM2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.08, "symbol_size": 60, "price_tick": 0.5 }, @@ -256,7 +256,7 @@ "mi_symbol": "l2005", "full_symbol": "L2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 5.0 }, @@ -274,7 +274,7 @@ "mi_symbol": "m2005", "full_symbol": "M2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.06, "symbol_size": 10, "price_tick": 1.0 }, @@ -301,7 +301,7 @@ "mi_symbol": "nr2004", "full_symbol": "NR2004", "exchange": "INE", - "margin_rate": 0.09, + "margin_rate": 0.11, "symbol_size": 10, "price_tick": 5.0 }, @@ -319,14 +319,14 @@ "mi_symbol": "p2005", "full_symbol": "P2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 10, "price_tick": 2.0 }, "PB": { "underlying_symbol": "PB", - "mi_symbol": "pb2003", - "full_symbol": "PB2003", + "mi_symbol": "pb2006", + "full_symbol": "PB2006", "exchange": "SHFE", "margin_rate": 0.1, "symbol_size": 5, @@ -346,7 +346,7 @@ "mi_symbol": "pp2005", "full_symbol": "PP2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 1.0 }, @@ -355,7 +355,7 @@ "mi_symbol": "rb2005", "full_symbol": "RB2005", "exchange": "SHFE", - "margin_rate": 0.1, + "margin_rate": 0.09, "symbol_size": 10, "price_tick": 1.0 }, @@ -373,7 +373,7 @@ "mi_symbol": "RM005", "full_symbol": "RM2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.06, "symbol_size": 10, "price_tick": 1.0 }, @@ -391,7 +391,7 @@ "mi_symbol": "RS011", "full_symbol": "RS2011", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.2, "symbol_size": 10, "price_tick": 1.0 }, @@ -400,7 +400,7 @@ "mi_symbol": "ru2005", "full_symbol": "RU2005", "exchange": "SHFE", - "margin_rate": 0.1, + "margin_rate": 0.11, "symbol_size": 10, "price_tick": 5.0 }, @@ -418,7 +418,7 @@ "mi_symbol": "sc2004", "full_symbol": "SC2004", "exchange": "INE", - "margin_rate": 0.05, + "margin_rate": 0.2, "symbol_size": 1000, "price_tick": 0.1 }, @@ -427,7 +427,7 @@ "mi_symbol": "SF005", "full_symbol": "SF2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 2.0 }, @@ -436,7 +436,7 @@ "mi_symbol": "SM005", "full_symbol": "SM2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 2.0 }, @@ -445,7 +445,7 @@ "mi_symbol": "sn2006", "full_symbol": "SN2006", "exchange": "SHFE", - "margin_rate": 0.09, + "margin_rate": 0.1, "symbol_size": 1, "price_tick": 10.0 }, @@ -454,7 +454,7 @@ "mi_symbol": "sp2005", "full_symbol": "SP2005", "exchange": "SHFE", - "margin_rate": 0.07, + "margin_rate": 0.08, "symbol_size": 10, "price_tick": 2.0 }, @@ -472,7 +472,7 @@ "mi_symbol": "ss2006", "full_symbol": "SS2006", "exchange": "SHFE", - "margin_rate": 0.08, + "margin_rate": 0.09, "symbol_size": 5, "price_tick": 5.0 }, @@ -490,14 +490,14 @@ "mi_symbol": "TA005", "full_symbol": "TA2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 5, "price_tick": 2.0 }, "TF": { "underlying_symbol": "TF", - "mi_symbol": "TF2003", - "full_symbol": "TF2003", + "mi_symbol": "TF2004", + "full_symbol": "TF2004", "exchange": "CFFEX", "margin_rate": 0.012, "symbol_size": 10000, @@ -526,7 +526,7 @@ "mi_symbol": "v2005", "full_symbol": "V2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.1, "symbol_size": 5, "price_tick": 5.0 }, @@ -535,7 +535,7 @@ "mi_symbol": "WH011", "full_symbol": "WH2011", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.07, "symbol_size": 20, "price_tick": 1.0 }, @@ -544,7 +544,7 @@ "mi_symbol": "wr2101", "full_symbol": "WR2101", "exchange": "SHFE", - "margin_rate": 0.2, + "margin_rate": 0.09, "symbol_size": 10, "price_tick": 1.0 }, @@ -553,7 +553,7 @@ "mi_symbol": "y2005", "full_symbol": "Y2005", "exchange": "DCE", - "margin_rate": 0.05, + "margin_rate": 0.06, "symbol_size": 10, "price_tick": 2.0 }, @@ -562,14 +562,14 @@ "mi_symbol": "ZC005", "full_symbol": "ZC2005", "exchange": "CZCE", - "margin_rate": 0.05, + "margin_rate": 0.06, "symbol_size": 100, "price_tick": 0.2 }, "ZN": { "underlying_symbol": "ZN", - "mi_symbol": "zn2003", - "full_symbol": "ZN2003", + "mi_symbol": "zn2005", + "full_symbol": "ZN2005", "exchange": "SHFE", "margin_rate": 0.1, "symbol_size": 5, diff --git a/vnpy/data/tdx/tdx_common.py b/vnpy/data/tdx/tdx_common.py index 3884c5e6..f55a973b 100644 --- a/vnpy/data/tdx/tdx_common.py +++ b/vnpy/data/tdx/tdx_common.py @@ -11,6 +11,20 @@ from vnpy.trader.utility import load_json, save_json # 期货的配置文件 TDX_FUTURE_CONFIG = 'tdx_future_config.json' # 股票的配置文件 +# 存储格式 dict{ +# "cache_time": datetime, +# "symbol_dict": { +# "symbol_marketid": { +# 'code', '395001', +# 'volunit', 100, +# 'decimal_point', 2, +# 'name', '主板A股', +# 'pre_close', 458.0, +# 'exchagne','SZSE', +# 'stock_type', 'index_cn', +# 'market_id', 0 +# } +# } } TDX_STOCK_CONFIG = 'tdx_stock_config.pkb2' @@ -112,6 +126,64 @@ def save_cache_json(data_dict: dict, json_file_name: str): save_json(filename=config_file_name, data=data_dict) +def get_stock_type(code): + """获取股票得分类""" + market_id = get_tdx_market_code(code) + + if market_id == 0: + return get_stock_type_sz(code) + else: + return get_stock_type_sh(code) + + +def get_stock_type_sz(code): + """深市代码分类 + Arguments: + code {[type]} -- [description] + Returns: + [type] -- [description] + """ + + if str(code)[0:2] in ['00', '30', '02']: + return 'stock_cn' + elif str(code)[0:2] in ['39']: + return 'index_cn' + elif str(code)[0:2] in ['15']: + return 'etf_cn' + elif str(code)[0:2] in ['10', '11', '13']: + # 10xxxx 国债现货 + # 11xxxx 债券 + # 12xxxx 国债回购 + return 'bond_cn' + elif str(code)[0:2] in ['12']: + # 12xxxx 可转换债券 + return 'cb_cn' + + elif str(code)[0:2] in ['20']: + return 'stockB_cn' + else: + return 'undefined' + + +def get_stock_type_sh(code): + if str(code)[0] == '6': + return 'stock_cn' + elif str(code)[0:3] in ['000', '880']: + return 'index_cn' + elif str(code)[0:2] == '51': + return 'etf_cn' + # 110×××120×××企业债券; + # 129×××100×××可转换债券; + elif str(code)[0:3] in ["009", "112", '120', "132", "204"]: + return 'bond_cn' + + elif str(code)[0:3] in ["110", "113", "121", "122", "126", + "130", "181", "190", "191", "192", "201", "202", "203"]: + return 'cb_cn' + else: + return 'undefined' + + class FakeStrategy(object): """制作一个假得策略,用于测试""" diff --git a/vnpy/data/tdx/tdx_future_data.py b/vnpy/data/tdx/tdx_future_data.py index 2b3d4078..9ceb37c5 100644 --- a/vnpy/data/tdx/tdx_future_data.py +++ b/vnpy/data/tdx/tdx_future_data.py @@ -241,6 +241,7 @@ class TdxFutureData(object): self.symbol_exchange_dict.update({tdx_symbol: Tdx_Vn_Exchange_Map.get(str(tdx_market_id))}) self.symbol_market_dict.update({tdx_symbol: tdx_market_id}) + # ---------------------------------------------------------------------- def get_bars(self, symbol: str, @@ -292,7 +293,7 @@ class TdxFutureData(object): end_date = end_dt if qry_start_date > end_date: qry_start_date = end_date - self.write_log('{}开始下载tdx:{} {}数据, {} to {}.' + self.write_log('{}开始下载tdx:{},周期类型:{}数据, {} to {}.' .format(datetime.now(), tdx_symbol, period, qry_start_date, end_date)) # print('{}开始下载tdx:{} {}数据, {} to {}.'.format(datetime.now(), tdx_symbol, tdx_period, last_date, end_date)) diff --git a/vnpy/data/tdx/tdx_stock_data.py b/vnpy/data/tdx/tdx_stock_data.py index 1ecdbb08..5d3c195e 100644 --- a/vnpy/data/tdx/tdx_stock_data.py +++ b/vnpy/data/tdx/tdx_stock_data.py @@ -28,6 +28,7 @@ from vnpy.data.tdx.tdx_common import ( get_tdx_market_code, get_cache_config, save_cache_config, + get_stock_type, TDX_STOCK_CONFIG) # 每个周期包含多少分钟 @@ -58,6 +59,8 @@ TDX_RQ_STOCK_MARKET_MAP = { RQ_TDX_STOCK_MARKET_MAP = {v: k for k, v in TDX_RQ_STOCK_MARKET_MAP.items()} +# 本地缓存文件 + class TdxStockData(object): def __init__(self, strategy=None): @@ -139,6 +142,12 @@ class TdxStockData(object): continue for security in security_list: tdx_symbol = security.get('code', None) + exchange = Exchange.SZSE.value if market_id == 0 else Exchange.SSE.value + stock_type = get_stock_type(tdx_symbol) + security.update({'market_id': market_id}) + security.update({'stock_type': stock_type}) + security.update({'exchange': exchange}) + if tdx_symbol: self.symbol_dict.update({f'{tdx_symbol}_{market_id}': security}) @@ -170,13 +179,21 @@ class TdxStockData(object): return results + def get_name(self, code, market_id): + symbol_info = self.symbol_dict.get(f'{code}_{market_id}') + if symbol_info: + return symbol_info.get('name', code) + + return code + # ---------------------------------------------------------------------- def get_bars(self, symbol: str, period: str, callback=None, bar_freq: int = 1, - start_dt: datetime = None): + start_dt: datetime = None, + return_bar: bool = True): """ 返回k线数据 symbol:股票 000001.XG @@ -192,14 +209,15 @@ class TdxStockData(object): if '.' in symbol: tdx_code, market_str = symbol.split('.') # 1, 上交所 , 0, 深交所 - market_code = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0 + market_id = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0 self.symbol_exchange_dict.update({tdx_code: symbol}) # tdx合约与vn交易所的字典 - self.symbol_market_dict.update({tdx_code: market_code}) # tdx合约与tdx市场的字典 + self.symbol_market_dict.update({tdx_code: market_id}) # tdx合约与tdx市场的字典 else: - market_code = get_tdx_market_code(symbol) + market_id = get_tdx_market_code(symbol) tdx_code = symbol self.symbol_exchange_dict.update({symbol: symbol}) # tdx合约与vn交易所的字典 - self.symbol_market_dict.update({symbol: market_code}) # tdx合约与tdx市场的字典 + self.symbol_market_dict.update({symbol: market_id}) # tdx合约与tdx市场的字典 + name = self.get_name(tdx_code, market_id) # period => tdx_period if period not in PERIOD_MAPPING.keys(): @@ -220,8 +238,8 @@ class TdxStockData(object): if qry_start_date > qry_end_date: qry_start_date = qry_end_date - self.write_log('{}开始下载tdx股票:{} {}数据, {} to {}.' - .format(datetime.now(), tdx_code, tdx_period, qry_start_date, qry_end_date)) + self.write_log('{}开始下载tdx股票: {},代码:{} {}数据, {} to {}.' + .format(datetime.now(), name, tdx_code, tdx_period, qry_start_date, qry_end_date)) try: _start_date = qry_end_date @@ -230,7 +248,7 @@ class TdxStockData(object): while _start_date > qry_start_date: _res = self.api.get_security_bars( category=PERIOD_MAPPING[period], - market=market_code, + market=market_id, code=tdx_code, start=_pos, count=QSIZE) @@ -273,43 +291,48 @@ class TdxStockData(object): data['time'] = data['datetime'].apply(lambda x: (x.strftime('%H:%M:%S'))) data = data.set_index('datetime', drop=False) - for index, row in data.iterrows(): - add_bar = BarData() - try: - add_bar.symbol = symbol - add_bar.datetime = index - add_bar.date = row['date'] - add_bar.time = row['time'] - add_bar.trading_date = row['trading_date'] - add_bar.open_price = float(row['open']) - add_bar.high_price = float(row['high']) - add_bar.low_price = float(row['low']) - add_bar.close_price = float(row['close']) - add_bar.volume = float(row['volume']) - except Exception as ex: - self.write_error('error when convert bar:{},ex:{},t:{}' - .format(row, str(ex), traceback.format_exc())) - # print('error when convert bar:{},ex:{},t:{}'.format(row, str(ex), traceback.format_exc())) - return False + if return_bar: + self.write_log('dataframe => [BarData]') + for index, row in data.iterrows(): + add_bar = BarData() + try: + add_bar.symbol = symbol + add_bar.datetime = index + add_bar.date = row['date'] + add_bar.time = row['time'] + add_bar.trading_date = row['trading_date'] + add_bar.open_price = float(row['open']) + add_bar.high_price = float(row['high']) + add_bar.low_price = float(row['low']) + add_bar.close_price = float(row['close']) + add_bar.volume = float(row['volume']) + except Exception as ex: + self.write_error('error when convert bar:{},ex:{},t:{}' + .format(row, str(ex), traceback.format_exc())) + # print('error when convert bar:{},ex:{},t:{}'.format(row, str(ex), traceback.format_exc())) + return False, ret_bars - if start_dt is not None and index < start_dt: - continue - ret_bars.append(add_bar) + if start_dt is not None and index < start_dt: + continue + ret_bars.append(add_bar) - if callback is not None: - freq = bar_freq - bar_is_completed = True - if period != '1min' and index == data['datetime'][-1]: - # 最后一个bar,可能是不完整的,强制修改 - # - 5min修改后freq基本正确 - # - 1day在VNPY合成时不关心已经收到多少Bar, 所以影响也不大 - # - 但其它分钟周期因为不好精确到每个品种, 修改后的freq可能有错 - if index > current_datetime: - bar_is_completed = False - # 根据秒数算的话,要+1,例如13:31,freq=31,第31根bar - freq = NUM_MINUTE_MAPPING[period] - int((index - current_datetime).total_seconds() / 60) - callback(add_bar, bar_is_completed, freq) + if callback is not None: + freq = bar_freq + bar_is_completed = True + if period != '1min' and index == data['datetime'][-1]: + # 最后一个bar,可能是不完整的,强制修改 + # - 5min修改后freq基本正确 + # - 1day在VNPY合成时不关心已经收到多少Bar, 所以影响也不大 + # - 但其它分钟周期因为不好精确到每个品种, 修改后的freq可能有错 + if index > current_datetime: + bar_is_completed = False + # 根据秒数算的话,要+1,例如13:31,freq=31,第31根bar + freq = NUM_MINUTE_MAPPING[period] - int((index - current_datetime).total_seconds() / 60) + callback(add_bar, bar_is_completed, freq) + else: + self.write_log('dataframe => [ dict ]') + ret_bars = list(data.T.to_dict().values()) return True, ret_bars except Exception as ex: self.write_error('exception in get:{},{},{}'.format(tdx_code, str(ex), traceback.format_exc())) @@ -400,6 +423,9 @@ class TdxStockData(object): self.symbol_exchange_dict.update({symbol: symbol}) # tdx合约与vn交易所的字典 self.symbol_market_dict.update({symbol: market_code}) # tdx合约与tdx市场的字典 + symbol_config = self.symbol_dict.get(f'{tdx_code}_{market_code}', {}) + decimal_point = symbol_config.get('decimal_point', 2) + q_size = QSIZE * 5 # 每秒 2个, 10小时 max_data_size = 1000000 @@ -462,6 +488,10 @@ class TdxStockData(object): last_dt = last_dt + timedelta(seconds=1) d.update({'datetime': last_dt}) d.update({'volume': d.pop('vol', 0)}) + if decimal_point > 2: + price = round(d.get('price') / (10 ** (decimal_point - 2)), decimal_point) + d.update({'price': price}) + d.update({'trading_date': last_dt.strftime('%Y-%m-%d')}) _datas = sorted(_datas, key=lambda s: s['datetime']) diff --git a/vnpy/gateway/ctp/ctp_gateway.py b/vnpy/gateway/ctp/ctp_gateway.py index 891cb904..5a2d2be0 100644 --- a/vnpy/gateway/ctp/ctp_gateway.py +++ b/vnpy/gateway/ctp/ctp_gateway.py @@ -693,10 +693,22 @@ class CtpTdApi(TdApi): symbol = data["InstrumentID"] exchange = symbol_exchange_map[symbol] + order_type = OrderType.LIMIT + if data["OrderPriceType"] == THOST_FTDC_OPT_LimitPrice and data["TimeCondition"] == THOST_FTDC_TC_IOC: + if data["VolumeCondition"] == THOST_FTDC_VC_AV: + order_type = OrderType.FAK + elif data["VolumeCondition"] == THOST_FTDC_VC_CV: + order_type = OrderType.FOK + + if data["OrderPriceType"] == THOST_FTDC_OPT_AnyPrice: + order_type = OrderType.MARKET + + order = OrderData( symbol=symbol, exchange=exchange, orderid=orderid, + type=order_type, direction=DIRECTION_CTP2VT[data["Direction"]], offset=OFFSET_CTP2VT.get(data["CombOffsetFlag"], Offset.NONE), price=data["LimitPrice"], @@ -913,12 +925,22 @@ class CtpTdApi(TdApi): order_ref = data["OrderRef"] orderid = f"{frontid}_{sessionid}_{order_ref}" + order_type = OrderType.LIMIT + if data["OrderPriceType"] == THOST_FTDC_OPT_LimitPrice and data["TimeCondition"] == THOST_FTDC_TC_IOC: + if data["VolumeCondition"] == THOST_FTDC_VC_AV: + order_type = OrderType.FAK + elif data["VolumeCondition"] == THOST_FTDC_VC_CV: + order_type = OrderType.FOK + + if data["OrderPriceType"] == THOST_FTDC_OPT_AnyPrice: + order_type = OrderType.MARKET + order = OrderData( symbol=symbol, exchange=exchange, orderid=orderid, sys_orderid=data.get('OrderSysID', ""), - type=ORDERTYPE_CTP2VT[data["OrderPriceType"]], + type=order_type, direction=DIRECTION_CTP2VT[data["Direction"]], offset=OFFSET_CTP2VT[data["CombOffsetFlag"]], price=data["LimitPrice"], diff --git a/vnpy/trader/converter.py b/vnpy/trader/converter.py index e9709286..f26868fd 100644 --- a/vnpy/trader/converter.py +++ b/vnpy/trader/converter.py @@ -25,8 +25,8 @@ class OffsetConverter: def update_position(self, position: PositionData) -> None: """""" - if not self.is_convert_required(position.vt_symbol): - return + # if not self.is_convert_required(position.vt_symbol): + # return holding = self.get_position_holding(position.vt_symbol, position.gateway_name) holding.update_position(position) @@ -57,6 +57,10 @@ class OffsetConverter: def get_position_holding(self, vt_symbol: str, gateway_name: str = '') -> "PositionHolding": """获取持仓信息""" + if len(gateway_name) == 0: + contract = self.main_engine.get_contract(vt_symbol) + if contract: + gateway_name = contract.gateway_name k = f'{gateway_name}.{vt_symbol}' holding = self.holdings.get(k, None) if not holding: diff --git a/vnpy/trader/utility.py b/vnpy/trader/utility.py index 3b67b3ca..40500d1c 100644 --- a/vnpy/trader/utility.py +++ b/vnpy/trader/utility.py @@ -9,7 +9,7 @@ import os import csv import re from pathlib import Path -from typing import Callable, Dict, Tuple, Union +from typing import Callable, Dict, Tuple, Union, Any from decimal import Decimal from math import floor, ceil from time import time @@ -17,6 +17,8 @@ from datetime import datetime, timedelta from functools import wraps, lru_cache import numpy as np import talib +import pickle +import bz2 from .object import BarData, TickData from .constant import Exchange, Interval @@ -615,6 +617,21 @@ def display_dual_axis(df, columns1, columns2=[], invert_yaxis1=False, invert_yax plt.show() +def load_data_from_pkb2(pkb2_file_name): + """获取本地压缩的数据""" + data = None + if not os.path.exists(pkb2_file_name): + return data + with bz2.BZ2File(pkb2_file_name, 'rb') as f: + data = pickle.load(f) + return data + +def save_data_to_pkb2(data: Any, pkb2_file_name): + """保存本地缓存的配置地址信息""" + with bz2.BZ2File(pkb2_file_name, 'wb') as f: + pickle.dump(data, f) + + class BarGenerator: """ For: