diff --git a/prod/jobs/daily_stock_adjust_grids.py b/prod/jobs/daily_stock_adjust_grids.py index ec52b983..14a96ca7 100644 --- a/prod/jobs/daily_stock_adjust_grids.py +++ b/prod/jobs/daily_stock_adjust_grids.py @@ -110,9 +110,21 @@ if __name__ == "__main__": # 更新数量 grid['volume'] = adj_volume + # 更新开仓\平仓\止损价格 + open_price = grid['open_price'] + new_open_price = round(float(open_price * adj_rate), 3) + close_price = grid['close_price'] + new_close_price = round(float(close_price * adj_rate), 3) + stop_price = grid['stop_price'] + new_stop_price = round(float(stop_price * adj_rate), 3) + # 更新执行日期 grid['snapshot'].update({'adjusted_date': dividOperateDate}) - msg = f'{strategy_name}:{vt_symbol}[{name}]发生除权调整:{cur_volume}=>{adj_volume}' + msg = f'{strategy_name}:{vt_symbol}[{name}]发生除权调整:持仓{cur_volume}=>{adj_volume},' \ + f'开仓价:{open_price}=>{new_open_price},' \ + f'平仓价:{close_price}=>{new_close_price},' \ + f'止损价:{stop_price}=>{new_stop_price}' + send_wx_msg(msg) print(msg) append_data(adj_record_file, dict_data={ @@ -126,8 +138,8 @@ if __name__ == "__main__": 'pre_back_adj': pre_data.get('backAdjustFactor'), 'last_back_adj': last_data.get('backAdjustFactor') }) - changed = True + changed = True if changed: print('保存更新后的Grids.json文件') diff --git a/prod/jobs/refill_tdx_stock_bars.py b/prod/jobs/refill_tdx_stock_bars.py index cf9ad920..74e1b8fa 100644 --- a/prod/jobs/refill_tdx_stock_bars.py +++ b/prod/jobs/refill_tdx_stock_bars.py @@ -107,7 +107,7 @@ def refill(symbol_info): data_df = data_df.sort_index() # print(data_df.head()) print(data_df.tail()) - data_df.to_csv(bar_file_path, index=True) + data_df.to_csv(bar_file_path, index=True, encoding='utf8') d2 = datetime.now() microseconds = (d1 - d1).microseconds print(f'{progress}% 首次更新{stock_code} {stock_name}数据 {microseconds} 毫秒=> 文件{bar_file_path}') @@ -176,8 +176,11 @@ if __name__ == '__main__': for symbol in symbol_dict.keys(): info = copy(symbol_dict[symbol]) stock_code = info['code'] - if ('stock_type' in info.keys() and info['stock_type'] in ['stock_cn', - 'cb_cn']) or stock_code in stock_list: + + # 股票/可转债; 或 存在指定下载文件中 + if ('stock_type' in info.keys() \ + and info['stock_type'] in ['stock_cn', 'cb_cn']) \ + or stock_code in stock_list: info['period'] = period tasks.append(info) # if len(tasks) > 12: diff --git a/vnpy/api/rest/rest_client.py b/vnpy/api/rest/rest_client.py index 0f86b5d2..4e9ebe8c 100644 --- a/vnpy/api/rest/rest_client.py +++ b/vnpy/api/rest/rest_client.py @@ -46,6 +46,7 @@ class Request(object): params: dict, data: Union[dict, str, bytes], headers: dict, + cookies: Union[requests.cookies.RequestsCookieJar, dict]=None, callback: CALLBACK_TYPE = None, on_failed: ON_FAILED_TYPE = None, on_error: ON_ERROR_TYPE = None, @@ -61,7 +62,7 @@ class Request(object): self.params = params self.data = data self.headers = headers - + self.cookies = cookies self.stream = stream self.on_connected = on_connected self.processing_line: Optional[str] = '' @@ -258,6 +259,7 @@ class RestClient(object): params: dict = None, data: Union[dict, str, bytes] = None, headers: dict = None, + cookies: Union[requests.cookies.RequestsCookieJar, dict]=None, on_failed: ON_FAILED_TYPE = None, on_error: ON_ERROR_TYPE = None, extra: Any = None, @@ -281,6 +283,7 @@ class RestClient(object): params=params, data=data, headers=headers, + cookies=cookies, callback=callback, on_failed=on_failed, on_error=on_error, @@ -416,6 +419,7 @@ class RestClient(object): headers = request.headers params = request.params data = request.data + cookies = request.cookies self._log("[%s] sending request %s %s, headers:%s, params:%s, data:%s", uid, method, url, headers, params, data) @@ -425,6 +429,7 @@ class RestClient(object): headers=headers, params=params, data=data, + cookies=cookies, proxies=self.proxies, stream=stream, ) @@ -498,6 +503,7 @@ class RestClient(object): params: dict = None, data: dict = None, headers: dict = None, + cookies: Union[requests.cookies.RequestsCookieJar, dict]=None ): """ Add a new request. @@ -514,6 +520,7 @@ class RestClient(object): params, data, headers, + cookies=cookies, client=self, ) request = self.sign(request) @@ -526,6 +533,7 @@ class RestClient(object): headers=request.headers, params=request.params, data=request.data, - proxies=self.proxies, + cookies=request.cookies, + proxies=self.proxies ) return response diff --git a/vnpy/app/cta_stock/back_testing.py b/vnpy/app/cta_stock/back_testing.py index d6011a5e..eb38876c 100644 --- a/vnpy/app/cta_stock/back_testing.py +++ b/vnpy/app/cta_stock/back_testing.py @@ -367,10 +367,10 @@ class BackTestingEngine(object): self.volume_tick.update({vt_symbol: volume_tick}) def get_volume_tick(self, vt_symbol: str): - return self.volume_tick.get(vt_symbol, 1) + return self.volume_tick.get(vt_symbol, 100) def set_contract(self, symbol: str, exchange: Exchange, product: Product, name: str, size: int, - price_tick: float, volume_tick: float = 1, margin_rate: float = 0.1): + price_tick: float, volume_tick: float = 100, margin_rate: float = 0.1): """设置合约信息""" vt_symbol = '.'.join([symbol, exchange.value]) if vt_symbol not in self.contract_dict: @@ -580,7 +580,7 @@ class BackTestingEngine(object): product=Product(symbol_data.get('product', "股票")), size=symbol_data.get('symbol_size', 1), price_tick=symbol_data.get('price_tick', 0.01), - volume_tick=symbol_data.get('min_volume', 10), + volume_tick=symbol_data.get('min_volume', 100), margin_rate=margin_rate ) diff --git a/vnpy/app/cta_strategy_pro/template.py b/vnpy/app/cta_strategy_pro/template.py index 7917226c..5cac1637 100644 --- a/vnpy/app/cta_strategy_pro/template.py +++ b/vnpy/app/cta_strategy_pro/template.py @@ -1825,7 +1825,8 @@ class CtaProFutureTemplate(CtaProTemplate): self.write_log(u'撤单逻辑 => 重新开仓') # 开空委托单 if order_info['direction'] == Direction.SHORT: - short_price = self.cur_mi_price - self.price_tick + cur_price = self.cta_engine.get_price(order_vt_symbol) + short_price = cur_price - self.price_tick if order_grid.volume != order_volume and order_volume > 0: self.write_log( u'网格volume:{},order_volume:{}不一致,修正'.format(order_grid.volume, order_volume)) @@ -1845,7 +1846,8 @@ class CtaProFutureTemplate(CtaProTemplate): else: self.write_error(u'撤单后,重新委托开空仓失败') else: - buy_price = self.cur_mi_price + self.price_tick + cur_price = self.cta_engine.get_price(order_vt_symbol) + buy_price = cur_price + self.price_tick if order_grid.volume != order_volume and order_volume > 0: self.write_log( u'网格volume:{},order_volume:{}不一致,修正'.format(order_grid.volume, order_volume)) diff --git a/vnpy/component/cta_fund_kline.py b/vnpy/component/cta_fund_kline.py index 279045dd..fbc8f7b3 100644 --- a/vnpy/component/cta_fund_kline.py +++ b/vnpy/component/cta_fund_kline.py @@ -16,7 +16,7 @@ from datetime import datetime import pandas as pd import traceback -from vnpy.component.cta_line_bar import CtaHourBar +from vnpy.component.cta_line_bar import CtaMinuteBar, CtaHourBar, CtaDayBar, CtaLineBar, get_cta_bar_type from vnpy.component.cta_renko_bar import CtaRenkoBar from vnpy.trader.object import BarData, TickData from vnpy.trader.utility import get_folder_path, get_trading_date @@ -60,9 +60,12 @@ class FundKline(object): self.write_log(u'使用CtaRenkoBar') self.kline = CtaRenkoBar(strategy=self, cb_on_bar=self.on_bar, setting=self.setting) else: - self.write_log(u'使用CtaHourBar') - self.kline = CtaHourBar(strategy=self, cb_on_bar=self.on_bar, setting=self.setting) - # self.kline = CtaDayBar(strategy=self, onBarFunc=self.onBar, setting=self.setting) + kline_setting = self.setting + kline_period = kline_setting.pop('kline_period', 'H1') # 默认使用1小时K线 + kline_class, kline_bar_interval = get_cta_bar_type(kline_period) + self.write_log(u'使用{}'.format(kline_period)) + kline_setting['bar_interval'] = kline_bar_interval # X分钟K线, X秒K线,X小时K线 + self.kline = kline_class(strategy=self, cb_on_bar=self.on_bar, setting=kline_setting) self.inited = False self.long_pos_dict = {} diff --git a/vnpy/component/cta_line_bar.py b/vnpy/component/cta_line_bar.py index 0ce5b21b..00510ce6 100644 --- a/vnpy/component/cta_line_bar.py +++ b/vnpy/component/cta_line_bar.py @@ -6240,13 +6240,14 @@ class CtaLineBar(object): if bi_len < 3: return - if self.cur_fenxing.is_rt: - return + # if self.cur_fenxing.is_rt: + # return bi_n = min(15, bi_len) price = self.cur_bi.low if self.cur_bi.direction == -1 else self.cur_bi.high + # 计算 3,5,7,,,,13笔的形态 for n in range(3, bi_n, 2): # => 信号 if n == 3: @@ -6294,7 +6295,7 @@ class CtaLineBar(object): 'end': self.cur_bi.end, 'price': price, 'signal': qsbc_2nd}) - if cur_signal and self.export_xt_filename: + if cur_signal is not None and self.export_xt_filename: self.append_data( file_name=self.export_xt_filename.replace('_n_', f'_2nd_'), dict_data=cur_signal, @@ -6304,6 +6305,23 @@ class CtaLineBar(object): else: self.xt_2nd_signals[-1].update({'end': self.cur_bi.end, 'price': price, 'signal': qsbc_2nd}) + def get_xt_signal(self, xt_name, x=0): + """ + 获取n笔形态/信号的倒x笔结果 + :param n: + :param x: 倒x笔,如倒1笔 + :return: {} + """ + xt_signals = getattr(self, xt_name) + if xt_signals is None: + return {} + + if len(xt_signals) > x: + return xt_signals[-1-x] + else: + return {} + + def write_log(self, content): """记录CTA日志""" self.strategy.write_log(u'[' + self.name + u']' + content) diff --git a/vnpy/component/cta_utility.py b/vnpy/component/cta_utility.py index 40add07e..b8b1c0f7 100644 --- a/vnpy/component/cta_utility.py +++ b/vnpy/component/cta_utility.py @@ -96,7 +96,7 @@ def check_bi_not_rt(kline, direction: Direction) -> bool: and kline.cur_fenxing.index == kline.index_list[-1] \ and kline.line_bar[-1].datetime.strftime('%Y-%m-%d %H:%M:%S') > kline.cur_fenxing.index \ and kline.line_bar[-1].low_price > float(kline.cur_fenxing.low) \ - and kline.line_bar[-1].high_price < kline.line_bar[-2].high_price: + and kline.line_bar[-1].high_price > kline.line_bar[-2].high_price: return True return False @@ -382,7 +382,7 @@ def check_chan_xt_five_bi(kline, bi_list: List[ChanObject]): # 五笔三买,要求bi_5.high是最高点, 或者bi_4.height,超过笔2、笔3两倍 if min_low < max(bi_1.low, bi_3.low) < min(bi_1.high, bi_3.high) < bi_5.low: - if bi_5.high == max_high: + if bi_5.high == max_high: # and max(bi_1.high, bi_3.high) < bi_5.low v = ChanSignals.LI0.value # 类三买, 五笔 elif bi_3.low == min_low and bi_1.high == max_high \ and bi_4.height > max(bi_1.height, bi_2.height, bi_3.height) \ @@ -418,7 +418,7 @@ def check_chan_xt_five_bi(kline, bi_list: List[ChanObject]): # 五笔三卖,要求bi_5.low是最低点,中枢可能是1~3 if min(bi_1.high, bi_3.high) > max(bi_1.low, bi_3.low) > bi_5.high: - if bi_5.low == min_low: + if bi_5.low == min_low: # and min(bi_1.low, bi_3.low) > bi_5.high return ChanSignals.SI0.value elif bi_3.high == max_high and bi_1.low == min_low \ and bi_4.height > max(bi_1.height, bi_2.height, bi_3.height) \ @@ -572,7 +572,7 @@ def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): and min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) > bi_6.high \ and min(bi_6.high, bi_8.high) > max(bi_6.low, bi_8.low) \ and min(bi_2.low, bi_4.low) > max(bi_6.high, bi_8.high) \ - and bi_9.height < bi_5.height and bi_9.atan <= bi_5.atan: + and (bi_9.height < bi_5.height or bi_9.atan <= bi_5.atan): return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔aAb式') # 九笔GG 类三买(1357构成中枢,最低点在3或5) @@ -639,7 +639,7 @@ def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): and bi_6.low > min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) \ and min(bi_6.high, bi_8.high) > max(bi_6.low, bi_8.low) \ and max(bi_2.high, bi_4.high) < min(bi_6.low, bi_8.low) \ - and bi_9.height < bi_5.height and bi_9.atan <= bi_5.atan: + and (bi_9.height < bi_5.height or bi_9.atan <= bi_5.atan): return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔aAbBc式') # 九笔类三卖 @@ -812,7 +812,7 @@ def check_chan_xt_thirteen_bi(kline, bi_list: List[ChanObject]): if direction == -1: if min_low == bi_13.low and max_high == bi_1.high: # ABC式类一买,A5B3C5 - if bi_5.low < min(bi_1.low, bi_3.low) and bi_9.high > max(bi_11.high, bi_13.high) \ + if bi_5.low < max(bi_1.low, bi_3.low) and bi_9.high > max(bi_11.high, bi_13.high) \ and bi_8.high > bi_6.low and bi_1.high - bi_5.low > bi_9.high - bi_13.low: return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="13笔A5B3C5式") @@ -827,12 +827,13 @@ def check_chan_xt_thirteen_bi(kline, bi_list: List[ChanObject]): and min(bi_6.high, bi_8.high, bi_10.high) > max(bi_6.low, bi_8.low, bi_10.low) \ and bi_1.high - bi_5.low > bi_11.high - bi_13.low: return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="13笔A5B5C3式") + # AB式底背驰, aAbBc # 上涨线段时,判断背驰类型 elif direction == 1: if max_high == bi_13.high and min_low == bi_1.low: # ABC式顶背驰,A5B3C5 - if bi_5.high > max(bi_3.high, bi_1.high) and bi_9.low < min(bi_11.low, bi_13.low) \ + if bi_5.high > min(bi_3.high, bi_1.high) and bi_9.low < min(bi_11.low, bi_13.low) \ and bi_8.low < bi_6.high and bi_5.high - bi_1.low > bi_13.high - bi_9.low: return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="13笔A5B3C5式") diff --git a/vnpy/data/tdx/tdx_stock_data.py b/vnpy/data/tdx/tdx_stock_data.py index 2ef2c9ff..1c58811c 100644 --- a/vnpy/data/tdx/tdx_stock_data.py +++ b/vnpy/data/tdx/tdx_stock_data.py @@ -16,6 +16,8 @@ import pickle import bz2 import traceback import pandas as pd +import random +from time import sleep from datetime import datetime, timedelta from logging import ERROR @@ -94,8 +96,8 @@ class TdxStockData(object): self.config = get_cache_config(TDX_STOCK_CONFIG) self.symbol_dict = self.config.get('symbol_dict', {}) self.cache_time = self.config.get('cache_time', datetime.now() - timedelta(days=7)) - self.best_ip = self.config.get('best_ip',{}) - self.exclude_ips = self.config.get('exclude_ips',[]) + self.best_ip = self.config.get('best_ip', {}) + self.exclude_ips = self.config.get('exclude_ips', []) if len(self.symbol_dict) == 0 or self.cache_time < datetime.now() - timedelta(days=1): self.cache_config() @@ -201,8 +203,8 @@ class TdxStockData(object): self.connection_status = True except Exception as ex: - self.write_log(u'连接服务器{}tdx异常:{},{}'.format(self.best_ip,str(ex), traceback.format_exc())) - cur_ip = self.best_ip.get('ip',None) + self.write_log(u'连接服务器{}tdx异常:{},{}'.format(self.best_ip, str(ex), traceback.format_exc())) + cur_ip = self.best_ip.get('ip', None) if cur_ip is not None and cur_ip not in self.exclude_ips: self.write_log(f'排除{cur_ip}') self.exclude_ips.append(cur_ip) @@ -320,7 +322,7 @@ class TdxStockData(object): self.write_log('{}开始下载tdx股票: {},代码:{} {}数据, {} to {}.' .format(datetime.now(), name, tdx_code, tdx_period, qry_start_date, qry_end_date)) - stock_type = get_stock_type(tdx_code,market_id) + stock_type = get_stock_type(tdx_code, market_id) if stock_type == 'index_cn': get_bar_func = self.api.get_index_bars else: @@ -349,6 +351,7 @@ class TdxStockData(object): if len(_bars) == 0: self.write_error('{} Handling {}, len1={}..., continue'.format( str(datetime.now()), tdx_code, len(_bars))) + sleep(3 * random.random()) return False, ret_bars current_datetime = datetime.now() diff --git a/vnpy/trader/engine.py b/vnpy/trader/engine.py index 773394ef..d0af5f6b 100644 --- a/vnpy/trader/engine.py +++ b/vnpy/trader/engine.py @@ -92,7 +92,7 @@ class MainEngine: # 缺省使用了接口自己定义的gateway_name gateway = gateway_class(self.event_engine) gateway_name = gateway.gateway_name - + self.write_log(f'添加{gateway_name}网关') self.gateways[gateway_name] = gateway # Add gateway supported exchanges into engine diff --git a/vnpy/trader/ui/kline/kline.py b/vnpy/trader/ui/kline/kline.py index 4190029d..39f33d16 100644 --- a/vnpy/trader/ui/kline/kline.py +++ b/vnpy/trader/ui/kline/kline.py @@ -1563,9 +1563,12 @@ class GridKline(QtWidgets.QWidget): # 配置项12: bi_file / duan_file / bi_zs_file / duan_zs_file,支持缠论的画线 - def __init__(self, parent=None, kline_settings={}, title='', relocate=True): + def __init__(self, parent=None, kline_settings={}, title='', relocate=True,screen_file=""): self.parent = parent super(GridKline, self).__init__(parent) + self.width = 1920 + self.height = 1080 + # widget的标题 if title: self.setWindowTitle(title) @@ -1590,6 +1593,9 @@ class GridKline(QtWidgets.QWidget): self.setLayout(self.grid_layout) self.relocate = relocate + + self.screen_file = screen_file + self.init_ui() def init_ui(self): @@ -1598,13 +1604,14 @@ class GridKline(QtWidgets.QWidget): id = 1 for kline_name, kline_setting in self.kline_settings.items(): - canvas = getattr(self, f'canvas_{id}') + canvas = getattr(self, f'canvas_{id}',None) if id > 8: print(f'最多支持8个K线同时展现', file=sys.stderr) continue - - # 创建K线图表 - canvas = KLineWidget(display_vol=False, display_sub=True) + if canvas is None: + # 创建K线图表 + canvas = KLineWidget(display_vol=False, display_sub=True) + setattr(self, f'canvas_{id}', canvas) canvas.show() # K线标题 canvas.KLtitle.setText(f'{kline_name}', size='18pt') @@ -1644,11 +1651,16 @@ class GridKline(QtWidgets.QWidget): if len(kline_names) == 0: break row += 1 - - self.show() + if len(self.screen_file) == 0: + self.show() self.load_multi_kline() + if len(self.screen_file) > 0: + p = self.grab() + p.save(self.screen_file,'png') + self.close() + # ---------------------------------------------------------------------- def load_multi_kline(self): """加载多周期窗口""" diff --git a/vnpy/trader/ui/kline/ui_snapshot.py b/vnpy/trader/ui/kline/ui_snapshot.py index e65d5aa7..db3e2378 100644 --- a/vnpy/trader/ui/kline/ui_snapshot.py +++ b/vnpy/trader/ui/kline/ui_snapshot.py @@ -10,8 +10,9 @@ import ctypes import bz2 import pickle import zlib +from time import sleep import pandas as pd - +from pyqtgraph import QtGui from vnpy.trader.ui.kline.crosshair import Crosshair from vnpy.trader.ui.kline.kline import * @@ -29,7 +30,8 @@ class UiSnapshot(object): tns_file: str = "", dist_file: str = "", dist_include_list=[], - use_grid=True): + use_grid=True, + export_file=""): """ 显示切片 :param snapshot_file: 切片文件路径(通过这个方法,可读取历史切片) @@ -119,14 +121,38 @@ class UiSnapshot(object): k: setting } ) - # K线界面 - try: - if use_grid: - w = GridKline(kline_settings=kline_settings, title=d.get('strategy', ''), relocate=True) - w.showMaximized() - else: - w = MultiKlineWindow(kline_settings=kline_settings, title=d.get('strategy', '')) - w.showMaximized() + try: + if len(export_file) == 0: + # K线界面 + if use_grid: + w = GridKline(kline_settings=kline_settings, title=d.get('strategy', ''), relocate=True) + w.showMaximized() + else: + w = MultiKlineWindow(kline_settings=kline_settings, title=d.get('strategy', '')) + w.showMaximized() + else: + w = GridKline(kline_settings=kline_settings, title=d.get('strategy',''),relocate=False,screen_file=export_file) + print('sleep') + sleep(1) + print('close') + w.close() except Exception as ex: print(u'exception:{},trace:{}'.format(str(ex), traceback.format_exc())) + + def export(self, + snapshot_file: str, + d: dict = None, + trade_file: str = "", + tns_file: str = "", + dist_file: str = "", + dist_include_list=[], + export_file:str="" + ): + + self.show(snapshot_file=snapshot_file, + d=d, + trade_file=trade_file, + dist_file=dist_file, + dist_include_list=dist_include_list, + export_file=export_file) diff --git a/vnpy/trader/utility.py b/vnpy/trader/utility.py index c5c5389f..c8257193 100644 --- a/vnpy/trader/utility.py +++ b/vnpy/trader/utility.py @@ -350,7 +350,7 @@ def get_csv_last_dt(file_name, dt_index=0, dt_format='%Y-%m-%d %H:%M:%S', line_l if not os.path.exists(file_name): return False - with open(file_name, 'r') as f: + with open(file_name, 'r', encoding='utf8') as f: f_size = os.path.getsize(file_name) if f_size < line_length: line_length = f_size