diff --git a/prod/jobs/refill_bao_stock_bars.py b/prod/jobs/refill_bao_stock_bars.py index 73573bea..03018119 100644 --- a/prod/jobs/refill_bao_stock_bars.py +++ b/prod/jobs/refill_bao_stock_bars.py @@ -20,7 +20,7 @@ import baostock as bs from vnpy.trader.constant import Exchange from vnpy.data.tdx.tdx_common import get_tdx_market_code from vnpy.trader.utility import load_json, get_csv_last_dt, extract_vt_symbol -from vnpy.data.stock.stock_base import get_stock_base +from vnpy.data.stock.stock_base import update_stock_base, get_stock_base # 保存的1分钟指数 bar目录 bar_data_folder = os.path.abspath(os.path.join(vnpy_root, 'bar_data')) @@ -34,6 +34,9 @@ if __name__ == "__main__": if login_msg.error_code != '0': print(f'证券宝登录错误代码:{login_msg.error_code}, 错误信息:{login_msg.error_msg}') + print('更新股票基本信息') + update_stock_base() + symbol_dict = get_stock_base() if len(sys.argv) >= 2 and sys.argv[1].lower() == 'all': stock_list = list(symbol_dict.keys()) @@ -43,7 +46,6 @@ if __name__ == "__main__": stock_list = load_json('stock_list.json') print('读取本地stock_list.json文件,共{}个'.format(len(stock_list))) - day_fields = "date,code,open,high,low,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,isST" min_fields = "date,time,code,open,high,low,close,volume,amount,adjustflag" @@ -74,7 +76,10 @@ if __name__ == "__main__": exchange_name = '深交所' exchange_code = 'sz' - symbol_info = symbol_dict.get(vt_symbol) + symbol_info = symbol_dict.get(vt_symbol,None) + if symbol_info is None: + print(f'找不到{vt_symbol}得配置信息', file=sys.stderr) + continue if symbol_info['类型'] == '指数': continue stock_name = symbol_info.get('name') diff --git a/requirements.txt b/requirements.txt index 28759e38..3aec27aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -six==1.13.0 +six PyQt5 pyqtgraph dataclasses; python_version<="3.6" @@ -8,7 +8,7 @@ websocket-client peewee mongoengine numpy -pandas==0.25.2 +pandas matplotlib seaborn futu-api diff --git a/vnpy/app/cta_strategy_pro/back_testing.py b/vnpy/app/cta_strategy_pro/back_testing.py index 6139e8fe..0b5b5579 100644 --- a/vnpy/app/cta_strategy_pro/back_testing.py +++ b/vnpy/app/cta_strategy_pro/back_testing.py @@ -1945,7 +1945,7 @@ class BackTestingEngine(object): self.daily_max_drawdown_rate = drawdown_rate self.max_drawdown_rate_time = data['date'] - msg = u'{}: net={}, capital={} max={} margin={} commission={}, pos: {}' \ + msg = u'{}: net={}, capital={} max={} holding_profit={} commission={}, pos: {}' \ .format(data['date'], data['net'], c, m, today_holding_profit, diff --git a/vnpy/app/cta_strategy_pro/portfolio_testing.py b/vnpy/app/cta_strategy_pro/portfolio_testing.py index 68e2b9fd..ec76641b 100644 --- a/vnpy/app/cta_strategy_pro/portfolio_testing.py +++ b/vnpy/app/cta_strategy_pro/portfolio_testing.py @@ -11,6 +11,7 @@ import sys import os import gc import pandas as pd +import numpy as np import traceback import random import bz2 @@ -425,7 +426,10 @@ class PortfolioTestingEngine(BackTestingEngine): last_price=tick_data['price'], volume=tick_data['volume'] ) - + if not isinstance(tick.last_price,float): + continue + if np.isnan(tick.last_price): + continue self.new_tick(tick) # 结束一个交易日后,更新每日净值 diff --git a/vnpy/app/cta_strategy_pro/spread_testing.py b/vnpy/app/cta_strategy_pro/spread_testing.py index 2d2a06b7..c2ad0846 100644 --- a/vnpy/app/cta_strategy_pro/spread_testing.py +++ b/vnpy/app/cta_strategy_pro/spread_testing.py @@ -11,6 +11,7 @@ import sys import os import gc import pandas as pd +import numpy as np import traceback import bz2 @@ -426,6 +427,9 @@ class SpreadTestingEngine(BackTestingEngine): bid_volume_1=int(tick_data['bid_volume_1']) ) + if np.isnan(tick.ask_price_1) or np.isnan(tick.bid_price_1): + continue + self.new_tick(tick) # 结束一个交易日后,更新每日净值 diff --git a/vnpy/app/cta_strategy_pro/template_spread.py b/vnpy/app/cta_strategy_pro/template_spread.py index bd16bed1..aa8f9f80 100644 --- a/vnpy/app/cta_strategy_pro/template_spread.py +++ b/vnpy/app/cta_strategy_pro/template_spread.py @@ -63,6 +63,12 @@ class CtaSpreadTemplate(CtaTemplate): self.klines = {} # K线组件字典: kline_name: kline self.cur_datetime = None # 当前Tick时间 + self.cur_mi_tick = None # 最新的主力合约tick( vt_symbol) + self.cur_99_tick = None # 最新得指数合约tick( idx_symbol) + + self.cur_mi_price = None # 当前价(主力合约 vt_symbol) + self.cur_99_price = None # 当前价(tick时,根据tick更新,onBar回测时,根据bar.close更新) + self.cur_act_tick = None # 最新的主动腿合约tick( act_vt_symbol) self.cur_pas_tick = None # 最新得被动腿合约tick( pas_vt_symbol) self.cur_spd_tick = None # 价差tick @@ -124,7 +130,6 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(u'保存k线缓存数据') self.save_klines_to_cache() - def save_klines_to_cache(self, kline_names: list = []): """ 保存K线数据到缓存 @@ -748,8 +753,8 @@ class CtaSpreadTemplate(CtaTemplate): 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__)) @@ -776,7 +781,7 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(f'{order_vt_symbol}涨停,不做buy') return - # 发送委托 + # FAK发送委托追单 vt_orderids = self.buy(price=buy_price, volume=order_volume, vt_symbol=order_vt_symbol, @@ -855,8 +860,8 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(f'移除grid中order_ids:{order.vt_orderid}') 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.active_orders.update({order.vt_orderid: old_order}) @@ -872,7 +877,7 @@ class CtaSpreadTemplate(CtaTemplate): return if not self.trading: - self.write_error(u'当前不允许交易') + self.write_error(f'{self.cur_datetime} 当前不允许交易') return # 直接更新“未完成委托单”,更新volume,Retry次数 @@ -906,8 +911,8 @@ class CtaSpreadTemplate(CtaTemplate): if order.vt_orderid in grid.order_ids: self.write_log(f'移除grid中order_ids:{order.vt_orderid}') 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__)) @@ -1001,8 +1006,8 @@ class CtaSpreadTemplate(CtaTemplate): if order.vt_orderid in grid.order_ids: self.write_log(f'移除grid中order_ids:{order.vt_orderid}') 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.active_orders.update({order.vt_orderid: old_order}) @@ -1047,15 +1052,17 @@ class CtaSpreadTemplate(CtaTemplate): self.active_orders.update({vt_orderid: order_info}) ret = self.cancel_order(str(vt_orderid)) if not ret: - self.write_log(u'撤单失败,更新状态为撤单成功') + self.write_log(f'{vt_orderid}撤单失败,更新状态为撤单成功') order_info.update({'status': Status.CANCELLED}) self.active_orders.update({vt_orderid: order_info}) else: + self.write_log(f'{vt_orderid}撤单成功') if order_grid: if vt_orderid in order_grid.order_ids: + self.write_log(f'{vt_orderid}存在网格委托队列{order_grid.order_ids}中,移除') order_grid.order_ids.remove(vt_orderid) - if len(order_grid.order_ids) == 0: - order_grid.order_status = False + #if len(order_grid.order_ids) == 0: + # order_grid.order_status = False continue # 处理状态为‘撤销’的委托单 @@ -1234,10 +1241,10 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(u'停止状态,不开仓') return [] if not self.allow_trading_open: - self.write_log(u'不允许开仓') + self.write_log(f'{self.cur_datetime}不允许开仓') return [] if self.force_trading_close: - self.write_log(u'强制平仓日,不开仓') + self.write_log(f'{self.cur_datetime}强制平仓日,不开仓') return [] # 检查流动性缺失 if not self.check_liquidity( direction=Direction.SHORT, @@ -1266,7 +1273,7 @@ class CtaSpreadTemplate(CtaTemplate): f'委托价:{self.cur_act_tick.bid_price_1}') return [] - # 开多被动腿 + # 开多被动腿(FAK或者限价单) pas_vt_orderids = self.buy(vt_symbol=self.pas_vt_symbol, lock=self.pas_exchange==Exchange.CFFEX, price=self.cur_pas_tick.ask_price_1, @@ -1303,10 +1310,10 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(u'停止状态,不开仓') return [] if not self.allow_trading_open: - self.write_log(u'不允许开仓') + self.write_log(f'{self.cur_datetime}不允许开仓') return [] if self.force_trading_close: - self.write_log(u'强制平仓日,不开仓') + self.write_log(f'{self.cur_datetime}强制平仓日,不开仓') return [] # 检查流动性缺失 if not self.check_liquidity( @@ -1324,7 +1331,7 @@ class CtaSpreadTemplate(CtaTemplate): self.write_log(u'价差{}不满足:{}'.format(self.cur_spd_tick.bid_price_1, grid.open_price)) return [] - # 开多主动腿 + # 开多主动腿(FAK 或者限价单) act_vt_orderids = self.buy(vt_symbol=self.act_vt_symbol, lock=self.act_exchange==Exchange.CFFEX, price=self.cur_act_tick.ask_price_1, diff --git a/vnpy/app/cta_strategy_pro/ui/widget.py b/vnpy/app/cta_strategy_pro/ui/widget.py index 7e32d061..e2301f6d 100644 --- a/vnpy/app/cta_strategy_pro/ui/widget.py +++ b/vnpy/app/cta_strategy_pro/ui/widget.py @@ -168,7 +168,7 @@ class StrategyManager(QtWidgets.QFrame): """ def __init__( - self, cta_manager: CtaManager, cta_engine: CtaEngine, data: dict + self, cta_manager: CtaManager, cta_engine: CtaEngine, data: dict ): """""" super(StrategyManager, self).__init__() @@ -300,6 +300,7 @@ class StrategyManager(QtWidgets.QFrame): tns_csv = os.path.abspath(os.path.join(self.cta_engine.get_data_path(), f'{self.strategy_name}_tns.csv')) ui_snapshot.show(snapshot_file="", d=snapshot, trade_file=trade_csv, tns_file=tns_csv) + class DataMonitor(QtWidgets.QTableWidget): """ Table monitor for parameters and variables. @@ -408,7 +409,7 @@ class SettingEditor(QtWidgets.QDialog): """ def __init__( - self, parameters: dict, strategy_name: str = "", class_name: str = "" + self, parameters: dict, strategy_name: str = "", class_name: str = "" ): """""" super(SettingEditor, self).__init__() @@ -477,7 +478,7 @@ class SettingEditor(QtWidgets.QDialog): try: value = type_(value_text) except Exception as ex: - print(f'{name}数据类型转换未指定') + print(f'{name}数据类型转换未指定:{str(ex)}') if isnumber(value_text): value = float(value_text) elif value_text == 'None': @@ -489,6 +490,7 @@ class SettingEditor(QtWidgets.QDialog): return setting + def isnumber(aString): try: float(aString) diff --git a/vnpy/app/risk_manager/ui/widget.py b/vnpy/app/risk_manager/ui/widget.py index 38b9c96b..439a1c37 100644 --- a/vnpy/app/risk_manager/ui/widget.py +++ b/vnpy/app/risk_manager/ui/widget.py @@ -40,7 +40,6 @@ class RiskManager(QtWidgets.QDialog): self.trade_hold_active_limit_spin = RiskManagerSpinBox() self.trade_hold_percent_limit_spin = RiskManagerSpinBox() - save_button = QtWidgets.QPushButton("保存") save_button.clicked.connect(self.save_setting) @@ -57,7 +56,7 @@ class RiskManager(QtWidgets.QDialog): form.addRow("激活废单/撤单(笔)", self.ratio_active_limit_spin) form.addRow("废单比上限(%)", self.reject_limit_percent_spin) form.addRow("撤单比上限(%)", self.cancel_limit_percent_spin) - form.addRow("激活成交/持仓比阈值(笔)" ,self.trade_hold_active_limit_spin) + form.addRow("激活成交/持仓比阈值(笔)", self.trade_hold_active_limit_spin) form.addRow("成交/持仓比上限(%)", self.trade_hold_percent_limit_spin) form.addRow(save_button) diff --git a/vnpy/data/renko/config.py b/vnpy/data/renko/config.py index 548f299b..0904b419 100644 --- a/vnpy/data/renko/config.py +++ b/vnpy/data/renko/config.py @@ -5,3 +5,5 @@ HEIGHT_LIST = [3, 5, 10, 'K3', 'K5', 'K10'] FUTURE_RENKO_DB_NAME = 'FutureRenko' STOCK_RENKO_DB_NAME = 'StockRenko' +CRYPTO_RENKO_DB_NAME= 'CryptoRenko' + diff --git a/vnpy/data/stock/stock_base.py b/vnpy/data/stock/stock_base.py index 849ecb3d..9c292295 100644 --- a/vnpy/data/stock/stock_base.py +++ b/vnpy/data/stock/stock_base.py @@ -26,6 +26,17 @@ stock_type_map = { } STOCK_BASE_FILE = 'stock_base.pkb2' +# get_stock_base 返回数据格式 +# vt_symbol: { +# 'exchange': 交易所代码 +# 'code': 股票代码 +# 'name': 中文名 +# 'ipo_date': 上市日期 +# 'out_date': 退市日期 +# '类型': 股票,指数,其他 +# 'type': stock_cn, index_cn,etf_cn,bond_cn,cb_cn +# 'status': '上市' '退市' +# } def get_stock_base(): """ 获取股票基础信息""" diff --git a/vnpy/data/tdx/tdx_stock_data.py b/vnpy/data/tdx/tdx_stock_data.py index ff1fd700..c16bae10 100644 --- a/vnpy/data/tdx/tdx_stock_data.py +++ b/vnpy/data/tdx/tdx_stock_data.py @@ -46,7 +46,6 @@ NUM_MINUTE_MAPPING['1day'] = 60 * 5.5 # 股票,收盘时间是15:00,开 # 常量 QSIZE = 800 - # 通达信 <=> 交易所代码 映射 TDX_VN_STOCK_MARKET_MAP = { TDXParams.MARKET_SH: Exchange.SSE, # 1: 上交所 @@ -76,7 +75,7 @@ class TdxStockData(object): self.proxy_ip = proxy_ip self.proxy_port = proxy_port - if self.proxy_port == 0 and len(self.proxy_ip)==0: + if self.proxy_port == 0 and len(self.proxy_ip) == 0: proxy_config = get_cache_json(TDX_PROXY_CONFIG) proxy_ip = proxy_config.get('proxy_ip', '') proxy_port = proxy_config.get('proxy_port', 0) @@ -388,16 +387,16 @@ class TdxStockData(object): cache_date: str): """加载缓存数据""" if not os.path.exists(cache_folder): - #self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder)) + # self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder)) return None cache_folder_year_month = os.path.join(cache_folder, cache_date[:6]) if not os.path.exists(cache_folder_year_month): - #self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder_year_month)) + # self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder_year_month)) return None cache_file = os.path.join(cache_folder_year_month, '{}_{}.pkb2'.format(cache_symbol, cache_date)) if not os.path.isfile(cache_file): - #self.write_error('缓存文件:{}不存在,不能读取'.format(cache_file)) + # self.write_error('缓存文件:{}不存在,不能读取'.format(cache_file)) return None with bz2.BZ2File(cache_file, 'rb') as f: data = pickle.load(f) @@ -531,8 +530,10 @@ class TdxStockData(object): self.connect() data = pd.concat( - [pd.concat([self.api.to_df(self.api.get_security_list(j, i * 1000)).assign(sse='sz' if j == 0 else 'sh').set_index( - ['code', 'sse'], drop=False) for i in range(int(self.api.get_security_count(j) / 1000) + 1)], axis=0) for j + [pd.concat( + [self.api.to_df(self.api.get_security_list(j, i * 1000)).assign(sse='sz' if j == 0 else 'sh').set_index( + ['code', 'sse'], drop=False) for i in range(int(self.api.get_security_count(j) / 1000) + 1)], + axis=0) for j in range(2)], axis=0) sz = data.query('sse=="sz"') sh = data.query('sse=="sh"') @@ -549,7 +550,7 @@ class TdxStockData(object): hq_codelist.append( { "code": row['code'], - "exchange": Exchange.SSE.value if row['sse'] == 'sh' else Exchange.SZSE.value, + "exchange": Exchange.SSE.value if row['sse'] == 'sh' else Exchange.SZSE.value, "market_id": 1 if row['sse'] == 'sh' else 0, "name": row['name'] @@ -575,12 +576,13 @@ class TdxStockData(object): def get_stock_quotes_by_type(self, stock_type): """根据股票代码类型,获取其最新行情""" - stock_list = [(stock.get('market_id'), stock.get('code')) for stock in self.symbol_dict.values() if stock.get('stock_type') == stock_type] + stock_list = [(stock.get('market_id'), stock.get('code')) for stock in self.symbol_dict.values() if + stock.get('stock_type') == stock_type] num_per_count = 60 results = [] - for i in range(0, len(stock_list)+1, num_per_count): - cur_results = self.get_security_quotes(stock_list[i:i+num_per_count]) + for i in range(0, len(stock_list) + 1, num_per_count): + cur_results = self.get_security_quotes(stock_list[i:i + num_per_count]) results.extend(cur_results) return results diff --git a/vnpy/data/tdx/test_tdx_future.py b/vnpy/data/tdx/test_tdx_future.py index d2201312..58351405 100644 --- a/vnpy/data/tdx/test_tdx_future.py +++ b/vnpy/data/tdx/test_tdx_future.py @@ -18,12 +18,12 @@ t2 = FakeStrategy() api_01 = TdxFutureData(strategy=t1) # 获取所有市场信息 -markets = api_01.get_markets() -str_markets = json.dumps(markets, indent=1, ensure_ascii=False) -print(u'{}'.format(str_markets)) +#markets = api_01.get_markets() +#str_markets = json.dumps(markets, indent=1, ensure_ascii=False) +#print(u'{}'.format(str_markets)) # 获取所有的期货合约明细 -api_01.qry_instrument() +#api_01.qry_instrument() # 获取某个合约得最新价 #price = api_01.get_price('rb2010') @@ -64,8 +64,8 @@ corr_rate = round(abs(corr.iloc[0, 1]) * 100, 2) # api_01.get_bars('IF99', period='1min', callback=t1.display_bar, bar_freq=1) # 获取bar,只返回 list[dict] -""" -result, bars = api_01.get_bars('IF99', period='1min', return_bar=False) + +result, bars = api_01.get_bars('SA2101', period='1min', return_bar=False) if result: print('前十根bar') for bar in bars[0:10]: @@ -73,15 +73,15 @@ if result: print('后十根bar') for bar in bars[-10:]: print(bar) -""" + # result,datas = api_01.get_transaction_data(symbol='ni1905') # api_02 = TdxFutureData(t2) # api_02.get_bars('IF99', period='1min', callback=t1.display_bar) # 获取当前交易日分时数据 -ret,result = api_01.get_transaction_data('NI99') -for r in result[0:10] + result[-10:]: - print(r) +#ret,result = api_01.get_transaction_data('NI99') +#for r in result[0:10] + result[-10:]: +# print(r) # 获取历史分时数据 # ret, result = api_01.get_history_transaction_data('RB99', '20190109') diff --git a/vnpy/data/tq/downloader.py b/vnpy/data/tq/downloader.py index 0849aee0..1d6920ed 100644 --- a/vnpy/data/tq/downloader.py +++ b/vnpy/data/tq/downloader.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -#__author__ = 'yangyang' +# __author__ = 'yangyang' # 修改: # 1, 输入单个合约时,标题不再扩展为 合约.标题 # 2. 下载tick时,5档行情都下载 @@ -74,11 +74,13 @@ class DataDownloader: if isinstance(start_dt, datetime): self._start_dt_nano = int(start_dt.timestamp() * 1e9) else: - self._start_dt_nano = _get_trading_day_start_time(int(datetime(start_dt.year, start_dt.month, start_dt.day).timestamp()) * 1000000000) + self._start_dt_nano = _get_trading_day_start_time( + int(datetime(start_dt.year, start_dt.month, start_dt.day).timestamp()) * 1000000000) if isinstance(end_dt, datetime): self._end_dt_nano = int(end_dt.timestamp() * 1e9) else: - self._end_dt_nano = _get_trading_day_end_time(int(datetime(end_dt.year, end_dt.month, end_dt.day).timestamp()) * 1000000000) + self._end_dt_nano = _get_trading_day_end_time( + int(datetime(end_dt.year, end_dt.month, end_dt.day).timestamp()) * 1000000000) self._current_dt_nano = self._start_dt_nano self._symbol_list = symbol_list if isinstance(symbol_list, list) else [symbol_list] # 检查合约代码是否存在 @@ -121,7 +123,7 @@ class DataDownloader: "focus_datetime": self._start_dt_nano, "focus_position": 0, } - if len(self._symbol_list) ==1: + if len(self._symbol_list) == 1: single_exchange, single_symbol = self._symbol_list[0].split('.') else: single_exchange, single_symbol = None, None @@ -132,7 +134,7 @@ class DataDownloader: csv_header = [] data_cols = ["open", "high", "low", "close", "volume", "open_oi", "close_oi"] if self._dur_nano != 0 else \ ["last_price", "highest", "lowest", "volume", - "amount", "open_interest","upper_limit","lower_limit", + "amount", "open_interest", "upper_limit", "lower_limit", "bid_price1", "bid_volume1", "ask_price1", "ask_volume1", "bid_price2", "bid_volume2", "ask_price2", "ask_volume2", "bid_price3", "bid_volume3", "ask_price3", "ask_volume3", diff --git a/vnpy/data/tq/tianqin_data.py b/vnpy/data/tq/tianqin_data.py index 333b9ad8..f1124323 100644 --- a/vnpy/data/tq/tianqin_data.py +++ b/vnpy/data/tq/tianqin_data.py @@ -28,15 +28,16 @@ import csv # pd.pandas.reset_option(‘参数名’, 参数值) # 恢复默认相关选项 tick_csv_header = [ - "datetime","symbol", "exchange", "last_price","highest","lowest","volume","amount","open_interest", - "upper_limit","lower_limit","bid_price1","bid_volume1","ask_price1", - "ask_volume1","bid_price2","bid_volume2","ask_price2","ask_volume2", - "bid_price3","bid_volume3","ask_price3","ask_volume3","bid_price4", + "datetime", "symbol", "exchange", "last_price", "highest", "lowest", "volume", "amount", "open_interest", + "upper_limit", "lower_limit", "bid_price1", "bid_volume1", "ask_price1", + "ask_volume1", "bid_price2", "bid_volume2", "ask_price2", "ask_volume2", + "bid_price3", "bid_volume3", "ask_price3", "ask_volume3", "bid_price4", "bid_volume4", - "ask_price4","ask_volume4", - "bid_price5","bid_volume5","ask_price5","ask_volume5" + "ask_price4", "ask_volume4", + "bid_price5", "bid_volume5", "ask_price5", "ask_volume5" ] + @lru_cache(maxsize=9999) def to_vt_symbol(tq_symbol: str) -> str: """""" @@ -95,8 +96,8 @@ def generate_tick_from_dict(vt_symbol: str, data: dict) -> TickData: volume=int(data["volume"]), open_interest=data["open_interest"], last_price=float(data["last_price"]), - #limit_up=float(data["upper_limit"]) if data["upper_limit"] !='#N/A' else None, - #limit_down=float(data["lower_limit"]), + # limit_up=float(data["upper_limit"]) if data["upper_limit"] !='#N/A' else None, + # limit_down=float(data["lower_limit"]), high_price=float(data["highest"]), low_price=float(data["lowest"]), bid_price_1=float(data["bid_price1"]), @@ -126,7 +127,7 @@ def generate_tick_from_dict(vt_symbol: str, data: dict) -> TickData: class TqFutureData(): def __init__(self, strategy=None): - self.strategy = strategy # 传进来策略实例,这样可以写日志到策略实例 + self.strategy = strategy # 传进来策略实例,这样可以写日志到策略实例 self.api = TqApi(TqSim(), url="wss://u.shinnytech.com/t/md/front/mobile") @@ -139,7 +140,7 @@ class TqFutureData(): with closing(self.api): # 获得 pp2009 tick序列的引用 ticks = self.api.get_tick_serial(symbol=tq_symbol, data_length=8964) # 每个序列最大支持请求 8964 个数据 - return ticks # 8964/3/60=49.8分钟 + return ticks # 8964/3/60=49.8分钟 except Exception as ex: print(u'获取历史tick数据出错:{},{}'.format(str(ex), traceback.format_exc())) return None @@ -148,7 +149,7 @@ class TqFutureData(): symbol, exchange = extract_vt_symbol(vt_symbol) tq_symbol = to_tq_symbol(symbol, exchange) - td = DataDownloader(self.api, symbol_list=tq_symbol, dur_sec=0, # Tick数据为dur_sec=0 + td = DataDownloader(self.api, symbol_list=tq_symbol, dur_sec=0, # Tick数据为dur_sec=0 start_dt=start_date, end_dt=end_date, csv_file_name=cache_file) @@ -182,7 +183,7 @@ class TqFutureData(): with open(file=ticks_file, mode='r', encoding='utf-8', ) as f: reader = csv.DictReader(f=f, fieldnames=tick_csv_header, delimiter=",") for row in reader: - if str(row.get('last_price','nan')) not in['nan','last_price']: + if str(row.get('last_price', 'nan')) not in ['nan', 'last_price']: tick_dict_list.append(row) return tick_dict_list @@ -191,7 +192,7 @@ class TqFutureData(): return [] - def get_bars(self, vt_symbol: str, start_date: datetime=None, end_date: datetime = None): + def get_bars(self, vt_symbol: str, start_date: datetime = None, end_date: datetime = None): """ 获取历史bar(受限于最大长度8964根bar) :param vt_symbol: @@ -235,7 +236,6 @@ class TqFutureData(): return bars - def get_ticks(self, vt_symbol: str, start_date: datetime, end_date: datetime = None): """获取历史tick""" @@ -253,7 +253,7 @@ class TqFutureData(): all_ticks = [] # 轮询每一天,读取缓存数据 - for n in range(n_days+1): + for n in range(n_days + 1): trading_date = start_date + timedelta(days=n) if trading_date.isoweekday() in [6, 7]: continue @@ -272,7 +272,7 @@ class TqFutureData(): all_ticks.extend(rt_ticks) return all_ticks - def get_runtime_ticks(self, vt_symbol: str, begin_dt: datetime= None): + def get_runtime_ticks(self, vt_symbol: str, begin_dt: datetime = None): """获取实时历史tick""" self.write_log(f"从天勤请求合约:{vt_symbol}的实时的8964条tick数据") symbol, exchange = extract_vt_symbol(vt_symbol) @@ -290,7 +290,7 @@ class TqFutureData(): 'bid_volume13', 'ask_price4', 'ask_volume14', 'bid_price4', 'bid_volume14', 'ask_price5', 'ask_volume15', 'bid_price5', 'bid_volume15', 'volume', 'amount', 'open_interest', 'symbol', 'duration'] - df.drop(['id','average','duration'], axis=1) + df.drop(['id', 'average', 'duration'], axis=1) for index, row in df.iterrows(): # 日期时间, 成交价, 成交量, 总量, 属性(持仓增减), B1价, B1量, B2价, B2量, B3价, B3量, S1价, S1量, S2价, S2量, S3价, S3量, BS @@ -341,18 +341,14 @@ if __name__ == '__main__': # tqsdk = Query_tqsdk_data(strategy=self) # 在策略中使用 tqsdk = TqFutureData() # ticks = tqsdk.query_tick_current("pp2009.DCE") - #tick_df = tqsdk.query_tick_history_data(vt_symbol="ni2009.SHFE", start_date=pd.to_datetime("2020-07-22")) - #print(tick_df) + # tick_df = tqsdk.query_tick_history_data(vt_symbol="ni2009.SHFE", start_date=pd.to_datetime("2020-07-22")) + # print(tick_df) - #ticks = tqsdk.get_runtime_ticks("ni2009.SHFE") + # ticks = tqsdk.get_runtime_ticks("ni2009.SHFE") - #print(ticks[0]) + # print(ticks[0]) - #print(ticks[-1]) + # print(ticks[-1]) bars = tqsdk.get_bars(vt_symbol='ni2011.SHFE') print(bars[0]) print(bars[-1]) - - - - diff --git a/vnpy/gateway/binancef/binancef_gateway.py b/vnpy/gateway/binancef/binancef_gateway.py index 99b3dda1..e766d45e 100644 --- a/vnpy/gateway/binancef/binancef_gateway.py +++ b/vnpy/gateway/binancef/binancef_gateway.py @@ -573,7 +573,7 @@ class BinancefRestApi(RestClient): self.cache_position_symbols.update({position.symbol: position.volume}) self.gateway.on_position(position) - #if position.symbol == 'BTCUSDT': + # if position.symbol == 'BTCUSDT': # self.gateway.write_log(f'{position.__dict__}\n {d}') # self.gateway.write_log("持仓信息查询成功") diff --git a/vnpy/gateway/ctp/ctp_gateway.py b/vnpy/gateway/ctp/ctp_gateway.py index 50e8fb86..3409f91a 100644 --- a/vnpy/gateway/ctp/ctp_gateway.py +++ b/vnpy/gateway/ctp/ctp_gateway.py @@ -180,6 +180,7 @@ TQ2VT_TYPE = { "OPTION": Product.OPTION, } + @lru_cache(maxsize=9999) def vt_to_tq_symbol(symbol: str, exchange: Exchange) -> str: """ @@ -432,12 +433,12 @@ class CtpGateway(BaseGateway): self.write_log(f'使用RabbitMQ接口订阅{req.symbol}') self.rabbit_api.subscribe(req) elif self.tq_api: - self.write_log(f'使用天勤接口订阅{ req.symbol}') + self.write_log(f'使用天勤接口订阅{req.symbol}') self.tq_api.subscribe(req) else: # 上期所、上能源支持五档行情,使用天勤接口 if self.tq_api and req.exchange in [Exchange.SHFE, Exchange.INE]: - self.write_log(f'使用天勤接口订阅{ req.symbol}') + self.write_log(f'使用天勤接口订阅{req.symbol}') self.tq_api.subscribe(req) else: self.write_log(f'使用CTP接口订阅{req.symbol}') @@ -544,6 +545,7 @@ class CtpGateway(BaseGateway): tick = copy(tick) combiner.on_tick(tick) + class CtpMdApi(MdApi): """""" @@ -652,7 +654,7 @@ class CtpMdApi(MdApi): # 处理一下标准套利合约的last_price if '&' in symbol: - tick.last_price = (tick.ask_price_1 + tick.bid_price_1)/2 + tick.last_price = (tick.ask_price_1 + tick.bid_price_1) / 2 if data["BidVolume2"] or data["AskVolume2"]: tick.bid_price_2 = adjust_price(data["BidPrice2"]) @@ -846,7 +848,7 @@ class CtpTdApi(TdApi): ) self.gateway.on_order(order) - #self.gateway.write_error("交易委托失败", error) + # self.gateway.write_error("交易委托失败", error) def onRspOrderAction(self, data: dict, error: dict, reqid: int, last: bool): """""" @@ -965,7 +967,7 @@ class CtpTdApi(TdApi): """""" if "AccountID" not in data: return - if len(self.accountid)== 0: + if len(self.accountid) == 0: self.accountid = data['AccountID'] balance = float(data["Balance"]) @@ -984,8 +986,11 @@ class CtpTdApi(TdApi): account.available = round(float(data["Available"]), 7) account.commission = round(float(data['Commission']), 7) account.margin = round(float(data['CurrMargin']), 7) - account.close_profit = round(float(data['CloseProfit']), 7) - account.holding_profit = round(float(data['PositionProfit']), 7) + account.close_profit = round(float(data['CloseProfit']), 7) + round( + float(data.get("SpecProductCloseProfit", 0)), 7) + account.holding_profit = round(float(data['PositionProfit']), 7) + round( + float(data.get("SpecProductPositionProfit", 0)), 7) + round( + float(data.get("SpecProductPositionProfitByAlg", 0)), 7) account.trading_day = str(data['TradingDay']) if '-' not in account.trading_day and len(account.trading_day) == 8: account.trading_day = '-'.join( @@ -1014,7 +1019,7 @@ class CtpTdApi(TdApi): gateway_name=self.gateway_name ) # 保证金费率(期权合约的保证金比例数值可能不对,所以设置个0.2的最大值) - contract.margin_rate = min(0.2,max(data.get('LongMarginRatio', 0), data.get('ShortMarginRatio', 0))) + contract.margin_rate = min(0.2, max(data.get('LongMarginRatio', 0), data.get('ShortMarginRatio', 0))) if contract.margin_rate == 0: contract.margin_rate = 0.1 @@ -1153,7 +1158,7 @@ class CtpTdApi(TdApi): exchange=exchange, orderid=orderid, sys_orderid=data.get("OrderSysID", orderid), - tradeid=tradeid.replace(' ',''), + tradeid=tradeid.replace(' ', ''), direction=DIRECTION_CTP2VT[data["Direction"]], offset=OFFSET_CTP2VT[data["OffsetFlag"]], price=data["Price"], @@ -1789,7 +1794,7 @@ class SubMdApi(): """转换dict, vnpy1 tick dict => vnpy2 tick dict""" if 'vtSymbol' not in d: return d - symbol= d.get('symbol') + symbol = d.get('symbol') exchange = d.get('exchange') vtSymbol = d.pop('vtSymbol', symbol) if '.' not in symbol: @@ -1798,20 +1803,19 @@ class SubMdApi(): d.update({'vt_symbol': f'{symbol}.{Exchange.LOCAL.value}'}) # 成交数据 - d.update({'last_price': d.pop('lastPrice',0.0)}) # 最新成交价 - d.update({'last_volume': d.pop('lastVolume', 0)}) # 最新成交量 + d.update({'last_price': d.pop('lastPrice', 0.0)}) # 最新成交价 + d.update({'last_volume': d.pop('lastVolume', 0)}) # 最新成交量 - d.update({'open_interest': d.pop('openInterest', 0)}) # 昨持仓量 + d.update({'open_interest': d.pop('openInterest', 0)}) # 昨持仓量 d.update({'open_interest': d.pop('tradingDay', get_trading_date())}) - # 常规行情 - d.update({'open_price': d.pop('openPrice', 0)}) # 今日开盘价 + d.update({'open_price': d.pop('openPrice', 0)}) # 今日开盘价 d.update({'high_price': d.pop('highPrice', 0)}) # 今日最高价 d.update({'low_price': d.pop('lowPrice', 0)}) # 今日最低价 d.update({'pre_close': d.pop('preClosePrice', 0)}) # 昨收盘价 - d.update({'limit_up': d.pop('upperLimit', 0)}) # 涨停价 + d.update({'limit_up': d.pop('upperLimit', 0)}) # 涨停价 d.update({'limit_down': d.pop('lowerLimit', 0)}) # 跌停价 # 五档行情 @@ -1951,7 +1955,7 @@ class TqMdApi(): ) if symbol.endswith('99') and tick.ask_price_1 == 0.0 and tick.bid_price_1 == 0.0: price_tick = quote['price_tick'] - if isinstance(price_tick, float) or isinstance(price_tick,int): + if isinstance(price_tick, float) or isinstance(price_tick, int): tick.ask_price_1 = tick.last_price + price_tick tick.ask_volume_1 = 1 tick.bid_price_1 = tick.last_price - price_tick @@ -1993,8 +1997,8 @@ class TqMdApi(): ] for contract in self.all_instruments: if ( - "SSWE" in contract["instrument_id"] - or "CSI" in contract["instrument_id"] + "SSWE" in contract["instrument_id"] + or "CSI" in contract["instrument_id"] ): # vnpy没有这两个交易所,需要可以自行修改vnpy代码 continue diff --git a/vnpy/gateway/futu/futu_gateway.py b/vnpy/gateway/futu/futu_gateway.py index f23f5aea..ca32298f 100644 --- a/vnpy/gateway/futu/futu_gateway.py +++ b/vnpy/gateway/futu/futu_gateway.py @@ -485,7 +485,7 @@ class FutuGateway(BaseGateway): sys_orderid = "" for ix, row in data.iterrows(): - sys_orderid = str(row.get("order_id","")) + sys_orderid = str(row.get("order_id", "")) if len(sys_orderid) > 0: self.write_log(f'系统委托号:{sys_orderid}') break diff --git a/vnpy/gateway/gj/gj_gateway.py b/vnpy/gateway/gj/gj_gateway.py index 371e727e..163d8f66 100644 --- a/vnpy/gateway/gj/gj_gateway.py +++ b/vnpy/gateway/gj/gj_gateway.py @@ -1167,7 +1167,7 @@ class TqMdApi(): return try: from tqsdk import TqApi - self.api = TqApi(_stock=True,url="wss://u.shinnytech.com/t/nfmd/front/mobile") + self.api = TqApi(_stock=True, url="wss://u.shinnytech.com/t/nfmd/front/mobile") except Exception as e: self.gateway.write_log(f'天勤股票行情API接入异常:'.format(str(e))) self.gateway.write_log(traceback.format_exc()) diff --git a/vnpy/gateway/pb/pb_gateway.py b/vnpy/gateway/pb/pb_gateway.py index 91e3e56a..9e6515fc 100644 --- a/vnpy/gateway/pb/pb_gateway.py +++ b/vnpy/gateway/pb/pb_gateway.py @@ -17,6 +17,11 @@ from functools import lru_cache from collections import OrderedDict from multiprocessing.dummy import Pool from threading import Thread + +from pytdx.hq import TdxHq_API +from pytdx.config.hosts import hq_hosts +from pytdx.params import TDXParams + from vnpy.event import EventEngine from vnpy.trader.event import EVENT_TIMER from vnpy.trader.constant import ( @@ -45,6 +50,9 @@ from vnpy.trader.object import ( from vnpy.trader.utility import get_folder_path, print_dict, extract_vt_symbol, get_stock_exchange, append_data from vnpy.data.tdx.tdx_common import get_stock_type_sz, get_stock_type_sh +# 通达信股票行情 +from vnpy.data.tdx.tdx_common import get_cache_config, get_tdx_market_code + # 代码 <=> 中文名称 symbol_name_map: Dict[str, str] = {} # 代码 <=> 交易所 @@ -303,11 +311,6 @@ STATUS_PB2VT: Dict[str, Status] = { } STOCK_CONFIG_FILE = 'tdx_stock_config.pkb2' -from pytdx.hq import TdxHq_API -# 通达信股票行情 -from vnpy.data.tdx.tdx_common import get_cache_config, get_tdx_market_code -from pytdx.config.hosts import hq_hosts -from pytdx.params import TDXParams class PbGateway(BaseGateway): @@ -474,13 +477,13 @@ class PbMdApi(object): {'ip': "124.160.88.183", 'port': 7709}, {'ip': "60.12.136.250", 'port': 7709}, {'ip': "218.108.98.244", 'port': 7709}, - #{'ip': "218.108.47.69", 'port': 7709}, + # {'ip': "218.108.47.69", 'port': 7709}, {'ip': "114.80.63.12", 'port': 7709}, {'ip': "114.80.63.35", 'port': 7709}, {'ip': "180.153.39.51", 'port': 7709}, - #{'ip': '14.215.128.18', 'port': 7709}, - #{'ip': '59.173.18.140', 'port': 7709} - ] + # {'ip': '14.215.128.18', 'port': 7709}, + # {'ip': '59.173.18.140', 'port': 7709} + ] self.best_ip = {'ip': None, 'port': None} self.api_dict = {} # API 的连接会话对象字典 @@ -722,7 +725,7 @@ class PbMdApi(object): margin_rate=1 ) - if product!= Product.INDEX: + if product != Product.INDEX: # 缓存 合约 =》 中文名 symbol_name_map.update({contract.symbol: contract.name}) @@ -1654,7 +1657,8 @@ class PbTdApi(object): order.status = Status.REJECTED self.gateway.write_log(f'dbf批量下单,委托被拒:{order.__dict__}') self.gateway.order_manager.on_order(order) - self.gateway.write_error(msg=f'{order.direction.value},{order.vt_symbol},{err_msg}', error={"ErrorID": err_id, "ErrorMsg": "委托失败"}) + self.gateway.write_error(msg=f'{order.direction.value},{order.vt_symbol},{err_msg}', + error={"ErrorID": err_id, "ErrorMsg": "委托失败"}) if sys_orderid != '0': self.gateway.order_manager.update_orderid_map(local_orderid=local_orderid, @@ -1933,7 +1937,6 @@ class PbTdApi(object): '{}{}.dbf'.format(PB_FILE_NAMES.get('cancel_order'), self.trading_date))) - # 打开dbf文件=》table table = dbf.Table(dbf_file) # 读取、写入模式 @@ -2062,14 +2065,14 @@ class TqMdApi(): self.ticks = {} - def connect(self, setting = {}): + def connect(self, setting={}): """""" if self.api and self.is_connected: self.gateway.write_log(f'天勤行情已经接入,无需重新连接') return try: from tqsdk import TqApi - self.api = TqApi(_stock=True) + self.api = TqApi(_stock=True, url="wss://u.shinnytech.com/t/nfmd/front/mobile") except Exception as e: self.gateway.write_log(f'天勤股票行情API接入异常:'.format(str(e))) self.gateway.write_log(traceback.format_exc()) @@ -2203,4 +2206,3 @@ class TqMdApi(): self.update_thread.join() except Exception as e: self.gateway.write_log('退出天勤行情api异常:{}'.format(str(e))) - diff --git a/vnpy/gateway/rohon/rohon_gateway.py b/vnpy/gateway/rohon/rohon_gateway.py index 864930d8..b03bc3fc 100644 --- a/vnpy/gateway/rohon/rohon_gateway.py +++ b/vnpy/gateway/rohon/rohon_gateway.py @@ -4,7 +4,7 @@ import sys import json import traceback from datetime import datetime, timedelta -from copy import copy,deepcopy +from copy import copy, deepcopy from functools import lru_cache from typing import List import pandas as pd @@ -175,6 +175,7 @@ TQ2VT_TYPE = { "OPTION": Product.OPTION, } + @lru_cache(maxsize=9999) def vt_to_tq_symbol(symbol: str, exchange: Exchange) -> str: """ @@ -270,7 +271,7 @@ class RohonGateway(BaseGateway): product_info = setting["产品信息"] rabbit_dict = setting.get('rabbit', None) tq_dict = setting.get('tq', None) - self.debug = setting.get('debug',False) + self.debug = setting.get('debug', False) if not td_address.startswith("tcp://"): td_address = "tcp://" + td_address @@ -425,12 +426,12 @@ class RohonGateway(BaseGateway): self.write_log(f'使用RabbitMQ接口订阅{req.symbol}') self.rabbit_api.subscribe(req) elif self.tq_api: - self.write_log(f'使用天勤接口订阅{ req.symbol}') + self.write_log(f'使用天勤接口订阅{req.symbol}') self.tq_api.subscribe(req) else: # 上期所、上能源支持五档行情,使用天勤接口 if self.tq_api and req.exchange in [Exchange.SHFE, Exchange.INE]: - self.write_log(f'使用天勤接口订阅{ req.symbol}') + self.write_log(f'使用天勤接口订阅{req.symbol}') self.tq_api.subscribe(req) else: self.write_log(f'使用CTP接口订阅{req.symbol}') @@ -537,6 +538,7 @@ class RohonGateway(BaseGateway): tick = copy(tick) combiner.on_tick(tick) + class RohonMdApi(MdApi): """""" @@ -645,7 +647,7 @@ class RohonMdApi(MdApi): # 处理一下标准套利合约的last_price if '&' in symbol: - tick.last_price = (tick.ask_price_1 + tick.bid_price_1)/2 + tick.last_price = (tick.ask_price_1 + tick.bid_price_1) / 2 if data["BidVolume2"] or data["AskVolume2"]: tick.bid_price_2 = adjust_price(data["BidPrice2"]) @@ -924,7 +926,7 @@ class RohonTdApi(TdApi): if "AccountID" not in data: return - if len(self.accountid)== 0: + if len(self.accountid) == 0: self.accountid = data['AccountID'] account = AccountData( @@ -955,7 +957,7 @@ class RohonTdApi(TdApi): """ Callback of instrument query. """ - #if self.gateway.debug: + # if self.gateway.debug: # print(f'onRspQryInstrument') product = PRODUCT_ROHON2VT.get(data["ProductClass"], None) @@ -1113,7 +1115,7 @@ class RohonTdApi(TdApi): exchange=exchange, orderid=orderid, sys_orderid=data.get("OrderSysID", orderid), - tradeid=tradeid.replace(' ',''), + tradeid=tradeid.replace(' ', ''), direction=DIRECTION_ROHON2VT[data["Direction"]], offset=OFFSET_ROHON2VT[data["OffsetFlag"]], price=data["Price"], @@ -1125,14 +1127,14 @@ class RohonTdApi(TdApi): self.gateway.on_trade(trade) def connect( - self, - address: str, - userid: str, - password: str, - brokerid: int, - auth_code: str, - appid: str, - product_info + self, + address: str, + userid: str, + password: str, + brokerid: int, + auth_code: str, + appid: str, + product_info ): """ Start connection to server. @@ -1760,29 +1762,28 @@ class SubMdApi(): """转换dict, vnpy1 tick dict => vnpy2 tick dict""" if 'vtSymbol' not in d: return d - symbol= d.get('symbol') + symbol = d.get('symbol') exchange = d.get('exchange') - vtSymbol = d.pop('vtSymbol', symbol) + d.pop('vtSymbol', None) if '.' not in symbol: d.update({'vt_symbol': f'{symbol}.{exchange}'}) else: d.update({'vt_symbol': f'{symbol}.{Exchange.LOCAL.value}'}) # 成交数据 - d.update({'last_price': d.pop('lastPrice',0.0)}) # 最新成交价 - d.update({'last_volume': d.pop('lastVolume', 0)}) # 最新成交量 + d.update({'last_price': d.pop('lastPrice', 0.0)}) # 最新成交价 + d.update({'last_volume': d.pop('lastVolume', 0)}) # 最新成交量 - d.update({'open_interest': d.pop('openInterest', 0)}) # 昨持仓量 + d.update({'open_interest': d.pop('openInterest', 0)}) # 昨持仓量 d.update({'open_interest': d.pop('tradingDay', get_trading_date())}) - # 常规行情 - d.update({'open_price': d.pop('openPrice', 0)}) # 今日开盘价 + d.update({'open_price': d.pop('openPrice', 0)}) # 今日开盘价 d.update({'high_price': d.pop('highPrice', 0)}) # 今日最高价 d.update({'low_price': d.pop('lowPrice', 0)}) # 今日最低价 d.update({'pre_close': d.pop('preClosePrice', 0)}) # 昨收盘价 - d.update({'limit_up': d.pop('upperLimit', 0)}) # 涨停价 + d.update({'limit_up': d.pop('upperLimit', 0)}) # 涨停价 d.update({'limit_down': d.pop('lowerLimit', 0)}) # 跌停价 # 五档行情 @@ -1922,7 +1923,7 @@ class TqMdApi(): ) if symbol.endswith('99') and tick.ask_price_1 == 0.0 and tick.bid_price_1 == 0.0: price_tick = quote['price_tick'] - if isinstance(price_tick, float) or isinstance(price_tick,int): + if isinstance(price_tick, float) or isinstance(price_tick, int): tick.ask_price_1 = tick.last_price + price_tick tick.ask_volume_1 = 1 tick.bid_price_1 = tick.last_price - price_tick @@ -1960,12 +1961,12 @@ class TqMdApi(): def query_contracts(self) -> None: """""" self.all_instruments = [ - v for k, v in self.api._data["quotes"].items() if v["expired"] == False + v for k, v in self.api._data["quotes"].items() if not v["expired"] ] for contract in self.all_instruments: if ( - "SSWE" in contract["instrument_id"] - or "CSI" in contract["instrument_id"] + "SSWE" in contract["instrument_id"] + or "CSI" in contract["instrument_id"] ): # vnpy没有这两个交易所,需要可以自行修改vnpy代码 continue diff --git a/vnpy/gateway/sopt/sopt_gateway.py b/vnpy/gateway/sopt/sopt_gateway.py index 7794942d..7a32b24a 100644 --- a/vnpy/gateway/sopt/sopt_gateway.py +++ b/vnpy/gateway/sopt/sopt_gateway.py @@ -71,7 +71,6 @@ from vnpy.trader.utility import ( ) from vnpy.trader.event import EVENT_TIMER - STATUS_SOPT2VT = { THOST_FTDC_OAS_Submitted: Status.SUBMITTING, THOST_FTDC_OAS_Accepted: Status.SUBMITTING, @@ -127,6 +126,7 @@ symbol_name_map = {} symbol_size_map = {} option_name_map = {} + class SoptGateway(BaseGateway): """ VN Trader Gateway for SOPT . @@ -282,7 +282,7 @@ class SoptGateway(BaseGateway): self.td_api.close() self.md_api.close() - #def write_error(self, msg: str, error: dict): + # def write_error(self, msg: str, error: dict): # """""" # error_id = error["ErrorID"] # error_msg = error["ErrorMsg"] @@ -306,7 +306,6 @@ class SoptGateway(BaseGateway): self.query_functions = [self.query_account, self.query_position] self.event_engine.register(EVENT_TIMER, self.process_timer_event) - def on_custom_tick(self, tick): """推送自定义合约行情""" # 自定义合约行情 @@ -386,9 +385,9 @@ class SoptMdApi(MdApi): exchange = symbol_exchange_map.get(symbol, "") if not exchange: return - timestamp = f"{data['TradingDay']} {data['UpdateTime']}.{int(data['UpdateMillisec']/100)}" + timestamp = f"{data['TradingDay']} {data['UpdateTime']}.{int(data['UpdateMillisec'] / 100)}" dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S.%f") - #dt = CHINA_TZ.localize(dt) + # dt = CHINA_TZ.localize(dt) tick = TickData( symbol=symbol, @@ -408,6 +407,7 @@ class SoptMdApi(MdApi): ask_price_1=data["AskPrice1"], bid_volume_1=data["BidVolume1"], ask_volume_1=data["AskVolume1"], + trading_day=dt.strftime('%Y-%m-%d'), gateway_name=self.gateway_name ) @@ -523,8 +523,8 @@ class SoptTdApi(TdApi): self.positions = {} self.sysid_orderid_map = {} - self.long_option_cost = None # 多头期权动态市值 - self.short_option_cost = None # 空头期权动态市值 + self.long_option_cost = None # 多头期权动态市值 + self.short_option_cost = None # 空头期权动态市值 def onFrontConnected(self): """""" @@ -597,7 +597,8 @@ class SoptTdApi(TdApi): ) self.gateway.on_order(order) - self.gateway.write_error(f"交易委托失败:{symbol} {order.direction.value} {order.offset.value} {order.price}, {order.volume}", error) + self.gateway.write_error( + f"交易委托失败:{symbol} {order.direction.value} {order.offset.value} {order.price}, {order.volume}", error) def onRspOrderAction(self, data: dict, error: dict, reqid: int, last: bool): """""" @@ -621,7 +622,7 @@ class SoptTdApi(TdApi): if not data: return - #self.gateway.write_log(print_dict(data)) + # self.gateway.write_log(print_dict(data)) # Get buffered position object key = f"{data['InstrumentID'], data['PosiDirection']}" @@ -682,16 +683,20 @@ class SoptTdApi(TdApi): # 重新累计多头期权动态权益 if position.direction == Direction.LONG: if self.long_option_cost is None: - self.long_option_cost = position.cur_price * position.volume * symbol_size_map.get(position.symbol, 0) + self.long_option_cost = position.cur_price * position.volume * symbol_size_map.get( + position.symbol, 0) else: - self.long_option_cost += position.cur_price * position.volume * symbol_size_map.get(position.symbol, 0) + self.long_option_cost += position.cur_price * position.volume * symbol_size_map.get( + position.symbol, 0) # 重新累计空头期权动态权益 if position.direction == Direction.SHORT: if self.short_option_cost is None: - self.short_option_cost = position.cur_price * position.volume * symbol_size_map.get(position.symbol, 0) + self.short_option_cost = position.cur_price * position.volume * symbol_size_map.get( + position.symbol, 0) else: - self.short_option_cost += position.cur_price * position.volume * symbol_size_map.get(position.symbol, 0) + self.short_option_cost += position.cur_price * position.volume * symbol_size_map.get( + position.symbol, 0) self.gateway.on_position(position) @@ -704,7 +709,7 @@ class SoptTdApi(TdApi): # 资金差额(权利金,正数,是卖call或卖put,收入权利金; 负数,是买call、买put,付出权利金) cash_in = data.get('CashIn') - #balance -= cash_in + # balance -= cash_in if self.long_option_cost is not None: balance += self.long_option_cost @@ -717,13 +722,14 @@ class SoptTdApi(TdApi): gateway_name=self.gateway_name ) - #self.gateway.write_log(print_dict(data)) + # self.gateway.write_log(print_dict(data)) account.available = data["Available"] account.commission = round(float(data['Commission']), 7) + round(float(data['SpecProductCommission']), 7) account.margin = round(float(data['CurrMargin']), 7) account.close_profit = round(float(data['CloseProfit']), 7) + round(float(data['SpecProductCloseProfit']), 7) - account.holding_profit = round(float(data['PositionProfit']), 7) + round(float(data['SpecProductPositionProfit']), 7) + account.holding_profit = round(float(data['PositionProfit']), 7) + round( + float(data['SpecProductPositionProfit']), 7) account.trading_day = str(data.get('TradingDay', datetime.now().strftime('%Y-%m-%d'))) if '-' not in account.trading_day and len(account.trading_day) == 8: @@ -758,14 +764,14 @@ class SoptTdApi(TdApi): if contract.product == Product.OPTION: contract.option_portfolio = data["UnderlyingInstrID"] + "_O" contract.option_underlying = ( - data["UnderlyingInstrID"] - + "-" - + str(data["DeliveryYear"]) - + str(data["DeliveryMonth"]).rjust(2, "0") + data["UnderlyingInstrID"] + + "-" + + str(data["DeliveryYear"]) + + str(data["DeliveryMonth"]).rjust(2, "0") ) contract.option_type = OPTIONTYPE_SOPT2VT.get(data["OptionsType"], None) contract.option_strike = data["StrikePrice"] - #contract.option_index = str(data["StrikePrice"]) + # contract.option_index = str(data["StrikePrice"]) contract.option_expiry = datetime.strptime(data["ExpireDate"], "%Y%m%d") contract.option_index = get_option_index( contract.option_strike, data["InstrumentCode"] @@ -806,7 +812,7 @@ class SoptTdApi(TdApi): timestamp = f"{data['InsertDate']} {data['InsertTime']}" dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S") - #dt = CHINA_TZ.localize(dt) + # dt = CHINA_TZ.localize(dt) order = OrderData( accountid=self.userid, @@ -862,14 +868,14 @@ class SoptTdApi(TdApi): self.gateway.on_trade(trade) def connect( - self, - address: str, - userid: str, - password: str, - brokerid: int, - auth_code: str, - appid: str, - product_info + self, + address: str, + userid: str, + password: str, + brokerid: int, + auth_code: str, + appid: str, + product_info ): """ Start connection to server. diff --git a/vnpy/gateway/xtp/xtp_gateway.py b/vnpy/gateway/xtp/xtp_gateway.py index 83e0cfd9..d9f38172 100644 --- a/vnpy/gateway/xtp/xtp_gateway.py +++ b/vnpy/gateway/xtp/xtp_gateway.py @@ -43,9 +43,9 @@ EXCHANGE_VT2XTP: Dict[Exchange, int] = {v: k for k, v in EXCHANGE_XTP2VT.items() # 方向 <=> Direction, Offset DIRECTION_STOCK_XTP2VT: Dict[int, Any] = { - 1: (Direction.LONG, Offset.NONE), # 买 - 2: (Direction.SHORT, Offset.NONE), # 卖 - 21: (Direction.LONG, Offset.OPEN), # 多,开 + 1: (Direction.LONG, Offset.NONE), # 买 + 2: (Direction.SHORT, Offset.NONE), # 卖 + 21: (Direction.LONG, Offset.OPEN), # 多,开 22: (Direction.SHORT, Offset.OPEN), # 空,开 24: (Direction.LONG, Offset.CLOSE), # 多,平 23: (Direction.SHORT, Offset.CLOSE) # 空, 平 @@ -128,12 +128,13 @@ symbol_name_map: Dict[str, str] = {} # 代码 <=> 交易所 symbol_exchange_map: Dict[str, Exchange] = {} + @lru_cache() def get_vt_symbol_name(vt_symbol): return symbol_name_map.get(vt_symbol, vt_symbol.split('.')[0]) -class XtpGateway(BaseGateway): +class XtpGateway(BaseGateway): default_setting: Dict[str, Any] = { "账号": "", "密码": "", @@ -301,7 +302,7 @@ class XtpMdApi(MdApi): tick.ask_volume_1, tick.ask_volume_2, tick.ask_volume_3, tick.ask_volume_4, tick.ask_volume_5 = data["ask_qty"][0:5] tick.name = get_vt_symbol_name(tick.vt_symbol) - #self.gateway.prices.update({tick.vt_symbol: tick.last_price}) + # self.gateway.prices.update({tick.vt_symbol: tick.last_price}) self.gateway.on_tick(tick) def onSubOrderBook(self, data: dict, error: dict, last: bool) -> None: @@ -364,14 +365,14 @@ class XtpMdApi(MdApi): min_volume=data["buy_qty_unit"], gateway_name=self.gateway_name ) - #if contract.symbol.startswith('1230'): + # if contract.symbol.startswith('1230'): # self.gateway.write_log(msg=f'合约信息:{contract.__dict__}') self.gateway.on_contract(contract) # 更新最新价 pre_close_price = float(data["pre_close_price"]) vt_symbol = contract.vt_symbol - if vt_symbol not in self.gateway.prices and pre_close_price>0: + if vt_symbol not in self.gateway.prices and pre_close_price > 0: self.gateway.prices.update({vt_symbol: pre_close_price}) # 更新 symbol <=> 中文名称映射 @@ -422,13 +423,13 @@ class XtpMdApi(MdApi): pass def connect( - self, - userid: str, - password: str, - client_id: int, - server_ip: str, - server_port: int, - quote_protocol: int + self, + userid: str, + password: str, + client_id: int, + server_ip: str, + server_port: int, + quote_protocol: int ) -> None: """""" self.userid = userid @@ -573,7 +574,7 @@ class XtpTdApi(TdApi): direction, offset = DIRECTION_STOCK_XTP2VT[data["side"]] trade_time = str(data["trade_time"]) - dt = datetime.strptime(trade_time,'%Y%m%d%H%M%S%f') + dt = datetime.strptime(trade_time, '%Y%m%d%H%M%S%f') trade = TradeData( accountid=self.userid, @@ -609,12 +610,12 @@ class XtpTdApi(TdApi): pass def onQueryPosition( - self, - data: dict, - error: dict, - request: int, - last: bool, - session: int + self, + data: dict, + error: dict, + request: int, + last: bool, + session: int ) -> None: """普通账号持仓""" # self.gateway.write_log(f"------\n {print_dict(data)}") @@ -634,10 +635,10 @@ class XtpTdApi(TdApi): pnl=data["unrealized_pnl"], yd_volume=data["yesterday_position"], gateway_name=self.gateway_name, - cur_price=self.gateway.prices.get(vt_symbol,0) + cur_price=self.gateway.prices.get(vt_symbol, 0) ) if position.volume > 0 and position.cur_price > 0: - position.pnl = round(position.volume * (position.cur_price - position.price),2) + position.pnl = round(position.volume * (position.cur_price - position.price), 2) self.gateway.on_position(position) # 如果持仓>0 获取持仓对应的当前最新价 @@ -648,7 +649,7 @@ class XtpTdApi(TdApi): def update_security_asset(self): """更新资产净值""" - #self.gateway.write_log(f'更新资产净值') + # self.gateway.write_log(f'更新资产净值') total_asset = 0 for vt_symbol, volume in self.security_volumes.items(): price = self.gateway.prices.get(vt_symbol, None) @@ -662,17 +663,17 @@ class XtpTdApi(TdApi): return total_asset += volume * price - #self.gateway.write_log(f'资产净值 => {total_asset}') + # self.gateway.write_log(f'资产净值 => {total_asset}') self.security_asset = total_asset def onQueryAsset( - self, - data: dict, - error: dict, - request: int, - last: bool, - session: int + self, + data: dict, + error: dict, + request: int, + last: bool, + session: int ) -> None: """""" # XTP_ACCOUNT_NORMAL = 0, ///<普通账户 @@ -691,8 +692,8 @@ class XtpTdApi(TdApi): account = AccountData( accountid=self.userid, - balance=balance, # 总资产 - margin=self.security_asset, # 证券资产 + balance=balance, # 总资产 + margin=self.security_asset, # 证券资产 frozen=data["withholding_amount"], gateway_name=self.gateway_name, trading_day=datetime.now().strftime('%Y-%m-%d') @@ -740,12 +741,12 @@ class XtpTdApi(TdApi): pass def onQueryCreditDebtInfo( - self, - data: dict, - error: dict, - request: int, - last: bool, - session: int + self, + data: dict, + error: dict, + request: int, + last: bool, + session: int ) -> None: """信用账号持仓""" self.gateway.write_log(f"------\n {print_dict(data)}") @@ -761,7 +762,7 @@ class XtpTdApi(TdApi): exchange=exchange, direction=Direction.SHORT, gateway_name=self.gateway_name, - cur_price=self.gateway.prices.get(f'{symbol}.{exchange.value}',0.0) + cur_price=self.gateway.prices.get(f'{symbol}.{exchange.value}', 0.0) ) self.short_positions[symbol] = position @@ -774,13 +775,13 @@ class XtpTdApi(TdApi): self.short_positions.clear() def connect( - self, - userid: str, - password: str, - client_id: int, - server_ip: str, - server_port: int, - software_key: str + self, + userid: str, + password: str, + client_id: int, + server_ip: str, + server_port: int, + software_key: str ) -> None: """""" @@ -860,7 +861,7 @@ class XtpTdApi(TdApi): "market": MARKET_VT2XTP[req.exchange], "price": req.price, "quantity": int(req.volume), - "side": DIRECTION_STOCK_VT2XTP.get((req.direction,req.offset), ""), + "side": DIRECTION_STOCK_VT2XTP.get((req.direction, req.offset), ""), "price_type": ORDERTYPE_VT2XTP[req.type], "business_type": BUSINESS_VT2XTP[req.offset] } diff --git a/vnpy/trader/constant.py b/vnpy/trader/constant.py index c6d99b7c..2172fc3b 100644 --- a/vnpy/trader/constant.py +++ b/vnpy/trader/constant.py @@ -88,45 +88,45 @@ class Exchange(Enum): Exchange. """ # Chinese - CFFEX = "CFFEX" # China Financial Futures Exchange - SHFE = "SHFE" # Shanghai Futures Exchange - CZCE = "CZCE" # Zhengzhou Commodity Exchange - DCE = "DCE" # Dalian Commodity Exchange - INE = "INE" # Shanghai International Energy Exchange - SSE = "SSE" # Shanghai Stock Exchange - SZSE = "SZSE" # Shenzhen Stock Exchange - SGE = "SGE" # Shanghai Gold Exchange - WXE = "WXE" # Wuxi Steel Exchange - CFETS = "CFETS" # China Foreign Exchange Trade System + CFFEX = "CFFEX" # China Financial Futures Exchange + SHFE = "SHFE" # Shanghai Futures Exchange + CZCE = "CZCE" # Zhengzhou Commodity Exchange + DCE = "DCE" # Dalian Commodity Exchange + INE = "INE" # Shanghai International Energy Exchange + SSE = "SSE" # Shanghai Stock Exchange + SZSE = "SZSE" # Shenzhen Stock Exchange + SGE = "SGE" # Shanghai Gold Exchange + WXE = "WXE" # Wuxi Steel Exchange + CFETS = "CFETS" # China Foreign Exchange Trade System # Global - SMART = "SMART" # Smart Router for US stocks - NYSE = "NYSE" # New York Stock Exchnage - NASDAQ = "NASDAQ" # Nasdaq Exchange - NYMEX = "NYMEX" # New York Mercantile Exchange - COMEX = "COMEX" # a division of theNew York Mercantile Exchange - GLOBEX = "GLOBEX" # Globex of CME - IDEALPRO = "IDEALPRO" # Forex ECN of Interactive Brokers - CME = "CME" # Chicago Mercantile Exchange - ICE = "ICE" # Intercontinental Exchange - SEHK = "SEHK" # Stock Exchange of Hong Kong - HKFE = "HKFE" # Hong Kong Futures Exchange - HKSE = "HKSE" # Hong Kong Stock Exchange - SGX = "SGX" # Singapore Global Exchange - CBOT = "CBT" # Chicago Board of Trade - CBOE = "CBOE" # Chicago Board Options Exchange - CFE = "CFE" # CBOE Futures Exchange - DME = "DME" # Dubai Mercantile Exchange - EUREX = "EUX" # Eurex Exchange - APEX = "APEX" # Asia Pacific Exchange - LME = "LME" # London Metal Exchange - BMD = "BMD" # Bursa Malaysia Derivatives - TOCOM = "TOCOM" # Tokyo Commodity Exchange - EUNX = "EUNX" # Euronext Exchange - KRX = "KRX" # Korean Exchange - AMEX = "AMEX" # NESE American + SMART = "SMART" # Smart Router for US stocks + NYSE = "NYSE" # New York Stock Exchnage + NASDAQ = "NASDAQ" # Nasdaq Exchange + NYMEX = "NYMEX" # New York Mercantile Exchange + COMEX = "COMEX" # a division of theNew York Mercantile Exchange + GLOBEX = "GLOBEX" # Globex of CME + IDEALPRO = "IDEALPRO" # Forex ECN of Interactive Brokers + CME = "CME" # Chicago Mercantile Exchange + ICE = "ICE" # Intercontinental Exchange + SEHK = "SEHK" # Stock Exchange of Hong Kong + HKFE = "HKFE" # Hong Kong Futures Exchange + HKSE = "HKSE" # Hong Kong Stock Exchange + SGX = "SGX" # Singapore Global Exchange + CBOT = "CBT" # Chicago Board of Trade + CBOE = "CBOE" # Chicago Board Options Exchange + CFE = "CFE" # CBOE Futures Exchange + DME = "DME" # Dubai Mercantile Exchange + EUREX = "EUX" # Eurex Exchange + APEX = "APEX" # Asia Pacific Exchange + LME = "LME" # London Metal Exchange + BMD = "BMD" # Bursa Malaysia Derivatives + TOCOM = "TOCOM" # Tokyo Commodity Exchange + EUNX = "EUNX" # Euronext Exchange + KRX = "KRX" # Korean Exchange + AMEX = "AMEX" # NESE American - OANDA = "OANDA" # oanda.com + OANDA = "OANDA" # oanda.com # CryptoCurrency BITMEX = "BITMEX" @@ -134,15 +134,15 @@ class Exchange(Enum): HUOBI = "HUOBI" BITFINEX = "BITFINEX" BINANCE = "BINANCE" - BYBIT = "BYBIT" # bybit.com + BYBIT = "BYBIT" # bybit.com COINBASE = "COINBASE" DERIBIT = "DERIBIT" GATEIO = "GATEIO" BITSTAMP = "BITSTAMP" # Special Function - LOCAL = "LOCAL" # For local generated data - SPD = "SPD" # Customer Spread data + LOCAL = "LOCAL" # For local generated data + SPD = "SPD" # Customer Spread data class Currency(Enum): @@ -165,12 +165,13 @@ class Interval(Enum): WEEKLY = "w" RENKO = 'renko' + class StockType(Enum): """股票类型(tdx)""" - STOCK = 'stock_cn' # 股票 - STOCKB = 'stockB_cn' # 深圳B股票(特别) - INDEX = 'index_cn' # 指数 - BOND = 'bond_cn' # 企业债券 - ETF = 'etf_cn' # ETF - CB = 'cb_cn' # 可转债 - UNDEFINED = 'undefined' # 未定义 + STOCK = 'stock_cn' # 股票 + STOCKB = 'stockB_cn' # 深圳B股票(特别) + INDEX = 'index_cn' # 指数 + BOND = 'bond_cn' # 企业债券 + ETF = 'etf_cn' # ETF + CB = 'cb_cn' # 可转债 + UNDEFINED = 'undefined' # 未定义 diff --git a/vnpy/trader/converter.py b/vnpy/trader/converter.py index 522844d8..8122a913 100644 --- a/vnpy/trader/converter.py +++ b/vnpy/trader/converter.py @@ -177,7 +177,7 @@ class PositionHolding: self.short_yd -= trade.volume # 多,平仓 =》 减少 elif trade.offset == Offset.CLOSE: - if trade.exchange in [Exchange.SHFE, Exchange.INE] and self.short_yd >=trade.volume: + if trade.exchange in [Exchange.SHFE, Exchange.INE] and self.short_yd >= trade.volume: self.short_yd -= trade.volume else: self.short_td -= trade.volume @@ -194,9 +194,9 @@ class PositionHolding: elif trade.offset == Offset.CLOSETODAY: self.long_td -= trade.volume elif trade.offset == Offset.CLOSEYESTERDAY: - self.long_yd -= trade.volume + self.long_yd -= trade.volume elif trade.offset == Offset.CLOSE: - if trade.exchange in [Exchange.SHFE, Exchange.INE] and self.long_yd >=trade.volume: + if trade.exchange in [Exchange.SHFE, Exchange.INE] and self.long_yd >= trade.volume: self.long_yd -= trade.volume else: self.long_td -= trade.volume diff --git a/vnpy/trader/engine.py b/vnpy/trader/engine.py index 54ece4af..837d6e8c 100644 --- a/vnpy/trader/engine.py +++ b/vnpy/trader/engine.py @@ -431,8 +431,8 @@ class OmsEngine(BaseEngine): self.today_contracts: Dict[str, ContractData] = {} # 自定义合约 - self.custom_contracts = {} # vt_symbol: ContractData - self.custom_settings = {} # symbol: dict + self.custom_contracts = {} # vt_symbol: ContractData + self.custom_settings = {} # symbol: dict self.symbol_spd_maping = {} # symbol: [spd_symbol] self.prices = {} @@ -568,7 +568,7 @@ class OmsEngine(BaseEngine): return Direction.LONG return direction - def create_spd_position_event(self, symbol, direction ): + def create_spd_position_event(self, symbol, direction): """创建自定义品种对持仓信息""" spd_symbols = self.symbol_spd_maping.get(symbol, []) if not spd_symbols: @@ -614,7 +614,7 @@ class OmsEngine(BaseEngine): continue # 根据leg1/leg2的volume ratio,计算出最小spd_volume - spd_volume = min(int(leg1_pos.volume/leg1_ratio), int(leg2_pos.volume/leg2_ratio)) + spd_volume = min(int(leg1_pos.volume / leg1_ratio), int(leg2_pos.volume / leg2_ratio)) if spd_volume <= 0 and spd_pos is None: continue @@ -766,6 +766,7 @@ class OmsEngine(BaseEngine): """根据主动腿/被动腿symbol,获取自定义套利对的symbol list""" return self.symbol_spd_maping.get(symbol, []) + class CustomContract(object): """ 定制合约 diff --git a/vnpy/trader/gateway.py b/vnpy/trader/gateway.py index 141161cd..b51b1ff4 100644 --- a/vnpy/trader/gateway.py +++ b/vnpy/trader/gateway.py @@ -334,7 +334,7 @@ class LocalOrderManager: Management tool to support use local order id for trading. """ - def __init__(self, gateway: BaseGateway, order_prefix: str = "", order_rjust:int = 8): + def __init__(self, gateway: BaseGateway, order_prefix: str = "", order_rjust: int = 8): """""" self.gateway: BaseGateway = gateway diff --git a/vnpy/trader/object.py b/vnpy/trader/object.py index 25da3f00..efdc4e99 100644 --- a/vnpy/trader/object.py +++ b/vnpy/trader/object.py @@ -96,7 +96,7 @@ class BarData(BaseData): trading_day: str = "" # '%Y-%m-%d' interval: Interval = None # constant.py Internal 1m, 1h, 1d, 1w .etc - interval_num: int = 1 # 5 for 5m, 5h etc + interval_num: int = 1 # 5 for 5m, 5h etc volume: float = 0 open_interest: float = 0 open_price: float = 0 @@ -209,6 +209,7 @@ class TradeData(BaseData): self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}" self.vt_accountid = f"{self.gateway_name}.{self.accountid}" + @dataclass class PositionData(BaseData): """ @@ -218,7 +219,7 @@ class PositionData(BaseData): symbol: str exchange: Exchange direction: Direction - accountid: str = "" # 账号id + accountid: str = "" # 账号id name: str = "" volume: float = 0 frozen: float = 0 @@ -238,6 +239,7 @@ class PositionData(BaseData): if self.name == "": self.name = self.vt_symbol + @dataclass class AccountData(BaseData): """ @@ -330,7 +332,7 @@ class ContractData(BaseData): option_underlying: str = "" # vt_symbol of underlying contract option_type: OptionType = None option_expiry: datetime = None - option_index: str = "" # vt_symbol mapping cur option + option_index: str = "" # vt_symbol mapping cur option def __post_init__(self): """""" diff --git a/vnpy/trader/ui/widget.py b/vnpy/trader/ui/widget.py index 38cee7ba..53320985 100644 --- a/vnpy/trader/ui/widget.py +++ b/vnpy/trader/ui/widget.py @@ -9,7 +9,6 @@ from copy import copy from PyQt5 import QtCore, QtGui, QtWidgets - from vnpy.event import Event, EventEngine from ..constant import Direction, Exchange, Offset, OrderType from ..engine import MainEngine @@ -628,7 +627,7 @@ class TradingWidget(QtWidgets.QWidget): [order_type.value for order_type in OrderType]) double_validator = QtGui.QDoubleValidator() - #double_validator.setBottom(0) + # double_validator.setBottom(0) self.price_line = QtWidgets.QLineEdit() self.price_line.setValidator(double_validator) @@ -721,9 +720,9 @@ class TradingWidget(QtWidgets.QWidget): self.setLayout(vbox) def create_label( - self, - color: str = "", - alignment: int = QtCore.Qt.AlignLeft + self, + color: str = "", + alignment: int = QtCore.Qt.AlignLeft ) -> QtWidgets.QLabel: """ Create label with certain font color. diff --git a/vnpy/trader/util_wechat.py b/vnpy/trader/util_wechat.py index 356e96db..4795e97d 100644 --- a/vnpy/trader/util_wechat.py +++ b/vnpy/trader/util_wechat.py @@ -14,15 +14,16 @@ import requests import sys import traceback from datetime import datetime -from functools import wraps +# from functools import wraps from vnpy.trader.utility import print_dict + global wechat_lock wechat_lock = Lock() # 这里可以设置UIDS, 多个人可同时接收 -UIDS = ['UID_kZguGPBQPWn41Ni9FK4CgPts2KjU'] +UIDS = ['UID_kZguGPBQPWn41Ni9FK4CgPts2Kjx'] -APP_TOKEN = 'AT_aDuiQu41dmAQV2vUMXOaaTDrWyhKJN2z' +APP_TOKEN = 'AT_aDuiQu41dmAQV2vUMXOaaTDrWyhKJN2x' class wechat_thread(Thread): @@ -42,7 +43,7 @@ class wechat_thread(Thread): self.topic_ids = topic_ids self.url = url self.lock = wechat_lock - self.app_token = app_token if len(app_token) > 0 else APP_TOKEN + self.app_token = app_token if app_token is not None and len(app_token) > 0 else APP_TOKEN def run(self): if self.content is None or len(self.content) == 0: @@ -61,7 +62,7 @@ class wechat_thread(Thread): if not response.get('success', False): print(response) except Exception as e: - print("{} wechat_thread sent failed! ex:{},trace:{}".format(datetime.now(), str(e), traceback.format_exc()), + print("{} 微信发送异常 ex:{},trace:{}".format(datetime.now(), str(e), traceback.format_exc()), file=sys.stderr) return @@ -84,8 +85,9 @@ def send_wx_msg(*args, **kwargs): try: # 如果存在华富资产的微信模块,则使用 - from vnpy.trader.util_huafu import sendWeChatMsg, WECHAT_URL,WECHAT_GROUP, WECHAT_LEVEL_INFO, WECHAT_MSG_TYPE_ALERT - target=kwargs.get('target','XXX') + from vnpy.trader.util_huafu import sendWeChatMsg, WECHAT_URL, WECHAT_GROUP, WECHAT_LEVEL_INFO, \ + WECHAT_MSG_TYPE_ALERT + target = kwargs.get('target', 'XXX') sendWeChatMsg(content=content, target=WECHAT_GROUP.get(target), url=kwargs.get('url', WECHAT_URL), @@ -93,7 +95,8 @@ def send_wx_msg(*args, **kwargs): msg_type=kwargs.get('msg_type', WECHAT_MSG_TYPE_ALERT)) return except Exception as ex: - pass + print(f'发送微信异常:{str(ex)}', file=sys.stderr) + pass # dict => str, none str => str if not isinstance(content, str): @@ -114,6 +117,7 @@ def send_wx_msg(*args, **kwargs): # t.run() t.start() + if __name__ == '__main__': text = u'微信测试标题!!!!\n第二行' diff --git a/vnpy/trader/utility.py b/vnpy/trader/utility.py index 3911f23a..4f1a572b 100644 --- a/vnpy/trader/utility.py +++ b/vnpy/trader/utility.py @@ -163,6 +163,7 @@ def get_trading_date(dt: datetime = None): else: return dt.strftime('%Y-%m-%d') + def extract_vt_symbol(vt_symbol: str) -> Tuple[str, Exchange]: """ :return: (symbol, exchange) @@ -328,6 +329,7 @@ def get_digits(value: float) -> int: else: return 0 + def print_dict(d: dict): """返回dict的字符串类型""" return '\n'.join([f'{key}:{d[key]}' for key in sorted(d.keys())]) @@ -352,10 +354,11 @@ def get_csv_last_dt(file_name, dt_index=0, dt_format='%Y-%m-%d %H:%M:%S', line_l try: last_dt = datetime.strptime(datas[dt_index], dt_format) return last_dt - except: # noqa + except: # noqa return None return None + def append_data(file_name: str, dict_data: dict, field_names: list = [], auto_header=True, encoding='utf8'): """ 添加数据到csv文件中 @@ -366,7 +369,7 @@ def append_data(file_name: str, dict_data: dict, field_names: list = [], auto_he dict_fieldnames = sorted(list(dict_data.keys())) if len(field_names) == 0 else field_names try: - if not os.path.exists(file_name): # or os.path.getsize(file_name) == 0: + if not os.path.exists(file_name): # or os.path.getsize(file_name) == 0: print(u'create csv file:{}'.format(file_name)) with open(file_name, 'a', encoding='utf8', newline='\n') as csvWriteFile: writer = csv.DictWriter(f=csvWriteFile, fieldnames=dict_fieldnames, dialect='excel') @@ -410,11 +413,11 @@ def import_module_by_str(import_module_name): mod = import_module(loaded_modules) comp = modules[-1] - #if not hasattr(mod, comp): + # if not hasattr(mod, comp): # loaded_modules = '.'.join([loaded_modules, comp]) print('realod {}'.format(loaded_modules)) mod = reload(mod) - #else: + # else: # print('from {} import {}'.format(loaded_modules, comp)) comp = getattr(mod, comp) return comp @@ -669,6 +672,7 @@ def load_data_from_pkb2(pkb2_file_name): 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: @@ -687,11 +691,11 @@ class BarGenerator: """ def __init__( - self, - on_bar: Callable, - window: int = 0, - on_window_bar: Callable = None, - interval: Interval = Interval.MINUTE + self, + on_bar: Callable, + window: int = 0, + on_window_bar: Callable = None, + interval: Interval = Interval.MINUTE ): """Constructor""" self.bar: BarData = None @@ -1087,11 +1091,11 @@ class ArrayManager(object): return result[-1] def macd( - self, - fast_period: int, - slow_period: int, - signal_period: int, - array: bool = False + self, + fast_period: int, + slow_period: int, + signal_period: int, + array: bool = False ) -> Union[ Tuple[np.ndarray, np.ndarray, np.ndarray], Tuple[float, float, float] @@ -1179,10 +1183,10 @@ class ArrayManager(object): return result[-1] def boll( - self, - n: int, - dev: float, - array: bool = False + self, + n: int, + dev: float, + array: bool = False ) -> Union[ Tuple[np.ndarray, np.ndarray], Tuple[float, float] @@ -1199,10 +1203,10 @@ class ArrayManager(object): return up, down def keltner( - self, - n: int, - dev: float, - array: bool = False + self, + n: int, + dev: float, + array: bool = False ) -> Union[ Tuple[np.ndarray, np.ndarray], Tuple[float, float] @@ -1219,7 +1223,7 @@ class ArrayManager(object): return up, down def donchian( - self, n: int, array: bool = False + self, n: int, array: bool = False ) -> Union[ Tuple[np.ndarray, np.ndarray], Tuple[float, float] @@ -1235,9 +1239,9 @@ class ArrayManager(object): return up[-1], down[-1] def aroon( - self, - n: int, - array: bool = False + self, + n: int, + array: bool = False ) -> Union[ Tuple[np.ndarray, np.ndarray], Tuple[float, float]