diff --git a/prod/jobs/refill_tdx_stock_bars.py b/prod/jobs/refill_tdx_stock_bars.py index a6944b10..5d4be31c 100644 --- a/prod/jobs/refill_tdx_stock_bars.py +++ b/prod/jobs/refill_tdx_stock_bars.py @@ -3,6 +3,7 @@ 下载通达信股票合约1分钟&日线bar => vnpy项目目录/bar_data/ 上海股票 => SSE子目录 深圳股票 => SZSE子目录 +修改为多进程模式 """ import os import sys @@ -10,6 +11,10 @@ import csv import json from collections import OrderedDict import pandas as pd +from multiprocessing import Pool +from concurrent.futures import ThreadPoolExecutor + +from copy import copy vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) if vnpy_root not in sys.path: @@ -35,97 +40,163 @@ api_01 = TdxStockData() # 额外需要数据下载的基金列表 stock_list = load_json('stock_list.json') +# 强制更新缓存 +api_01.cache_config() symbol_dict = api_01.symbol_dict +# +# thread_executor = ThreadPoolExecutor(max_workers=1) +# thread_tasks = [] -# 下载所有的股票数据 -num_stocks = 0 -for period in ['1min', '1day']: - for symbol in symbol_dict.keys(): - symbol_info = symbol_dict[symbol] - stock_code = symbol_info['code'] - if ('stock_type' in symbol_info.keys() and symbol_info['stock_type'] in ['stock_cn', 'cb_cn']) or stock_code in stock_list: - # if stock_code in stock_list: - # print(symbol_info['code']) - if symbol_info['exchange'] == 'SZSE': - exchange_name = '深交所' - exchange = Exchange.SZSE - else: - exchange_name = '上交所' - exchange = Exchange.SSE - else: - continue - num_stocks += 1 - stock_name = symbol_info.get('name') - print(f'开始更新:{exchange_name}/{stock_name}, 代码:{stock_code}') - bar_file_folder = os.path.abspath(os.path.join(bar_data_folder, f'{exchange.value}')) - if not os.path.exists(bar_file_folder): - os.makedirs(bar_file_folder) - # csv数据文件名 - bar_file_path = os.path.abspath(os.path.join(bar_file_folder, f'{stock_code}_{period[0:2]}.csv')) +def refill(symbol_info): + period = symbol_info['period'] + progress = symbol_info['progress'] + # print("{}_{}".format(period, symbol_info['code'])) + # return + stock_code = symbol_info['code'] - # 如果文件存在, - if os.path.exists(bar_file_path): - # 取最后一条时间 - last_dt = get_csv_last_dt(bar_file_path) - else: - last_dt = None + # if stock_code in stock_list: + # print(symbol_info['code']) + if symbol_info['exchange'] == 'SZSE': + exchange_name = '深交所' + exchange = Exchange.SZSE + else: + exchange_name = '上交所' + exchange = Exchange.SSE - if last_dt: - start_dt = last_dt - timedelta(days=1) - print(f'文件{bar_file_path}存在,最后时间:{start_date}') - else: - start_dt = datetime.strptime(start_date, '%Y%m%d') - print(f'文件{bar_file_path}不存在,或读取最后记录错误,开始时间:{start_date}') + # num_stocks += 1 - result, bars = api_01.get_bars(symbol=stock_code, - period=period, - callback=None, - start_dt=start_dt, - return_bar=False) - # [dict] => dataframe - if not result or len(bars) == 0: - continue + stock_name = symbol_info.get('name') + print(f'开始更新:{exchange_name}/{stock_name}, 代码:{stock_code}') + bar_file_folder = os.path.abspath(os.path.join(bar_data_folder, f'{exchange.value}')) + if not os.path.exists(bar_file_folder): + os.makedirs(bar_file_folder) + # csv数据文件名 + p_name = period.replace('min', 'm').replace('day', 'd').replace('hour', 'h') + bar_file_path = os.path.abspath(os.path.join(bar_file_folder, f'{stock_code}_{p_name}.csv')) - # 全新数据 - if last_dt is None: - data_df = pd.DataFrame(bars) - data_df.set_index('datetime', inplace=True) - data_df = data_df.sort_index() - # print(data_df.head()) - print(data_df.tail()) - data_df.to_csv(bar_file_path, index=True) - print(f'首次更新{stock_code} {stock_name}数据 => 文件{bar_file_path}') + # 如果文件存在, + if os.path.exists(bar_file_path): + # 取最后一条时间 + last_dt = get_csv_last_dt(bar_file_path) + else: + last_dt = None - # 增量更新 - else: - # 获取标题 - headers = [] - with open(bar_file_path, "r", encoding='utf8') as f: - reader = csv.reader(f) - for header in reader: - headers = header - break + if last_dt: + start_dt = last_dt - timedelta(days=1) + print(f'文件{bar_file_path}存在,最后时间:{start_dt}') + else: + start_dt = datetime.strptime(start_date, '%Y%m%d') + print(f'文件{bar_file_path}不存在,或读取最后记录错误,开始时间:{start_date}') - bar_count = 0 - # 写入所有大于最后bar时间的数据 - # with open(bar_file_path, 'a', encoding='utf8', newline='\n') as csvWriteFile: - with open(bar_file_path, 'a', encoding='utf8') as csvWriteFile: + d1 = datetime.now() + result, bars = api_01.get_bars(symbol=stock_code, + period=period, + callback=None, + start_dt=start_dt, + return_bar=False) + # [dict] => dataframe + if not result or len(bars) == 0: + return - writer = csv.DictWriter(f=csvWriteFile, fieldnames=headers, dialect='excel', - extrasaction='ignore') - for bar in bars: - if bar['datetime'] <= last_dt: - continue - bar_count += 1 - writer.writerow(bar) + need_resample = False + # 全新数据 + if last_dt is None: + data_df = pd.DataFrame(bars) + data_df.set_index('datetime', inplace=True) + data_df = data_df.sort_index() + # print(data_df.head()) + print(data_df.tail()) + data_df.to_csv(bar_file_path, index=True) + d2 = datetime.now() + microseconds = (d1 - d1).microseconds + print(f'{progress}% 首次更新{stock_code} {stock_name}数据 {microseconds} 毫秒=> 文件{bar_file_path}') + need_resample = True - print(f'更新{stock_code} {stock_name} 数据 => 文件{bar_file_path}, 最后记录:{bars[-1]}') + # 增量更新 + else: + # 获取标题 + headers = [] + with open(bar_file_path, "r", encoding='utf8') as f: + reader = csv.reader(f) + for header in reader: + headers = header + break - # 输出 5、15、30分钟的数据 - if period == '1min': - out_files, err_msg = resample_bars_file(vnpy_root=vnpy_root, symbol=stock_code, exchange=exchange, x_mins=[5,15,30]) + bar_count = 0 + # 写入所有大于最后bar时间的数据 + # with open(bar_file_path, 'a', encoding='utf8', newline='\n') as csvWriteFile: + with open(bar_file_path, 'a', encoding='utf8') as csvWriteFile: -msg = 'tdx股票数据补充完毕: num_stocks={}'.format(num_stocks) -send_wx_msg(content=msg) -os._exit(0) + writer = csv.DictWriter(f=csvWriteFile, fieldnames=headers, dialect='excel', + extrasaction='ignore') + for bar in bars: + if bar['datetime'] <= last_dt: + continue + bar_count += 1 + writer.writerow(bar) + if not need_resample: + need_resample = True + d2 = datetime.now() + microseconds = round((d2 - d1).microseconds / 100, 0) + print(f'{progress}%,更新{stock_code} {stock_name} 数据 {microseconds}毫秒 => 文件{bar_file_path}, 最后记录:{bars[-1]}') + + # 采用多线程方式输出 5、15、30分钟的数据 + # if period == '1min' and need_resample: + # task = thread_executor.submit(resample, stock_code, exchange, [5, 15, 30]) + # thread_tasks.append(task) + + +def resample(symbol, exchange, x_mins=[5, 15, 30]): + """ + 更新多周期文件 + :param symbol: + :param exchange: + :param x_mins: + :return: + """ + d1 = datetime.now() + out_files, err_msg = resample_bars_file(vnpy_root=vnpy_root, + symbol=symbol, + exchange=exchange, + x_mins=x_mins) + d2 = datetime.now() + microseconds = round((d2 - d1).microseconds / 100, 0) + if len(err_msg) > 0: + print(err_msg, file=sys.stderr) + + if out_files: + print(f'{microseconds}毫秒,生成 =>{out_files}') + + +if __name__ == '__main__': + + # 下载所有的股票数据 + num_progress = 0 + total_tasks = len(symbol_dict.keys()) * 2 + tasks = [] + for period in ['1min', '5min', '15min', '30min', '1hour', '1day']: + 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: + info['period'] = period + tasks.append(info) + # if len(tasks) > 12: + # break + + total_tasks = len(tasks) + for task in tasks: + num_progress += 1 + task['progress'] = round(100 * num_progress / total_tasks, 2) + + p = Pool(12) + p.map(refill, tasks) + p.close() + p.join() + + # + msg = 'tdx股票数据补充完毕: num_stocks={}'.format(total_tasks) + send_wx_msg(content=msg) + os._exit(0) diff --git a/vnpy/app/cta_crypto/back_testing.py b/vnpy/app/cta_crypto/back_testing.py index 4c1a9b82..2ac6aa45 100644 --- a/vnpy/app/cta_crypto/back_testing.py +++ b/vnpy/app/cta_crypto/back_testing.py @@ -1921,7 +1921,7 @@ class BackTestingEngine(object): # 返回回测结果 d = {} d['init_capital'] = self.init_capital - d['profit'] = self.cur_capital - self.init_capital + d['profit'] = self.net_capital - self.init_capital d['net_capital'] = self.net_capital d['max_capital'] = self.max_net_capital # 取消原 maxCapital @@ -2006,8 +2006,8 @@ class BackTestingEngine(object): result_info.update({u'期末资金': d['net_capital']}) self.output(u'期末资金:\t%s' % format_number(d['net_capital'])) - result_info.update({u'平仓盈亏': d['profit']}) - self.output(u'平仓盈亏:\t%s' % format_number(d['profit'])) + result_info.update({u'总盈亏': d['profit']}) + self.output(u'总盈亏:\t%s' % format_number(d['profit'])) result_info.update({u'资金最高净值': d['max_capital']}) self.output(u'资金最高净值:\t%s' % format_number(d['max_capital'])) diff --git a/vnpy/app/cta_crypto/template.py b/vnpy/app/cta_crypto/template.py index b28d1491..73b8cb66 100644 --- a/vnpy/app/cta_crypto/template.py +++ b/vnpy/app/cta_crypto/template.py @@ -1316,7 +1316,7 @@ class CtaFutureTemplate(CtaTemplate): up_grids_info = "" for grid in list(self.gt.up_grids): - if not grid.open_status and grid.order_status: + if grid.open_status and grid.order_status: up_grids_info += f'平空中: [已平:{grid.traded_volume} => 目标:{grid.volume}, 委托时间:{grid.order_time}]\n' if len(grid.order_ids) > 0: up_grids_info += f'委托单号:{grid.order_ids}' @@ -1333,7 +1333,7 @@ class CtaFutureTemplate(CtaTemplate): dn_grids_info = "" for grid in list(self.gt.dn_grids): - if not grid.open_status and grid.order_status: + if grid.open_status and grid.order_status: dn_grids_info += f'平多中: [已平:{grid.traded_volume} => 目标:{grid.volume}, 委托时间:{grid.order_time}]\n' if len(grid.order_ids) > 0: dn_grids_info += f'委托单号:{grid.order_ids}' diff --git a/vnpy/app/cta_strategy_pro/portfolio_testing.py b/vnpy/app/cta_strategy_pro/portfolio_testing.py index d8d1e8a0..c527a22e 100644 --- a/vnpy/app/cta_strategy_pro/portfolio_testing.py +++ b/vnpy/app/cta_strategy_pro/portfolio_testing.py @@ -452,7 +452,14 @@ class PortfolioTestingEngine(BackTestingEngine): ticks = [] if not os.path.isfile(file_path): self.write_log(u'{}文件不存在'.format(file_path)) - return None + file_path = os.path.abspath( + os.path.join( + tick_folder, + tick_date.strftime('%Y%m'), + '{}_{}.csv'.format(symbol.lower(), tick_date.strftime('%Y%m%d')))) + if not os.path.isfile(file_path): + self.write_log(u'{}文件不存在'.format(file_path)) + return None try: df = pd.read_csv(file_path, parse_dates=False) # datetime,symbol,exchange,last_price,highest,lowest,volume,amount,open_interest,upper_limit,lower_limit, @@ -589,8 +596,32 @@ class PortfolioTestingEngine(BackTestingEngine): time=dt.strftime('%H:%M:%S.%f'), trading_day=test_day.strftime('%Y-%m-%d'), last_price=last_price, - volume=tick_data['volume'] + volume=tick_data['volume'], + ask_price_1=float(tick_data.get('ask_price_1',0)), + ask_volume_1=int(tick_data.get('ask_volume_1',0)), + bid_price_1=float(tick_data.get('bid_price_1',0)), + bid_volume_1=int(tick_data.get('bid_volume_1',0)) ) + if tick_data.get('ask_price_5',0) > 0: + tick.ask_price_2 = float(tick_data.get('ask_price_2',0)) + tick.ask_volume_2 = int(tick_data.get('ask_volume_2', 0)) + tick.bid_price_2 = float(tick_data.get('bid_price_2', 0)) + tick.bid_volume_2 = int(tick_data.get('bid_volume_2', 0)) + + tick.ask_price_3 = float(tick_data.get('ask_price_3', 0)) + tick.ask_volume_3 = int(tick_data.get('ask_volume_3', 0)), + tick.bid_price_3 = float(tick_data.get('bid_price_3', 0)), + tick.bid_volume_3 = int(tick_data.get('bid_volume_3', 0)) + + tick.ask_price_4 = float(tick_data.get('ask_price_4', 0)) + tick.ask_volume_4 = int(tick_data.get('ask_volume_4', 0)), + tick.bid_price_4 = float(tick_data.get('bid_price_4', 0)), + tick.bid_volume_4 = int(tick_data.get('bid_volume_4', 0)) + + tick.ask_price_5 = float(tick_data.get('ask_price_5', 0)) + tick.ask_volume_5 = int(tick_data.get('ask_volume_5', 0)), + tick.bid_price_5 = float(tick_data.get('bid_price_5', 0)), + tick.bid_volume_5 = int(tick_data.get('bid_volume_5', 0)) self.new_tick(tick) diff --git a/vnpy/component/base.py b/vnpy/component/base.py index ac8458a1..5cdfd638 100644 --- a/vnpy/component/base.py +++ b/vnpy/component/base.py @@ -5,6 +5,9 @@ import sys from abc import ABC from enum import Enum from logging import INFO, ERROR +import json +import numpy as np +import datetime from vnpy.trader.constant import Direction # noqa @@ -71,3 +74,20 @@ class CtaComponent(ABC): self.strategy.write_log(msg=content, level=level) else: print(content, file=sys.stderr) + + +class MyEncoder(json.JSONEncoder): + """ + 自定义转换器,处理np,datetime等不能被json转换得问题 + """ + def default(self, obj): + if isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, np.ndarray): + return obj.tolist() + elif isinstance(obj, datetime): + return obj.strftime('%Y-%m-%d %H:%M:%S') + else: + return super(MyEncoder, self).default(obj) diff --git a/vnpy/component/cta_grid_trade.py b/vnpy/component/cta_grid_trade.py index 012ba37e..a629e794 100644 --- a/vnpy/component/cta_grid_trade.py +++ b/vnpy/component/cta_grid_trade.py @@ -10,7 +10,7 @@ import traceback from collections import OrderedDict from datetime import datetime from vnpy.trader.utility import get_folder_path -from vnpy.component.base import Direction, CtaComponent +from vnpy.component.base import Direction, CtaComponent,MyEncoder """ 网格交易,用于套利单 @@ -894,7 +894,7 @@ class CtaGridTrade(CtaComponent): data = self.to_json() with open(grid_json_file, 'w', encoding='utf8') as f: - json_data = json.dumps(data, indent=4, ensure_ascii=False) + json_data = json.dumps(data, indent=4, ensure_ascii=False, cls=MyEncoder) f.write(json_data) self.write_log(u'GrideTrade保存文件{}完成'.format(grid_json_file)) diff --git a/vnpy/component/cta_line_bar.py b/vnpy/component/cta_line_bar.py index c4c9d36c..9149c980 100644 --- a/vnpy/component/cta_line_bar.py +++ b/vnpy/component/cta_line_bar.py @@ -28,8 +28,9 @@ from vnpy.component.base import ( MARKET_ZJ) from vnpy.component.cta_period import CtaPeriod, Period from vnpy.trader.object import BarData, TickData -from vnpy.trader.constant import Interval, Color +from vnpy.trader.constant import Interval, Color, ChanSignals from vnpy.trader.utility import round_to, get_trading_date, get_underlying_symbol +from vnpy.component.cta_utility import check_chan_xt,check_chan_xt_three_bi, check_qsbc_2nd try: from vnpy.component.chanlun import ChanGraph, ChanLibrary @@ -206,6 +207,8 @@ class CtaLineBar(object): self.export_bi_filename = None # 通过缠论笔csv文件 self.export_zs_filename = None # 通过缠论的笔中枢csv文件 self.export_duan_filename = None # 通过缠论的线段csv文件 + self.export_xt_filename = None # 缠论笔的形态csv文件, 满足 strategy_name_xt_n_signals.csv + # n 会转换为 3,5,7,9,11,13 self.pre_bi_start = None # 前一个笔的时间 self.pre_zs_start = None # 前一个中枢的时间 @@ -295,6 +298,8 @@ class CtaLineBar(object): self.param_list.append('para_ema1_len') # 三条EMA均线 self.param_list.append('para_ema2_len') self.param_list.append('para_ema3_len') + self.param_list.append('para_ema4_len') + self.param_list.append('para_ema5_len') self.param_list.append('para_dmi_len') self.param_list.append('para_dmi_max') @@ -365,6 +370,7 @@ class CtaLineBar(object): self.param_list.append('para_bd_len') self.param_list.append('para_active_chanlun') # 激活缠论 + self.param_list.append('para_active_chan_xt') # 激活缠论的形态分析 def init_properties(self): """ @@ -417,6 +423,8 @@ class CtaLineBar(object): self.para_ema1_len = 0 # 13 # 第一根EMA均线的周期长度 self.para_ema2_len = 0 # 21 # 第二根EMA均线的周期长度 self.para_ema3_len = 0 # 120 # 第三根EMA均线的周期长度 + self.para_ema4_len = 0 # 120 # 第四根EMA均线的周期长度 + self.para_ema5_len = 0 # 120 # 第五根EMA均线的周期长度 self.para_dmi_len = 0 # 14 # DMI的计算周期 self.para_dmi_max = 0 # 30 # Dpi和Mdi的突破阈值 @@ -538,10 +546,14 @@ class CtaLineBar(object): self.line_ema1 = [] # K线的EMA1均线,周期是para_ema1_len1,不包含当前bar self.line_ema2 = [] # K线的EMA2均线,周期是para_ema1_len2,不包含当前bar self.line_ema3 = [] # K线的EMA3均线,周期是para_ema1_len3,不包含当前bar + self.line_ema4 = [] # K线的EMA4均线,周期是para_ema1_len4,不包含当前bar + self.line_ema5 = [] # K线的EMA5均线,周期是para_ema1_len5,不包含当前bar self._rt_ema1 = None # K线的实时EMA(para_ema1_len) self._rt_ema2 = None # K线的实时EMA(para_ema2_len) self._rt_ema3 = None # K线的实时EMA(para_ema3_len) + self._rt_ema4 = None # K线的实时EMA(para_ema4_len) + self._rt_ema5 = None # K线的实时EMA(para_ema5_len) # K线的DMI( Pdi,Mdi,ADX,Adxr) 计算数据 self.cur_pdi = 0 # bar内的升动向指标,即做多的比率 @@ -793,6 +805,14 @@ class CtaLineBar(object): self._bi_zs_list = [] # 笔中枢列表 self._duan_list = [] # 段列表 self._duan_zs_list = [] # 段中枢列表 + self.para_active_chan_xt = False # 是否激活czsc缠论形态识别 + self.xt_3_signals = [] # czsc 三笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_5_signals = [] # czsc 五笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_7_signals = [] # czsc 七笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_9_signals = [] # czsc 九笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_11_signals = [] # czsc 11笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_13_signals = [] # czsc 13笔信号列表 {'bi_start'最后一笔开始,'bi_end'最后一笔结束,'signal'} + self.xt_2nd_signals = [] # 趋势背驰2买或趋势背驰2卖信号 def set_params(self, setting: dict = {}): """设置参数""" @@ -828,7 +848,7 @@ class CtaLineBar(object): return self.cur_datetime = tick.datetime - self.cur_tick = copy.copy(tick) + self.cur_tick = tick #copy.copy(tick) # 兼容 标准套利合约,它没有last_price if self.cur_tick.last_price is None or self.cur_tick.last_price == 0: @@ -842,7 +862,7 @@ class CtaLineBar(object): self.cur_price = self.cur_tick.last_price # 3.生成x K线,若形成新Bar,则触发OnBar事件 - self.generate_bar(copy.copy(self.cur_tick)) + self.generate_bar(self.cur_tick) # copy.copy(self.cur_tick) # 更新curPeriod的High,low if self.cur_period is not None: @@ -864,8 +884,8 @@ class CtaLineBar(object): self.cur_datetime = bar.datetime + timedelta(minutes=bar_freq) if self.bar_len == 0: - new_bar = copy.deepcopy(bar) - self.line_bar.append(new_bar) + #new_bar = copy.deepcopy(bar) + self.line_bar.append(bar) self.cur_trading_day = bar.trading_day self.on_bar(bar) return @@ -910,8 +930,8 @@ class CtaLineBar(object): if is_new_bar: # 添加新的bar - new_bar = copy.deepcopy(bar) - self.line_bar.append(new_bar) + #new_bar = copy.deepcopy(bar) + self.line_bar.append(bar) # new_bar # 将上一个Bar推送至OnBar事件 self.on_bar(lastBar) @@ -1008,6 +1028,9 @@ class CtaLineBar(object): # 输出缠论=》csv文件 self.export_chan() + # 识别缠论分笔形态 + self.update_chan_xt() + # 回调上层调用者,将合成的 x分钟bar,回调给策略 def on_bar_x(self, bar: BarData):函数 if self.cb_on_bar: self.cb_on_bar(bar=bar) @@ -1121,6 +1144,12 @@ class CtaLineBar(object): if self.para_ema3_len > 0 and len(self.line_ema3) > 0: msg = msg + u',EMA({0}):{1}'.format(self.para_ema3_len, self.line_ema3[-1]) + if self.para_ema4_len > 0 and len(self.line_ema4) > 0: + msg = msg + u',EMA({0}):{1}'.format(self.para_ema4_len, self.line_ema4[-1]) + + if self.para_ema5_len > 0 and len(self.line_ema5) > 0: + msg = msg + u',EMA({0}):{1}'.format(self.para_ema5_len, self.line_ema5[-1]) + if self.para_dmi_len > 0 and len(self.line_pdi) > 0: msg = msg + u',Pdi:{1};Mdi:{1};Adx:{2}'.format(self.line_pdi[-1], self.line_mdi[-1], self.line_adx[-1]) @@ -2506,13 +2535,16 @@ class CtaLineBar(object): def __count_ema(self): """计算K线的EMA1 和EMA2""" - if not (self.para_ema1_len > 0 or self.para_ema2_len > 0 or self.para_ema3_len > 0): # 不计算 + if not (self.para_ema1_len > 0 or self.para_ema2_len > 0 or self.para_ema3_len > 0 + or self.para_ema4_len > 0 or self.para_ema5_len > 0): # 不计算 return ema1_data_len = min(self.para_ema1_len * 4, self.para_ema1_len + 40) if self.para_ema1_len > 0 else 0 ema2_data_len = min(self.para_ema2_len * 4, self.para_ema2_len + 40) if self.para_ema2_len > 0 else 0 ema3_data_len = min(self.para_ema3_len * 4, self.para_ema3_len + 40) if self.para_ema3_len > 0 else 0 - max_data_len = max(ema1_data_len, ema2_data_len, ema3_data_len) + ema4_data_len = min(self.para_ema4_len * 4, self.para_ema4_len + 40) if self.para_ema4_len > 0 else 0 + ema5_data_len = min(self.para_ema5_len * 4, self.para_ema5_len + 40) if self.para_ema5_len > 0 else 0 + max_data_len = max(ema1_data_len, ema2_data_len, ema3_data_len, ema4_data_len, ema5_data_len) # 1、lineBar满足长度才执行计算 if self.bar_len < max_data_len: self.write_log(u'数据未充分,当前Bar数据数量:{0},计算EMA需要:{1}'. @@ -2524,7 +2556,7 @@ class CtaLineBar(object): count_len = min(self.para_ema1_len, self.bar_len - 1) # 3、获取前InputN周期(不包含当前周期)的K线 - barEma1 = ta.EMA(self.close_array[-ema1_data_len:], count_len)[-1] + barEma1 = ta.EMA(self.close_array[-self.para_ema1_len * 4:], count_len)[-1] if np.isnan(barEma1): return barEma1 = round(float(barEma1), self.round_n) @@ -2539,7 +2571,7 @@ class CtaLineBar(object): # 3、获取前InputN周期(不包含当前周期)的自适应均线 - barEma2 = ta.EMA(self.close_array[-ema2_data_len:], count_len)[-1] + barEma2 = ta.EMA(self.close_array[-self.para_ema2_len * 4:], count_len)[-1] if np.isnan(barEma2): return barEma2 = round(float(barEma2), self.round_n) @@ -2553,7 +2585,7 @@ class CtaLineBar(object): count_len = min(self.bar_len - 1, self.para_ema3_len) # 3、获取前InputN周期(不包含当前周期)的自适应均线 - barEma3 = ta.EMA(self.close_array[-ema3_data_len:], count_len)[-1] + barEma3 = ta.EMA(self.close_array[-self.para_ema3_len * 4:], count_len)[-1] if np.isnan(barEma3): return barEma3 = round(float(barEma3), self.round_n) @@ -2562,16 +2594,47 @@ class CtaLineBar(object): del self.line_ema3[0] self.line_ema3.append(barEma3) + # 计算第四条EMA均线 + if self.para_ema4_len > 0: + count_len = min(self.bar_len - 1, self.para_ema4_len) + + # 3、获取前InputN周期(不包含当前周期)的自适应均线 + barEma4 = ta.EMA(self.close_array[-self.para_ema4_len * 4:], count_len)[-1] + if np.isnan(barEma4): + return + barEma4 = round(float(barEma4), self.round_n) + + if len(self.line_ema4) > self.max_hold_bars: + del self.line_ema4[0] + self.line_ema4.append(barEma4) + + # 计算第五条EMA均线 + if self.para_ema5_len > 0: + count_len = min(self.bar_len - 1, self.para_ema5_len) + + # 3、获取前InputN周期(不包含当前周期)的自适应均线 + barEma5 = ta.EMA(self.close_array[-self.para_ema5_len * 4:], count_len)[-1] + if np.isnan(barEma5): + return + barEma5 = round(float(barEma5), self.round_n) + + if len(self.line_ema5) > self.max_hold_bars: + del self.line_ema5[0] + self.line_ema5.append(barEma5) + def rt_count_ema(self): """计算K线的EMA1 和EMA2""" - if not (self.para_ema1_len > 0 or self.para_ema2_len > 0 or self.para_ema3_len > 0): # 不计算 + if not (self.para_ema1_len > 0 or self.para_ema2_len > 0 or self.para_ema3_len > 0 + or self.para_ema4_len > 0 or self.para_ema5_len > 0): # 不计算 return ema1_data_len = min(self.para_ema1_len * 4, self.para_ema1_len + 40) if self.para_ema1_len > 0 else 0 ema2_data_len = min(self.para_ema2_len * 4, self.para_ema2_len + 40) if self.para_ema2_len > 0 else 0 ema3_data_len = min(self.para_ema3_len * 4, self.para_ema3_len + 40) if self.para_ema3_len > 0 else 0 - max_data_len = max(ema1_data_len, ema2_data_len, ema3_data_len) + ema4_data_len = min(self.para_ema4_len * 4, self.para_ema4_len + 40) if self.para_ema4_len > 0 else 0 + ema5_data_len = min(self.para_ema5_len * 4, self.para_ema5_len + 40) if self.para_ema5_len > 0 else 0 + max_data_len = max(ema1_data_len, ema2_data_len, ema3_data_len, ema4_data_len, ema5_data_len) # 1、lineBar满足长度才执行计算 if self.bar_len < max_data_len: return @@ -2581,7 +2644,7 @@ class CtaLineBar(object): count_len = min(self.para_ema1_len, self.bar_len) # 3、获取前InputN周期(不包含当前周期)的K线 - barEma1 = ta.EMA(np.append(self.close_array[-ema1_data_len:], [self.cur_price]), count_len)[-1] + barEma1 = ta.EMA(np.append(self.close_array[-self.para_ema1_len * 4:], [self.cur_price]), count_len)[-1] if np.isnan(barEma1): return self._rt_ema1 = round(float(barEma1), self.round_n) @@ -2592,7 +2655,7 @@ class CtaLineBar(object): # 3、获取前InputN周期(不包含当前周期)的自适应均线 - barEma2 = ta.EMA(np.append(self.close_array[-ema2_data_len:], [self.cur_price]), count_len)[-1] + barEma2 = ta.EMA(np.append(self.close_array[-self.para_ema2_len * 4:], [self.cur_price]), count_len)[-1] if np.isnan(barEma2): return self._rt_ema2 = round(float(barEma2), self.round_n) @@ -2602,11 +2665,31 @@ class CtaLineBar(object): count_len = min(self.bar_len, self.para_ema3_len) # 3、获取前InputN周期(不包含当前周期)的自适应均线 - barEma3 = ta.EMA(np.append(self.close_array[-ema3_data_len:], [self.cur_price]), count_len)[-1] + barEma3 = ta.EMA(np.append(self.close_array[-self.para_ema3_len * 4:], [self.cur_price]), count_len)[-1] if np.isnan(barEma3): return self._rt_ema3 = round(float(barEma3), self.round_n) + # 计算第四条EMA均线 + if self.para_ema4_len > 0: + count_len = min(self.bar_len, self.para_ema4_len) + + # 3、获取前InputN周期(不包含当前周期)的自适应均线 + barEma4 = ta.EMA(np.append(self.close_array[-self.para_ema4_len * 4:], [self.cur_price]), count_len)[-1] + if np.isnan(barEma4): + return + self._rt_ema4 = round(float(barEma4), self.round_n) + + # 计算第五条EMA均线 + if self.para_ema5_len > 0: + count_len = min(self.bar_len, self.para_ema5_len) + + # 3、获取前InputN周期(不包含当前周期)的自适应均线 + barEma5 = ta.EMA(np.append(self.close_array[-self.para_ema5_len * 4:], [self.cur_price]), count_len)[-1] + if np.isnan(barEma5): + return + self._rt_ema5 = round(float(barEma5), self.round_n) + @property def rt_ema1(self): self.check_rt_funcs(self.rt_count_ema) @@ -2628,6 +2711,20 @@ class CtaLineBar(object): return self.line_ema3[-1] return self._rt_ema3 + @property + def rt_ema4(self): + self.check_rt_funcs(self.rt_count_ema) + if self._rt_ema4 is None and len(self.line_ema4) > 0: + return self.line_ema4[-1] + return self._rt_ema4 + + @property + def rt_ema5(self): + self.check_rt_funcs(self.rt_count_ema) + if self._rt_ema5 is None and len(self.line_ema5) > 0: + return self.line_ema5[-1] + return self._rt_ema5 + def __count_dmi(self): """计算K线的DMI数据和条件""" @@ -5806,7 +5903,7 @@ class CtaLineBar(object): return False # 当前段包含的分笔,必须大于3 - if len(cur_duan.bi_list) < 3: + if len(cur_duan.bi_list) <= 3: return False # 获取倒数第二根同向分笔的结束dif值或macd值 @@ -6132,6 +6229,79 @@ class CtaLineBar(object): return False + def update_chan_xt(self): + """更新缠论形态""" + if not self.para_active_chan_xt: + return + bi_len = len(self.bi_list) + if bi_len < 3: + 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 + + for n in range(3, bi_n, 2): + # => 信号 + if n == 3: + signal = check_chan_xt_three_bi(self, self.bi_list[-n:]) + else: + signal = check_chan_xt(self, self.bi_list[-n:]) + # => 信号列表 + xt_signals = getattr(self, f'xt_{n}_signals') + if xt_signals is None: + continue + # => 上一信号 + cur_signal = xt_signals[-1] if len(xt_signals) > 0 else None + # 不同笔开始时间 + if cur_signal is None or cur_signal.get("start", "") != self.cur_bi.start: + # 新增 + xt_signals.append({'start': self.cur_bi.start, + 'end': self.cur_bi.end, + 'price': price, + 'signal': signal}) + if len(xt_signals) > 200: + del xt_signals[0] + if cur_signal is not None and self.export_xt_filename : + self.append_data( + file_name=self.export_xt_filename.replace('_n_',f'_{n}_'), + dict_data=cur_signal, + field_names=["start", "end", "price", "signal"] + ) + # 直接更新 + else: + xt_signals[-1].update({'end': self.cur_bi.end, 'price': price,'signal': signal}) + + # 是否趋势二买 + qsbc_2nd = ChanSignals.Other.value + if self.cur_bi.direction == -1: + if check_qsbc_2nd(big_kline=self, small_kline=None, signal_direction=Direction.LONG): + qsbc_2nd = ChanSignals.Q2L0.value + else: + if check_qsbc_2nd(big_kline=self, small_kline=None,signal_direction=Direction.SHORT): + qsbc_2nd = ChanSignals.Q2S0.value + cur_signal = self.xt_2nd_signals[-1] if len(self.xt_2nd_signals) > 0 else None + # 不同笔开始时间 + if cur_signal is None or cur_signal.get("start", "") != self.cur_bi.start: + # 新增 + self.xt_2nd_signals.append({'start': self.cur_bi.start, + 'end': self.cur_bi.end, + 'price': price, + 'signal': qsbc_2nd}) + if cur_signal and self.export_xt_filename: + self.append_data( + file_name=self.export_xt_filename.replace('_n_', f'_2nd_'), + dict_data=cur_signal, + field_names=["start", "end", "price", "signal"] + ) + # 直接更新 + else: + self.xt_2nd_signals[-1].update({'end': self.cur_bi.end, 'price': price, 'signal': qsbc_2nd}) + + def write_log(self, content): """记录CTA日志""" @@ -6338,6 +6508,22 @@ class CtaLineBar(object): 'type': 'line' } indicators.update({indicator.get('name'): copy.copy(indicator)}) + if isinstance(self.para_ema4_len, int) and self.para_ema4_len > 0: + indicator = { + 'name': 'EMA{}'.format(self.para_ema4_len), + 'attr_name': 'line_ema4', + 'is_main': True, + 'type': 'line' + } + indicators.update({indicator.get('name'): copy.copy(indicator)}) + if isinstance(self.para_ema5_len, int) and self.para_ema5_len > 0: + indicator = { + 'name': 'EMA{}'.format(self.para_ema5_len), + 'attr_name': 'line_ema5', + 'is_main': True, + 'type': 'line' + } + indicators.update({indicator.get('name'): copy.copy(indicator)}) # MA 均线 (主图) if isinstance(self.para_ma1_len, int) and self.para_ma1_len > 0: @@ -6820,9 +7006,9 @@ class CtaMinuteBar(CtaLineBar): is_new_bar = True if is_new_bar: - new_bar = copy.deepcopy(bar) + #new_bar = copy.deepcopy(bar) # 添加新的bar - self.line_bar.append(new_bar) + self.line_bar.append(bar) # new_bar # 将上一个Bar推送至OnBar事件 self.on_bar(lastBar) else: @@ -7261,8 +7447,8 @@ class CtaDayBar(CtaLineBar): bar_len = len(self.line_bar) if bar_len == 0: - new_bar = copy.deepcopy(bar) - self.line_bar.append(new_bar) + #new_bar = copy.deepcopy(bar) + self.line_bar.append(bar) # new_bar self.cur_trading_day = bar.trading_day if bar.trading_day is not None else get_trading_date(bar.datetime) if bar_is_completed: self.on_bar(bar) @@ -7288,8 +7474,8 @@ class CtaDayBar(CtaLineBar): if is_new_bar: # 添加新的bar - new_bar = copy.deepcopy(bar) - self.line_bar.append(new_bar) + #new_bar = copy.deepcopy(bar) + self.line_bar.append(bar) # new_bar # 将上一个Bar推送至OnBar事件 self.on_bar(lastBar) else: diff --git a/vnpy/component/cta_period.py b/vnpy/component/cta_period.py index 6f99cf34..ee664389 100644 --- a/vnpy/component/cta_period.py +++ b/vnpy/component/cta_period.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -# 周期类,定义CTA的五种周期,及其周期变换矩阵 +# 周期状态类,定义CTA的多种周期状态,及其状态变换矩阵 from enum import Enum from datetime import datetime @@ -9,8 +9,12 @@ from datetime import datetime class Period(Enum): INIT = u'初始状态' LONG = u'多' + LONG_STOP = u'止涨' SHORT = u'空' + SHORT_STOP = u'止跌' SHOCK = u'震荡' + SHOCK_LONG = u'震荡偏多' + SHOCK_SHORT = u'震荡偏空' LONG_EXTREME = u'极端多' SHORT_EXTREME = u'极端空' diff --git a/vnpy/component/cta_policy.py b/vnpy/component/cta_policy.py index 1f8d994d..b977db13 100644 --- a/vnpy/component/cta_policy.py +++ b/vnpy/component/cta_policy.py @@ -4,7 +4,7 @@ import os import json from datetime import datetime from collections import OrderedDict -from vnpy.component.base import CtaComponent +from vnpy.component.base import CtaComponent, MyEncoder from vnpy.trader.utility import get_folder_path TNS_STATUS_OBSERVATE = 'observate' @@ -15,24 +15,6 @@ TNS_STATUS_CLOSED = 'closed' import numpy as np - -class MyEncoder(json.JSONEncoder): - """ - 自定义转换器,处理np,datetime等不能被json转换得问题 - """ - def default(self, obj): - if isinstance(obj, np.integer): - return int(obj) - elif isinstance(obj, np.floating): - return float(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, datetime): - return obj.strftime('%Y-%m-%d %H:%M:%S') - else: - return super(MyEncoder, self).default(obj) - - class CtaPolicy(CtaComponent): """ 策略的持久化Policy组件 diff --git a/vnpy/component/cta_utility.py b/vnpy/component/cta_utility.py index d4c4a191..246b0f1a 100644 --- a/vnpy/component/cta_utility.py +++ b/vnpy/component/cta_utility.py @@ -4,15 +4,16 @@ from vnpy.trader.constant import ChanSignals, Direction from vnpy.component.chanlun.pyChanlun import ChanBi, ChanDuan, ChanObject -from vnpy.component.cta_line_bar import CtaLineBar +# from vnpy.component.cta_line_bar import CtaLineBar from typing import List, Union # 所有底背驰信号集合 -DI_BEICHI_SIGNALS = [ChanSignals.LA0.value, ChanSignals.LA1.value, ChanSignals.LA2.value, ChanSignals.LA3.value, - ChanSignals.LB0.value, ChanSignals.LB1.value, ChanSignals.LB2.value, ChanSignals.LB3.value] +DI_BEICHI_SIGNALS = [ChanSignals.LA0.value, + ChanSignals.LB0.value] + # 所有顶背驰信号集合 -DING_BEICHI_SIGNALS = [ChanSignals.SA0.value, ChanSignals.SA1.value, ChanSignals.SA2.value, ChanSignals.SA3.value, - ChanSignals.SB0.value, ChanSignals.SB1.value, ChanSignals.SB2.value, ChanSignals.SB3.value] +DING_BEICHI_SIGNALS = [ChanSignals.SA0.value, + ChanSignals.SB0.value] def duan_bi_is_end(duan: ChanDuan, direction: Direction) -> bool: @@ -37,7 +38,7 @@ def duan_bi_is_end(duan: ChanDuan, direction: Direction) -> bool: return False -def check_duan_not_rt(kline: CtaLineBar, direction: Direction) -> bool: +def check_duan_not_rt(kline, direction: Direction) -> bool: """ 检查某一个K线当前线段是否非实时 :param kline: @@ -58,7 +59,7 @@ def check_duan_not_rt(kline: CtaLineBar, direction: Direction) -> bool: return True -def check_bi_not_rt(kline: CtaLineBar, direction: Direction) -> bool: +def check_bi_not_rt(kline, direction: Direction) -> bool: """ 检查某一个K线当前分笔是否非实时并符合判断方向 :param kline: @@ -83,9 +84,9 @@ def check_bi_not_rt(kline: CtaLineBar, direction: Direction) -> bool: if direction == 1: # 判断还没走完的bar,是否满足顶分型 if float(kline.cur_fenxing.high) == float(kline.high_array[-1]) \ - 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].high_price < float(kline.cur_fenxing.high) \ + 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].high_price < float(kline.cur_fenxing.high) \ and kline.line_bar[-1].low_price < kline.line_bar[-2].low_price: return True @@ -103,7 +104,7 @@ def check_bi_not_rt(kline: CtaLineBar, direction: Direction) -> bool: return True -def check_fx_power(kline: CtaLineBar, direction: Direction) -> str: +def check_fx_power(kline, direction: Direction) -> str: """ 获取分型强弱 :param kline: 本级别K线 @@ -165,7 +166,7 @@ def check_fx_power(kline: CtaLineBar, direction: Direction) -> str: return ret -def check_chan_xt(kline: CtaLineBar, bi_list: List[ChanObject]) -> str: +def check_chan_xt(kline, bi_list: List[ChanObject]) -> str: """ 获取缠论得形态 如果提供得是段内得bi_list,一般可以算出该线段是否有背驰, @@ -189,12 +190,12 @@ def check_chan_xt(kline: CtaLineBar, bi_list: List[ChanObject]) -> str: return v -def check_chan_xt_three_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_three_bi(kline, bi_list: List[ChanObject]): """ 获取指定3分笔得形态 (含有三笔) - :param kline: - :param bi_list: + :param kline: ctaLineBar对象 + :param bi_list: 笔列表 :return: """ v = ChanSignals.Other.value @@ -207,64 +208,148 @@ def check_chan_xt_three_bi(kline: CtaLineBar, bi_list: List[ChanObject]): # 最后一笔是下跌 if bi_3.direction == -1: # X3LA0~向下不重合 + # ^ + # / \ + # / \ + # / + # \ / + # \ / + # v if bi_3.low > bi_1.high: v = ChanSignals.X3LA0.value - # X3LB0~向下奔走型中枢 + # X3LB0~向下奔走型 + # ^ + # / \ + # / \ + # \ / \ + # \ / + # \ / + # v if bi_2.low < bi_3.low < bi_1.high < bi_2.high: v = ChanSignals.X3LB0.value - # X3LC0~向下三角收敛中枢 + # X3LC0~向下收敛 + # \ + # \ ^ + # \ / \ + # \ / \ + # \ / + # \ / + # v if bi_1.high > bi_3.high and bi_1.low < bi_3.low: v = ChanSignals.X3LC0.value - # 向下三角扩张中枢 + # X3LD0~向下扩张 + # ^ + # / \ + # \ / \ + # \ / \ + # v \ + # \ if bi_1.high < bi_3.high and bi_1.low > bi_3.low: v = ChanSignals.X3LD0.value - # X3LE0~向下盘背中枢, X3LF0~向下无背中枢 + # X3LE0~向下盘背, X3LF0~向下无背 if bi_3.low < bi_1.low and bi_3.high < bi_1.high: if bi_3.height < bi_1.height: - # X3LE0~向下盘背中枢 + # X3LE0~向下盘背 + # \ + # \ + # \ ^ + # \ / \ + # \ / \ + # v \ + # \ v = ChanSignals.X3LE0.value else: # X3LF0~向下无背中枢 + # \ + # \ ^ + # \ / \ + # \ / \ + # v \ + # \ + # \ v = ChanSignals.X3LF0.value # 上涨线段 elif bi_3.direction == 1: # X3SA0~向上不重合 - if bi_3.high > bi_1.low: + # ^ + # / \ + # / \ + # \ + # \ / + # \ / + # v + if bi_3.high < bi_1.low: v = ChanSignals.X3SA0.value - # X3SB0~向上奔走型中枢 + # X3SB0~向上奔走型 + # ^ + # / \ + # / \ / + # / \ / + # \ / + # v if bi_2.low < bi_1.low < bi_3.high < bi_2.high: v = ChanSignals.X3SB0.value - # X3SC0~向上三角收敛中枢 + # X3SC0~向上收敛 + # ^ + # / \ + # / \ / + # / \ / + # / v + # / if bi_1.high > bi_3.high and bi_1.low < bi_3.low: v = ChanSignals.X3SC0.value - # X3SD0~向上三角扩张中枢 + # X3SD0~向上扩张 + # / + # ^ / + # / \ / + # / \ / + # / \ / + # v if bi_1.high < bi_3.high and bi_1.low > bi_3.low: v = ChanSignals.X3SD0.value - # X3SE0~向上盘背中枢,X3SF0~向上无背中枢 + + # X3SE0~向上盘背,X3SF0~向上无背 if bi_3.low > bi_1.low and bi_3.high > bi_1.high: if bi_3.height < bi_1.height: - # X3SE0~向上盘背中枢 + # X3SE0~向上盘背 + # / + # ^ / + # / \ / + # / \ / + # / \ / + # / v + # / + # / v = ChanSignals.X3SE0.value else: - # X3SF0~向上无背中枢 + # X3SF0~向上无背 + # / + # / + # ^ / + # / \ / + # / \ / + # / \ / + # / v + # / v = ChanSignals.X3SF0.value return v -def check_chan_xt_five_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_five_bi(kline, bi_list: List[ChanObject]): """识别当前5笔形态 - :param cur_duan: 当前线段 - :return: str - """ + :param kline: ctaLineBar对象 + :param bi_list: 笔列表 + :return: str + """ v = ChanSignals.Other.value if len(bi_list) != 5: @@ -282,77 +367,77 @@ def check_chan_xt_five_bi(kline: CtaLineBar, bi_list: List[ChanObject]): # aAb式底背驰 if min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) and max_high == bi_1.high and bi_5.height < bi_1.height: if (min_low == bi_3.low and bi_5.low < bi_1.low) or (min_low == bi_5.low): - v = ChanSignals.LA0.value + return ChanSignals.LA0.value # "底背驰”五笔aAb式 # 类趋势底背驰( 笔5 的强度比笔1、笔3低) if max_high == bi_1.high and min_low == bi_5.low and bi_4.high < bi_2.low \ and bi_5.height < max(bi_3.height, bi_1.height) \ and bi_5.atan < max(bi_3.atan, bi_1.atan): - v = ChanSignals.LA0.value + return ChanSignals.LA0.value # "底背驰" 五笔类趋势 # 上颈线突破 if (min_low == bi_1.low and bi_5.high > min(bi_1.high, bi_2.high) > bi_5.low > bi_1.low) \ or (min_low == bi_3.low and bi_5.high > bi_3.high > bi_5.low > bi_3.low): - v = ChanSignals.LG0.value + return ChanSignals.LG0.value # 上颈线突破 五笔 # 五笔三买,要求bi_5.high是最高点, 或者bi_4.height,超过笔2、笔3两倍 - if max(bi_1.low, bi_3.low) < min(bi_1.high, bi_3.high) < bi_5.low: + 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: - v = ChanSignals.LI0.value - elif bi_4.low == min_low and bi_1.high == max_high \ + 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) \ and bi_4.height > 2 * max(bi_2.height, bi_3.height): - v = ChanSignals.LI0.value + v = ChanSignals.LI0.value # 类三买, 五笔 - # 向上三角扩张中枢 - if bi_1.high < bi_3.high < bi_5.high and bi_1.low > bi_3.low > bi_5.low: - v = ChanSignals.LJ0.value - - # 向上三角收敛中枢 - if bi_1.high > bi_3.high > bi_5.high and bi_1.low < bi_3.low < bi_5.low: - v = ChanSignals.LK0.value + # # 向上三角扩张中枢 + # if bi_1.high < bi_3.high < bi_5.high and bi_1.low > bi_3.low > bi_5.low: + # v = ChanSignals.LJ0.value + # + # # 向上三角收敛中枢 + # if bi_1.high > bi_3.high > bi_5.high and bi_1.low < bi_3.low < bi_5.low: + # v = ChanSignals.LK0.value # 上涨线段,寻找顶背驰 elif direction == 1: - # aAb式顶背驰 + # aAb式顶背驰,类一卖 if min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) and min_low == bi_1.low and bi_5.height < bi_1.height: if (max_high == bi_3.high and bi_5.high > bi_1.high) or (max_high == bi_5.high): - v = ChanSignals.SA0.value + return ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='五笔aAb式' - # 类趋势顶背驰 + # 类趋势顶背驰,类一卖 if min_low == bi_1.low and max_high == bi_5.high \ and bi_5.height < max(bi_1.height, bi_3.height) \ and bi_5.atan < max(bi_1.atan, bi_3.atan) \ and bi_4.low > bi_2.high: - v = ChanSignals.SA0.value + return ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='五笔类趋势') # 下颈线突破 if (max_high == bi_1.high and bi_5.low < max(bi_1.low, bi_2.low) < bi_5.high < max_high) \ or (max_high == bi_3.high and bi_5.low < bi_3.low < bi_5.high < max_high): - v = ChanSignals.SG0.value + return ChanSignals.SG0.value # k3='基础形态', v1='下颈线突破', v2='五笔') # 五笔三卖,要求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: - v = ChanSignals.SI0.value - elif bi_4.high == max_high and bi_1.low == min_low \ - and bi_4.height > max(bi_1.height, bi_2.height, bi_3.height)\ - and bi_4.height > 2 * max(bi_2.height,bi_3.height): - v = ChanSignals.SI0.value + 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) \ + and bi_4.height > 2 * max(bi_2.height, bi_3.height): + return ChanSignals.SI0.value # k3='基础形态', v1='类三卖', v2='五笔') # elif bi_1.high == max_high and bi_1.low == min_low: - # 向下三角扩张中枢 - if bi_1.high < bi_3.high < bi_5.high and bi_1.low > bi_3.low > bi_5.low: - v = ChanSignals.SJ0.value - - # 向下三角收敛中枢 - if bi_1.high > bi_3.high > bi_5.high and bi_1.low < bi_3.low < bi_5.low: - v = ChanSignals.SK0.value + # # 向下三角扩张中枢 + # if bi_1.high < bi_3.high < bi_5.high and bi_1.low > bi_3.low > bi_5.low: + # v = ChanSignals.SJ0.value + # + # # 向下三角收敛中枢 + # if bi_1.high > bi_3.high > bi_5.high and bi_1.low < bi_3.low < bi_5.low: + # v = ChanSignals.SK0.value return v -def check_chan_xt_seven_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_seven_bi(kline, bi_list: List[ChanObject]): """ 识别当前7笔的形态 :param cur_duan: @@ -371,80 +456,80 @@ def check_chan_xt_seven_bi(kline: CtaLineBar, bi_list: List[ChanObject]): # aAbcd式底背驰, d.高度斜率 小于 b.高度斜率 if min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) > bi_6.high \ and bi_7.height < bi_5.height and bi_7.atan <= bi_5.atan: - v = ChanSignals.LA0.value + v = ChanSignals.LA0.value # k3='基础形态', v1='底背驰', v2='七笔aAbcd式') # abcAd式底背驰 if bi_2.low > min(bi_4.high, bi_6.high) > max(bi_4.low, bi_6.low) \ and bi_7.height < (bi_1.high - bi_3.low) \ and bi_7.atan < (bi_1.atan + bi_3.atan) / 2: - v = ChanSignals.LA0.value + v = ChanSignals.LA0.value # k3='基础形态', v1='底背驰', v2='七笔abcAd式') # aAb式底背驰 if min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) \ and bi_7.height < bi_1.height and bi_7.atan <= bi_1.atan: - v = ChanSignals.LA0.value + v = ChanSignals.LA0.value # k3='基础形态', v1='底背驰', v2='七笔aAb式' # 类趋势底背驰 if bi_2.low > bi_4.high and bi_4.low > bi_6.high \ - and bi_7.height < max(bi_5.height, bi_3.height, bi_1.height)\ + and bi_7.height < max(bi_5.height, bi_3.height, bi_1.height) \ and bi_7.atan < max(bi_5.atan, bi_3.atan, bi_1.atan): - v = ChanSignals.LA0.value + v = ChanSignals.LA0.value # k3='基础形态', v1='底背驰', v2='七笔类趋势' # 向上中枢完成 if bi_4.low == min_low and min(bi_1.high, bi_3.high) > max(bi_1.low, bi_3.low) \ and min(bi_5.high, bi_7.high) > max(bi_5.low, bi_7.low) \ and max(bi_4.high, bi_6.high) > min(bi_3.high, bi_4.high): if max(bi_1.low, bi_3.low) < max(bi_5.high, bi_7.high): - v = ChanSignals.LH0.value + v = ChanSignals.LH0.value # k3='基础形态', v1='向上中枢完成', v2='七笔') - # 七笔三买,567回调 + # 七笔三买,567回调 :1~3构成中枢,最低点在1~3,最高点在5~7,5~7的最低点大于1~3的最高点 if bi_5.high == max_high and bi_5.high > bi_7.high \ and bi_5.low > bi_7.low > min(bi_1.high, bi_3.high) > max(bi_1.low, bi_3.low): - v = ChanSignals.LI0.value - # + v = ChanSignals.LI0.value # k3='基础形态', v1='类三买', v2='七笔' + elif bi_7.direction == 1: # 顶背驰 if bi_1.low == min_low and bi_7.high == max_high: # aAbcd式顶背驰 if bi_6.low > min(bi_2.high, bi_4.high) > max(bi_2.low, bi_4.low) \ and bi_7.height < bi_5.height and bi_7.atan <= bi_5.atan: - v = ChanSignals.SA0.value + v = ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='七笔aAbcd式' # abcAd式顶背驰 if min(bi_4.high, bi_6.high) > max(bi_4.low, bi_6.low) > bi_2.high \ and bi_7.height < (bi_3.high - bi_1.low) \ and bi_7.atan < (bi_1.atan + bi_3.atan) / 2: - v = ChanSignals.SA0.value + v = ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='七笔abcAd式' # aAb式顶背驰 if min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) \ and bi_7.height < bi_1.height and bi_7.atan <= bi_1.atan: - v = ChanSignals.SA0.value + v = ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='七笔aAb式' # 类趋势顶背驰 if bi_2.high < bi_4.low and bi_4.high < bi_6.low \ - and bi_7.height < max(bi_5.height, bi_3.height, bi_1.height)\ + and bi_7.height < max(bi_5.height, bi_3.height, bi_1.height) \ and bi_7.atan < max(bi_5.atan, bi_3.atan, bi_1.atan): - v = ChanSignals.SA0.value + v = ChanSignals.SA0.value # k3='基础形态', v1='顶背驰', v2='七笔类趋势' # 向下中枢完成 if bi_4.high == max_high and min(bi_1.high, bi_3.high) > max(bi_1.low, bi_3.low) \ and min(bi_5.high, bi_7.high) > max(bi_5.low, bi_7.low) \ and min(bi_4.low, bi_6.low) < max(bi_3.low, bi_4.low): if min(bi_1.high, bi_3.high) > min(bi_5.low, bi_7.low): - v = ChanSignals.SH0.value + v = ChanSignals.SH0.value # k3='基础形态', v1='向下中枢完成', v2='七笔' - # 七笔三卖,567回调,中枢可能在1~3 + # 七笔三卖,567回调 1~3构成中枢,最高点在1~3,最低点在5~7,5~7的最高点小于1~3的最低点 if bi_5.low == min_low and bi_5.low < bi_7.low \ and min(bi_1.high, bi_3.high) > max(bi_1.low, bi_3.low) > bi_7.high > bi_5.high: - v = ChanSignals.SI0.value + v = ChanSignals.SI0.value # k3='基础形态', v1='类三卖', v2='七笔' return v -def check_chan_xt_nine_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_nine_bi(kline, bi_list: List[ChanObject]): """ - 获取线段得形态(9分笔) + 获取线段得买卖点(9分笔) :param cur_duan: :return: """ @@ -460,72 +545,126 @@ def check_chan_xt_nine_bi(kline: CtaLineBar, bi_list: List[ChanObject]): # 依据最后一笔得方向进行判断 if direction == -1: if min_low == bi_9.low and max_high == bi_1.high: - # aAbBc式底背驰 - if 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_7.atan <= bi_5.atan: - v = ChanSignals.LA0.value - - # aAb式底背驰 + # aAb式类一买 if min(bi_2.high, bi_4.high, bi_6.high, bi_8.high) > max(bi_2.low, bi_4.low, bi_6.low, bi_8.low) \ and bi_9.height < bi_1.height and bi_9.atan <= bi_1.atan \ and bi_3.low >= bi_1.low and bi_7.high <= bi_9.high: - v = ChanSignals.LA0.value + return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔aAb式' - # aAbcd式底背驰 + # aAbcd式类一买 if min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) > bi_8.high \ and bi_9.height < bi_7.height and bi_9.atan <= bi_7.atan: - v = ChanSignals.LA0.value + return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔aAbcd式' - # ABC式底背驰 + # ABC式类一买 if bi_3.low < bi_1.low and bi_7.high > bi_9.high \ and min(bi_4.high, bi_6.high) > max(bi_4.low, bi_6.low) \ and (bi_1.high - bi_3.low) > (bi_7.high - bi_9.low): - v = ChanSignals.LA0.value + return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔ABC式' - # 九笔三买(789回调)中枢可能在3~7内 - if min_low == bi_1.low and max_high == bi_9.high \ - and bi_9.low > min([x.high for x in [bi_3, bi_5, bi_7]]) > max([x.low for x in [bi_3, bi_5, bi_7]]): - v = ChanSignals.LI0.value + # 类趋势一买 + if bi_8.high < bi_6.low < bi_6.high < bi_4.high < bi_2.low \ + and bi_9.atan < max([bi_1.atan, bi_3.atan, bi_5.atan, bi_7.atan]): + return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔类趋势' + + # 9笔 aAbBc式类一买(2~4构成中枢A,6~8构成中枢B,9背驰) + if max_high == max(bi_1.high, bi_3.high) and min_low == bi_9.low \ + 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: + return ChanSignals.Q1L0.value # k3='类买卖点', v1='类一买', v2='九笔aAb式') + + # 九笔GG 类三买(1357构成中枢,最低点在3或5) + if max_high == bi_9.high > bi_9.low \ + > max([x.high for x in [bi_1, bi_3, bi_5, bi_7]]) \ + > min([x.high for x in [bi_1, bi_3, bi_5, bi_7]]) \ + > max([x.low for x in [bi_1, bi_3, bi_5, bi_7]]) \ + > min([x.low for x in [bi_3, bi_5]]) == min_low: + return ChanSignals.Q3L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三买', v2='九笔GG三买') + + # 类三买(357构成中枢,8的力度小于2,9回调不跌破GG构成三买) + if bi_8.height < bi_2.height and max_high == bi_9.high > bi_9.low \ + > max([x.high for x in [bi_3, bi_5, bi_7]]) \ + > min([x.high for x in [bi_3, bi_5, bi_7]]) \ + > max([x.low for x in [bi_3, bi_5, bi_7]]) > bi_1.low == min_low: + return ChanSignals.Q3L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三买', v2='九笔GG三买') + + # # 九笔三买(789回调)中枢可能在3~7内 + # if min_low == bi_1.low and max_high == bi_9.high \ + # and bi_9.low > min([x.high for x in [bi_3, bi_5, bi_7]]) > max([x.low for x in [bi_3, bi_5, bi_7]]): + # v = ChanSignals.Q3L0.value + + if min_low == bi_5.low and max_high == bi_1.high and bi_4.high < bi_2.low: # 前五笔构成向下类趋势 + zd = max([x.low for x in [bi_5, bi_7]]) + zg = min([x.high for x in [bi_5, bi_7]]) + gg = max([x.high for x in [bi_5, bi_7]]) + if zg > zd and bi_8.high > gg: # 567构成中枢,且8的高点大于gg + if bi_9.low > zg: + return ChanSignals.Q3L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三买', v2='九笔ZG三买') + + # 类二买 + if bi_9.high > gg > zg > bi_9.low > zd: + return ChanSignals.Q2L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二买', v2='九笔') elif direction == 1: - if max_high == bi_9.high and min_low == bi_1.low: - # aAbBc式顶背驰 - if 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: - v = ChanSignals.SA0.value - # aAb式顶背驰 + # 倒9笔是最高点,倒一笔是最低点 + if max_high == bi_9.high and min_low == bi_1.low: + + # aAb式类一卖 if min(bi_2.high, bi_4.high, bi_6.high, bi_8.high) > max(bi_2.low, bi_4.low, bi_6.low, bi_8.low) \ and bi_9.height < bi_1.height and bi_9.atan <= bi_1.atan \ and bi_3.high <= bi_1.high and bi_7.low >= bi_9.low: - v = ChanSignals.SA0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔aAb式') - # aAbcd式顶背驰 + # aAbcd式类一卖 if bi_8.low > min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) \ and bi_9.height < bi_7.height and bi_9.atan <= bi_7.atan: - v = ChanSignals.SA0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔aAbcd式') - # ABC式顶背驰 + # ABC式类一卖 if bi_3.high > bi_1.high and bi_7.low < bi_9.low \ and min(bi_4.high, bi_6.high) > max(bi_4.low, bi_6.low) \ and (bi_3.high - bi_1.low) > (bi_9.high - bi_7.low): - v = ChanSignals.SA0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔ABC式') - # 九笔三卖 + # 类趋势类一卖 + if bi_8.low > bi_6.high > bi_6.low > bi_4.high > bi_4.low > bi_2.high \ + and bi_9.atan < max([bi_1.atan, bi_3.atan, bi_5.atan, bi_7.atan]): + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔类趋势') + + # aAbBc式类一卖 + if max_high == bi_9.high and min_low == min(bi_1.low, bi_3.low) \ + 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: + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2='九笔aAbBc式') + + # 九笔类三卖 if max_high == bi_1.high and min_low == bi_9.low \ and bi_9.high < max([x.low for x in [bi_3, bi_5, bi_7]]) < min([x.high for x in [bi_3, bi_5, bi_7]]): - v = ChanSignals.SI0.value + return ChanSignals.Q3S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三卖', v2='九笔') + + if min_low == bi_1.low and max_high == bi_5.high and bi_2.high < bi_4.low: # 前五笔构成向上类趋势 + zd = max([x.low for x in [bi_5, bi_7]]) + zg = min([x.high for x in [bi_5, bi_7]]) + dd = min([x.low for x in [bi_5, bi_7]]) + if zg > zd and bi_8.low < dd: # 567构成中枢,且8的低点小于dd + if bi_9.high < zd: + return ChanSignals.Q3S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三卖', v2='九笔ZD三卖') + + # 类二卖 + if dd < zd <= bi_9.high < zg: + return ChanSignals.Q2S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二卖', v2='九笔') return v -def check_chan_xt_eleven_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_eleven_bi(kline, bi_list: List[ChanObject]): """ - 获取线段得背驰形态(含11个分笔) + 获取线段得的类买卖点(含11个分笔) :param cur_duan: :return: """ @@ -538,95 +677,126 @@ def check_chan_xt_eleven_bi(kline: CtaLineBar, bi_list: List[ChanObject]): max_high = max([x.high for x in bi_list]) min_low = min([x.low for x in bi_list]) + # 11笔向下,寻找买点 if direction == -1: + # 1笔最高,11笔最低 if min_low == bi_11.low and max_high == bi_1.high: - # aAbBc式底背驰,bi_2-bi_6构成A - if min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) > bi_8.high \ - and min(bi_8.high, bi_10.high) > max(bi_8.low, bi_10.low) \ - and min(bi_2.low, bi_4.low, bi_6.low) > max(bi_8.high, bi_10.high) \ - and bi_11.height < bi_7.height and bi_11.atan <= bi_7.atan: - v = ChanSignals.LA0.value - # aAbBc式底背驰,bi_6-bi_10构成B - if 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, bi_10.high) > max(bi_6.low, bi_8.low, bi_10.low) \ - and min(bi_2.low, bi_4.low) > max(bi_6.high, bi_8.high, bi_10.high) \ - and bi_11.height < bi_5.height and bi_11.atan <= bi_5.atan: - v = ChanSignals.LA0.value - - # ABC式底背驰,A5B3C3 + # ABC式类一买,A5B3C3 if bi_5.low == min([x.low for x in [bi_1, bi_3, bi_5]]) \ and bi_9.low > bi_11.low and bi_9.high > bi_11.high \ and bi_8.high > bi_6.low and bi_1.high - bi_5.low > bi_9.high - bi_11.low: - v = ChanSignals.LA0.value - # C内部背驰 - if bi_11.height < bi_9.height: - v = ChanSignals.LB0.value + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="11笔A5B3C3式") - # ABC式底背驰,A3B3C5 + # ABC式类一买,A3B3C5 if bi_1.high > bi_3.high and bi_1.low > bi_3.low \ and bi_7.high == max([x.high for x in [bi_7, bi_9, bi_11]]) \ and bi_6.high > bi_4.low and bi_1.high - bi_3.low > bi_7.high - bi_11.low: - v = ChanSignals.LA0.value - # C内部背驰 - if bi_11.height < max(bi_9.height, bi_7.height) and bi_11.atan <= max(bi_9.atan, bi_7.atan): - v = ChanSignals.LB0.value + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="11笔A3B3C5式") - # ABC式底背驰,A3B5C3 + # ABC式类一买,A3B5C3 if bi_1.low > bi_3.low and min(bi_4.high, bi_6.high, bi_8.high) > max(bi_4.low, bi_6.low, bi_8.low) \ and bi_9.high > bi_11.high and bi_1.high - bi_3.low > bi_9.high - bi_11.low: - v = ChanSignals.LA0.value - # C内部背驰 - if bi_11.height < max(bi_9.height, bi_7.height) and bi_11.atan <= max(bi_9.atan, bi_7.atan): - v = ChanSignals.LB0.value + v = ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="11笔A3B5C3式") + # a1Ab式类一买,a1(1~7构成的类趋势) + if bi_2.low > bi_4.high > bi_4.low > bi_6.high > bi_5.low > bi_7.low and bi_10.high > bi_8.low: + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="11笔a1Ab式") + + # 类二买:1~9构成类趋势,11不创新低,斜率低于9 + if min_low == bi_9.low < bi_8.high < bi_6.low < bi_6.high < bi_4.low < bi_4.high < bi_2.low < bi_1.high == max_high \ + and bi_11.low > bi_9.low and bi_9.atan > bi_11.atan: + return ChanSignals.Q2L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二买', v2="11笔") + + # 类二买(1~7构成盘整背驰,246构成下跌中枢,9/11构成上涨中枢,且上涨中枢ZG大于下跌中枢ZG) + if bi_7.atan < bi_1.atan and min_low == bi_7.low < max([x.low for x in [bi_2, bi_4, bi_6]]) \ + < min([x.high for x in [bi_2, bi_4, bi_6]]) < max( + [x.high for x in [bi_9, bi_11]]) < bi_1.high == max_high \ + and bi_11.low > min([x.low for x in [bi_2, bi_4, bi_6]]) \ + and min([x.high for x in [bi_9, bi_11]]) > max([x.low for x in [bi_9, bi_11]]): + return ChanSignals.Q2L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二买', v2="11笔") + + # 类二买(1~7为区间极值,9~11构成上涨中枢,上涨中枢GG大于4~6的最大值,上涨中枢DD大于4~6的最小值) + if max_high == bi_1.high and min_low == bi_7.low \ + and min(bi_9.high, bi_11.high) > max(bi_9.low, bi_11.low) \ + and max(bi_11.high, bi_9.high) > max(bi_4.high, bi_6.high) \ + and min(bi_9.low, bi_11.low) > min(bi_4.low, bi_6.low): + return ChanSignals.Q2L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二买', v2="11笔") + + # 类三买(1~9构成大级别中枢,10离开,11回调不跌破GG) + gg = max([x.high for x in [bi_1, bi_2, bi_3]]) + zg = min([x.high for x in [bi_1, bi_2, bi_3]]) + zd = max([x.low for x in [bi_1, bi_2, bi_3]]) + dd = min([x.low for x in [bi_1, bi_2, bi_3]]) + if max_high == bi_11.high and bi_11.low > zg > zd \ + and gg > bi_5.low and gg > bi_7.low and gg > bi_9.low \ + and dd < bi_5.high and dd < bi_7.high and dd < bi_9.high: + return ChanSignals.Q3L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三买', v2="11笔GG三买") + + # 11笔向上,寻找卖点 elif direction == 1: + # 1笔最低,11笔最高 if max_high == bi_11.high and min_low == bi_1.low: - # aAbBC式顶背驰,bi_2-bi_6构成A - if bi_8.low > min(bi_2.high, bi_4.high, bi_6.high) >= max(bi_2.low, bi_4.low, bi_6.low) \ - and min(bi_8.high, bi_10.high) >= max(bi_8.low, bi_10.low) \ - and max(bi_2.high, bi_4.high, bi_6.high) < min(bi_8.low, bi_10.low) \ - and bi_11.height < bi_7.height and bi_11.atan <= bi_7.atan: - v = ChanSignals.SA0.value - - # aAbBC式顶背驰,bi_6-bi_10构成B - if 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, bi_10.high) >= max(bi_6.low, bi_8.low, bi_10.low) \ - and max(bi_2.high, bi_4.high) < min(bi_6.low, bi_8.low, bi_10.low) \ - and bi_11.height < bi_7.height and bi_11.atan <= bi_7.atan: - v = ChanSignals.SA0.value # ABC式顶背驰,A5B3C3 if bi_5.high == max([bi_1.high, bi_3.high, bi_5.high]) and bi_9.low < bi_11.low and bi_9.high < bi_11.high \ and bi_8.low < bi_6.high and bi_11.high - bi_9.low < bi_5.high - bi_1.low: - v = ChanSignals.SA0.value - # C内部背驰 - if bi_11.height < bi_9.height and bi_11.atan <= bi_9.atan: - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="11笔A5B3C3式") # ABC式顶背驰,A3B3C5 if bi_7.low == min([bi_11.low, bi_9.low, bi_7.low]) and bi_1.high < bi_3.high and bi_1.low < bi_3.low \ and bi_6.low < bi_4.high and bi_11.high - bi_7.low < bi_3.high - bi_1.low: - v = ChanSignals.SA0.value - # C内部背驰 - if bi_11.height < max(bi_9.height, bi_7.height) and bi_11.atan <= max(bi_9.atan, bi_7.atan): - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="11笔A3B3C5式") # ABC式顶背驰,A3B5C3 if bi_1.high < bi_3.high and min(bi_4.high, bi_6.high, bi_8.high) > max(bi_4.low, bi_6.low, bi_8.low) \ and bi_9.low < bi_11.low and bi_3.high - bi_1.low > bi_11.high - bi_9.low: - v = ChanSignals.SA0.value - # C内部背驰 - if bi_11.height < max(bi_9.height, bi_7.height) and bi_11.atan <= max(bi_9.atan, bi_7.atan): - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value + + # a1Ab式类一卖,a1(1~7构成的类趋势) + if bi_2.high < bi_4.low < bi_4.high < bi_6.low < bi_5.high < bi_7.high and bi_10.low < bi_8.high: + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="11笔a1Ab式") + + # 类二卖:1~9构成类趋势,11不创新高 + if max_high == bi_9.high > bi_8.low > bi_6.high > bi_6.low > bi_4.high > bi_4.low > bi_2.high > bi_1.low == min_low \ + and bi_11.high < bi_9.high and bi_9.atan > bi_11.atan: + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二卖', v2="11笔") + + # 类二卖(1~7构成盘整背驰,246构成上涨中枢,9/11构成下跌中枢,且下跌中枢DD小于上涨中枢ZD) + if bi_7.atan < bi_1.atan and max_high == bi_7.high \ + > min([x.high for x in [bi_2, bi_4, bi_6]]) \ + > max([x.low for x in [bi_2, bi_4, bi_6]]) \ + > min([x.low for x in [bi_9, bi_11]]) \ + > bi_1.low == min_low \ + and bi_11.high < max([x.high for x in [bi_2, bi_4, bi_6]]) \ + and max([x.low for x in [bi_9, bi_11]]) < min([x.high for x in [bi_9, bi_11]]): + return ChanSignals.Q2S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二卖', v2="11笔") + + # 类二卖(1~7为区间极值,9~11构成下跌中枢,下跌中枢DD小于4~6的最小值,下跌中枢GG小于4~6的最大值) + if min_low == bi_1.low and max_high == bi_7.high \ + and max(bi_9.low, bi_11.low) < min(bi_9.high, bi_11.high) \ + and min(bi_11.low, bi_9.low) < min(bi_4.low, bi_6.low) \ + and max(bi_9.high, bi_11.high) < max(bi_4.high, bi_6.high): + return ChanSignals.Q2S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类二卖', v2="11笔") + + # 类三卖(1~9构成大级别中枢,10离开,11回调不涨破DD) + gg = min([x.low for x in [bi_1, bi_2, bi_3]]) + zg = max([x.low for x in [bi_1, bi_2, bi_3]]) + zd = min([x.high for x in [bi_1, bi_2, bi_3]]) + dd = max([x.high for x in [bi_1, bi_2, bi_3]]) + if min_low == bi_11.low and bi_11.high < zd < zg \ + and dd < bi_5.high and dd < bi_7.high and dd < bi_9.high \ + and gg > bi_5.low and gg > bi_7.low and gg > bi_9.low: + return ChanSignals.Q3S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类三卖', v2="11笔GG三买") return v -def check_chan_xt_thirteen_bi(kline: CtaLineBar, bi_list: List[ChanObject]): +def check_chan_xt_thirteen_bi(kline, bi_list: List[ChanObject]): """ - 获取线段得背驰形态(含13个分笔) - :param cur_duan: + 获取线段得形态(含13个分笔) + :param kline: + :param bi_list: 由远及近的十三笔 :return: """ v = ChanSignals.Other.value @@ -641,78 +811,48 @@ def check_chan_xt_thirteen_bi(kline: CtaLineBar, bi_list: List[ChanObject]): # 下跌线段时,判断背驰类型 if direction == -1: if min_low == bi_13.low and max_high == bi_1.high: - # aAbBc式底背驰,bi_2-bi_6构成A,bi_8-bi_12构成B - if min(bi_2.high, bi_4.high, bi_6.high) > max(bi_2.low, bi_4.low, bi_6.low) > bi_8.high \ - and min(bi_8.high, bi_10.high, bi_12.high) > max(bi_8.low, bi_10.low, bi_12.low) \ - and min(bi_2.low, bi_4.low, bi_6.low) > max(bi_8.high, bi_10.high, bi_12.high) \ - and bi_13.height < bi_7.height and bi_13.atan <= bi_7.atan: - v = ChanSignals.LA0.value - - # ABC式底背驰,A5B3C5 + # ABC式类一买,A5B3C5 if bi_5.low < min(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: - v = ChanSignals.LA0.value + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="13笔A5B3C5式") - if bi_13.height < max(bi_11.height, bi_9.height) and bi_13.atan <= max(bi_11.atan, bi_9.atan): - v = ChanSignals.LB0.value - - # ABC式底背驰,A3B5C5 + # ABC式类一买,A3B5C5 if bi_3.low < min(bi_1.low, bi_5.low) and bi_9.high > max(bi_11.high, bi_13.high) \ and min(bi_4.high, bi_6.high, bi_8.high) > max(bi_4.low, bi_6.low, bi_8.low) \ and bi_1.high - bi_3.low > bi_9.high - bi_13.low: - v = ChanSignals.LA0.value - - if bi_13.height < max(bi_11.height, bi_9.height) and bi_13.atan <= max(bi_11.atan, bi_9.atan): - v = ChanSignals.LB0.value + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="13笔A3B5C5式") # ABC式底背驰,A5B5C3 if bi_5.low < min(bi_1.low, bi_3.low) and bi_11.high > max(bi_9.high, bi_13.high) \ 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: - v = ChanSignals.LA0.value - - if bi_13.height < bi_11.height and bi_13.atan <= bi_11.atan: - v = ChanSignals.LB0.value + return ChanSignals.Q1L0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一买', v2="13笔A5B5C3式") # 上涨线段时,判断背驰类型 elif direction == 1: if max_high == bi_13.high and min_low == bi_1.low: - # aAbBC式顶背驰,bi_2-bi_6构成A,bi_8-bi_12构成B - if bi_8.low > min(bi_2.high, bi_4.high, bi_6.high) >= max(bi_2.low, bi_4.low, bi_6.low) \ - and min(bi_8.high, bi_10.high, bi_12.high) >= max(bi_8.low, bi_10.low, bi_12.low) \ - and max(bi_2.high, bi_4.high, bi_6.high) < min(bi_8.low, bi_10.low, bi_12.low) \ - and bi_13.height < bi_7.height and bi_13.atan <= bi_7.atan: - v = ChanSignals.SA0.value - # ABC式顶背驰,A5B3C5 if bi_5.high > max(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: - v = ChanSignals.SA0.value - # C内部顶背驰,形成双重顶背驰 - if bi_13.height < max(bi_11.height, bi_9.height) and bi_13.atan <= max(bi_11.atan, bi_9.atan): - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="13笔A5B3C5式") # ABC式顶背驰,A3B5C5 if bi_3.high > max(bi_5.high, bi_1.high) and bi_9.low < min(bi_11.low, bi_13.low) \ and min(bi_4.high, bi_6.high, bi_8.high) > max(bi_4.low, bi_6.low, bi_8.low) \ and bi_3.high - bi_1.low > bi_13.high - bi_9.low: - v = ChanSignals.SA0.value - # C内部顶背驰,形成双重顶背驰 - if bi_13.height < max(bi_11.height, bi_9.height) and bi_13.atan <= max(bi_11.atan, bi_9.atan): - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="13笔A3B5C5式") # ABC式顶背驰,A5B5C3 if bi_5.high > max(bi_3.high, bi_1.high) and bi_11.low < min(bi_9.low, bi_13.low) \ and min(bi_6.high, bi_8.high, bi_10.high) > max(bi_6.low, bi_8.low, bi_10.low) \ and bi_5.high - bi_1.low > bi_13.high - bi_11.low: - v = ChanSignals.SA0.value - # C内部顶背驰,形成双重顶背驰 - if bi_13.height < bi_11.height and bi_13.atan <= bi_11.atan: - v = ChanSignals.SB0.value + return ChanSignals.Q1S0.value # Signal(k1=freq.value, k2=di_name, k3='类买卖点', v1='类一卖', v2="13笔A5B5C3式") + return v -def check_pzbc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +# def check_pzbc_1st(big_kline, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +def check_pzbc_1st(big_kline, small_kline, signal_direction: Direction): """ 判断中枢盘整背驰1买/1卖信号 big_kline当前线段为调整段,与信号方向相反,线段具有盘整一个中枢, @@ -806,7 +946,8 @@ def check_pzbc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], return False -def check_qsbc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +# def check_qsbc_1st(big_kline, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +def check_qsbc_1st(big_kline, small_kline, signal_direction: Direction): """ 判断趋势背驰1买/1卖信号 big_kline当前线段为趋势,与信号方向相反,线段具有2个中枢, @@ -890,7 +1031,8 @@ def check_qsbc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], return False -def check_pz3bc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +# def check_pz3bc_1st(big_kline, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +def check_pz3bc_1st(big_kline, small_kline, signal_direction: Direction): """ 判断三卖后盘整背驰一买/三买后盘整背驰1卖信号 big_kline当前线段与信号方向相反,线段具有盘整一个中枢,离开中枢的一笔力度与三买/卖信号后的一笔对比(高度、斜率) @@ -966,7 +1108,8 @@ def check_pz3bc_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], return False -def check_qjt_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +# def check_qjt_1st(big_kline, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +def check_qjt_1st(big_kline, small_kline, signal_direction: Direction): """ 判断区间套一买/区间套1卖信号 big_kline当前线段与信号方向相反,线段具有盘整一个中枢, @@ -1038,7 +1181,8 @@ def check_qjt_1st(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], s return False -def check_qsbc_2nd(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +# def check_qsbc_2nd(big_kline, small_kline: Union[CtaLineBar, None], signal_direction: Direction): +def check_qsbc_2nd(big_kline, small_kline, signal_direction: Direction): """ 判断趋势背驰1买/1卖后的二买、二卖信号 big_kline当前线段为趋势,与信号方向相反,线段具有2个中枢, @@ -1136,8 +1280,9 @@ def check_qsbc_2nd(big_kline: CtaLineBar, small_kline: Union[CtaLineBar, None], return True -def check_zs_3rd(big_kline: CtaLineBar, - small_kline: Union[CtaLineBar, None], +# : Union[CtaLineBar, None] +def check_zs_3rd(big_kline, + small_kline, signal_direction: Direction, first_zs: bool = True, all_zs: bool = True): @@ -1181,7 +1326,7 @@ def check_zs_3rd(big_kline: CtaLineBar, zs_list = [zs for zs in big_kline.bi_zs_list[-3:] if zs.start >= big_kline.cur_duan.start] zs_num = len(zs_list) # 是否现在线段得首个中枢后的三买三卖 - if first_zs and zs_num> 1: + if first_zs and zs_num > 1: return False else: # 中枢需要与当前线段有交集[部分交集、或中枢完全在当前段内形成] diff --git a/vnpy/data/stock/adjust_factor.py b/vnpy/data/stock/adjust_factor.py index a0818f16..64c0e1a7 100644 --- a/vnpy/data/stock/adjust_factor.py +++ b/vnpy/data/stock/adjust_factor.py @@ -112,7 +112,7 @@ def download_adjust_factor(): login_msg = bs.login() if login_msg.error_code != '0': print(f'证券宝登录错误代码:{login_msg.error_code}, 错误信息:{login_msg.error_msg}') - return + return None for k, v in base_dict.items(): if v.get('类型') != '股票': @@ -127,6 +127,7 @@ def download_adjust_factor(): save_data_to_pkb2(factor_dict, cache_file_name) print(f'保存除权除息至文件:{cache_file_name}') + return factor_dict if __name__ == '__main__': download_adjust_factor() diff --git a/vnpy/gateway/pb/pb_gateway.py b/vnpy/gateway/pb/pb_gateway.py index 19be4491..4cf977cf 100644 --- a/vnpy/gateway/pb/pb_gateway.py +++ b/vnpy/gateway/pb/pb_gateway.py @@ -415,7 +415,7 @@ class PbGateway(BaseGateway): def process_timer_event(self, event) -> None: """""" self.count += 1 - if self.count < 2: + if self.count < 5: return self.count = 0 @@ -1116,10 +1116,26 @@ class PbTdApi(object): '{}{}.dbf'.format( PB_FILE_NAMES.get('accounts'), self.trading_date))) + + copy_dbf = os.path.abspath(os.path.join(self.account_folder, + '{}{}_{}.dbf'.format( + PB_FILE_NAMES.get('accounts'), + self.trading_date, + datetime.now().strftime('%H%M')))) try: + if not os.path.exists(account_dbf): + return + if os.path.exists(copy_dbf): + os.remove(copy_dbf) + + # =》转移至于新文件 + os.rename(account_dbf, copy_dbf) + if not os.path.exists(copy_dbf): + return + # dbf => 资金帐号信息 - self.gateway.write_log(f'扫描资金帐号信息:{account_dbf}') - table = dbf.Table(account_dbf, codepage='cp936') + self.gateway.write_log(f'扫描资金帐号信息:{copy_dbf}') + table = dbf.Table(copy_dbf, codepage='cp936') table.open(dbf.READ_ONLY) for data in table: # ["资金账户"] @@ -1137,6 +1153,8 @@ class PbTdApi(object): table.close() self.warning_dict.pop('query_account', None) + if os.path.exists(copy_dbf): + os.remove(copy_dbf) except Exception as ex: err_msg = f'dbf扫描资金帐号异常:{str(ex)}' @@ -1200,10 +1218,24 @@ class PbTdApi(object): '{}{}.dbf'.format( PB_FILE_NAMES.get('positions'), self.trading_date))) + + copy_dbf = os.path.abspath(os.path.join(self.account_folder, + '{}{}_{}.dbf'.format( + PB_FILE_NAMES.get('positions'), + self.trading_date, + datetime.now().strftime('%H%M')))) try: + if not os.path.exists(position_dbf): + return + if os.path.exists(copy_dbf): + os.remove(copy_dbf) + os.rename(position_dbf, copy_dbf) + if not os.path.exists(copy_dbf): + return + # dbf => 股票持仓信息 - self.gateway.write_log(f'扫描股票持仓信息:{position_dbf}') - table = dbf.Table(position_dbf, codepage='cp936') + self.gateway.write_log(f'扫描股票持仓信息:{copy_dbf}') + table = dbf.Table(copy_dbf, codepage='cp936') table.open(dbf.READ_ONLY) for data in table: if str(data.zjzh).strip() != self.userid: @@ -1242,6 +1274,9 @@ class PbTdApi(object): table.close() self.warning_dict.pop('query_position', None) + if os.path.exists(copy_dbf): + os.remove(copy_dbf) + except Exception as ex: err_msg = f'dbf扫描股票持仓异常:{str(ex)}' @@ -1325,6 +1360,8 @@ class PbTdApi(object): PB_FILE_NAMES.get('orders'), self.trading_date))) try: + if not os.path.exists(orders_dbf): + return # dbf => 股票委托信息 self.gateway.write_log(f'扫描股票委托信息:{orders_dbf}') table = dbf.Table(orders_dbf, codepage='cp936') @@ -1506,8 +1543,9 @@ class PbTdApi(object): '{}{}.dbf'.format( PB_FILE_NAMES.get('update_orders'), self.trading_date))) - # dbf => 所有委托记录 try: + if not os.path.exists(orders_dbf): + return # dbf => 所有成交记录 self.gateway.write_log(f'扫描所有委托查询:{orders_dbf}') table = dbf.Table(orders_dbf, codepage='cp936') @@ -1686,6 +1724,8 @@ class PbTdApi(object): PB_FILE_NAMES.get('trades'), self.trading_date))) try: + if not os.path.exists(trades_dbf): + return # dbf => 股票成交信息 self.gateway.write_log(f'扫描股票成交信息:{trades_dbf}') table = dbf.Table(trades_dbf, codepage='cp936') @@ -1824,6 +1864,8 @@ class PbTdApi(object): self.trading_date))) try: + if not os.path.exists(trades_dbf): + return # dbf => 所有成交记录 self.gateway.write_log(f'扫描所有成交记录:{trades_dbf}') table = dbf.Table(trades_dbf, codepage='cp936') @@ -1981,6 +2023,9 @@ class PbTdApi(object): dbf_file = os.path.abspath(os.path.join(self.order_folder, '{}{}.dbf'.format(PB_FILE_NAMES.get('send_order'), self.trading_date))) try: + if not os.path.exists(dbf_file): + return + table = dbf.Table(dbf_file, codepage='cp936') table.open(dbf.READ_ONLY) for record in table: diff --git a/vnpy/trader/constant.py b/vnpy/trader/constant.py index cd6de929..d897940b 100644 --- a/vnpy/trader/constant.py +++ b/vnpy/trader/constant.py @@ -214,85 +214,20 @@ class ChanSignals(Enum): LB0 = "LB0~双重底背驰" LG0 = "LG0~上颈线突破" LH0 = "LH0~向上中枢完成" - LI0 = "LI0~三买" + LI0 = "LI0~类三买" LJ0 = "LJ0~向上三角扩张中枢" LK0 = "LK0~向上三角收敛中枢" LL0 = "LL0~向上平台型中枢" - # ------------------------------------------------------------------------------------------------------------------ - - LA1 = "LA1~底背驰特例一" - LA2 = "LA2~底背驰特例二" - LA3 = "LA3~底背驰特例三" - - LB1 = "LB1~双重底背驰特例一" - LB2 = "LB2~双重底背驰特例二" - LB3 = "LB3~双重底背驰特例三" - - LG1 = "LG1~上颈线突破特例一" - LG2 = "LG2~上颈线突破特例二" - LG3 = "LG3~上颈线突破特例三" - - LH1 = "LH1~向上中枢完成特例一" - LH2 = "LH2~向上中枢完成特例二" - LH3 = "LH3~向上中枢完成特例三" - - LI1 = "LI1~三买特例一" - LI2 = "LI2~三买特例二" - LI3 = "LI3~三买特例三" - - LJ1 = "LJ1~向上三角扩张中枢特例一" - LJ2 = "LJ2~向上三角扩张中枢特例二" - LJ3 = "LJ3~向上三角扩张中枢特例三" - - LK1 = "LK1~向上三角收敛中枢特例一" - LK2 = "LK2~向上三角收敛中枢特例二" - LK3 = "LK3~向上三角收敛中枢特例三" - - LL1 = "LL1~向上平台型中枢特例一" - LL2 = "LL2~向上平台型中枢特例二" - LL3 = "LL3~向上平台型中枢特例三" # ------------------------------------------------------------------------------------------------------------------ SA0 = "SA0~顶背驰" SB0 = "SB0~双重顶背驰" SG0 = "SG0~下颈线突破" SH0 = "SH0~向下中枢完成" - SI0 = "SI0~三卖" + SI0 = "SI0~类三卖" SJ0 = "SJ0~向下三角扩张中枢" SK0 = "SK0~向下三角收敛中枢" SL0 = "SL0~向下平台型中枢" - # ------------------------------------------------------------------------------------------------------------------ - SA1 = "SA1~顶背驰特例一" - SA2 = "SA2~顶背驰特例二" - SA3 = "SA3~顶背驰特例三" - - SB1 = "SB1~双重顶背驰特例一" - SB2 = "SB2~双重顶背驰特例二" - SB3 = "SB3~双重顶背驰特例三" - - SG1 = "SG1~下颈线突破特例一" - SG2 = "SG2~下颈线突破特例二" - SG3 = "SG3~下颈线突破特例三" - - SH1 = "SH1~向下中枢完成特例一" - SH2 = "SH2~向下中枢完成特例二" - SH3 = "SH3~向下中枢完成特例三" - - SI1 = "SI1~三卖特例一" - SI2 = "SI2~三卖特例二" - SI3 = "SI3~三卖特例三" - - SJ1 = "SJ1~向下三角扩张中枢特例一" - SJ2 = "SJ2~向下三角扩张中枢特例二" - SJ3 = "SJ3~向下三角扩张中枢特例三" - - SK1 = "SK1~向下三角收敛中枢特例一" - SK2 = "SK2~向下三角收敛中枢特例二" - SK3 = "SK3~向下三角收敛中枢特例三" - - SL1 = "SL1~向下平台型中枢特例一" - SL2 = "SL2~向下平台型中枢特例二" - SL3 = "SL3~向下平台型中枢特例三" # -------------------------------------------------------------------------------------------- # 信号值编码规则: @@ -304,16 +239,24 @@ class ChanSignals(Enum): # 三笔形态信号 # -------------------------------------------------------------------------------------------- X3LA0 = "X3LA0~向下不重合" - X3LB0 = "X3LB0~向下奔走型中枢" - X3LC0 = "X3LC0~向下三角收敛中枢" - X3LD0 = "X3LD0~向下三角扩张中枢" - X3LE0 = "X3LE0~向下盘背中枢" - X3LF0 = "X3LF0~向下无背中枢" + X3LB0 = "X3LB0~向下奔走型" + X3LC0 = "X3LC0~向下收敛" + X3LD0 = "X3LD0~向下扩张" + X3LE0 = "X3LE0~向下盘背" + X3LF0 = "X3LF0~向下无背" X3SA0 = "X3SA0~向上不重合" - X3SB0 = "X3SB0~向上奔走型中枢" - X3SC0 = "X3SC0~向上三角收敛中枢" - X3SD0 = "X3SD0~向上三角扩张中枢" - X3SE0 = "X3SE0~向上盘背中枢" - X3SF0 = "X3SF0~向上无背中枢" + X3SB0 = "X3SB0~向上奔走型" + X3SC0 = "X3SC0~向上收敛" + X3SD0 = "X3SD0~向上扩张" + X3SE0 = "X3SE0~向上盘背" + X3SF0 = "X3SF0~向上无背" + # 趋势类买卖点 + Q1L0 = "Q1L0~趋势类一买" + Q2L0 = "Q2L0~趋势类二买" + Q3L0 = "Q2L0~趋势类三买" + + Q1S0 = "Q1S0~趋势类一卖" + Q2S0 = "Q2S0~趋势类二卖" + Q3S0 = "Q2S0~趋势类三卖" diff --git a/vnpy/trader/engine.py b/vnpy/trader/engine.py index 59f39388..773394ef 100644 --- a/vnpy/trader/engine.py +++ b/vnpy/trader/engine.py @@ -171,6 +171,8 @@ class MainEngine: gateway = self.get_gateway(gateway_name) if gateway: return gateway.get_default_setting() + else: + self.write_error(f'获取网格设置时,找不到{gateway_name}') return None def get_all_gateway_names(self) -> List[str]: @@ -209,6 +211,8 @@ class MainEngine: except Exception as ex: msg = f'gateway:{gateway_name}启动连接失败:{str(ex)}' self.write_log(msg=msg) + else: + self.write_error(f'连接{gateway_name}时,系统找不到{gateway_name}') def subscribe(self, req: SubscribeRequest, gateway_name: str) -> None: """ @@ -219,6 +223,8 @@ class MainEngine: gateway = self.get_gateway(gateway_name) if gateway: gateway.subscribe(req) + else: + self.write_error(f'订阅合约时,找不到{gateway_name}') else: for gateway in self.gateways.values(): if gateway: @@ -239,6 +245,7 @@ class MainEngine: if gateway: return gateway.send_order(req) else: + self.write_error(f'发送委托时,找不到{gateway_name}') return "" def cancel_order(self, req: CancelRequest, gateway_name: str) -> bool: @@ -253,6 +260,8 @@ class MainEngine: gateway = self.get_gateway(gateway_name) if gateway: return gateway.cancel_order(req) + else: + self.write_error(f'撤单时,找不到{gateway_name}') return False def send_orders(self, reqs: Sequence[OrderRequest], gateway_name: str) -> List[str]: @@ -263,6 +272,7 @@ class MainEngine: if gateway: return gateway.send_orders(reqs) else: + self.write_error(f'批量发单时,找不到{gateway_name}') return ["" for req in reqs] def cancel_orders(self, reqs: Sequence[CancelRequest], gateway_name: str) -> None: @@ -271,6 +281,8 @@ class MainEngine: gateway = self.get_gateway(gateway_name) if gateway: gateway.cancel_orders(reqs) + else: + self.write_error(f'批量撤单时,找不到{gateway_name}') def query_history(self, req: HistoryRequest, gateway_name: str) -> Optional[List[BarData]]: """ @@ -280,7 +292,7 @@ class MainEngine: if gateway: return gateway.query_history(req) else: - self.write_error(f'网关为空,请检查合约得网关是否与连接得网关一致') + self.write_error(f'找不到网关{gateway_name},请检查合约得网关是否与连接得网关一致') return None def close(self) -> None: diff --git a/vnpy/trader/object.py b/vnpy/trader/object.py index fc971295..aa6c814e 100644 --- a/vnpy/trader/object.py +++ b/vnpy/trader/object.py @@ -135,6 +135,7 @@ class OrderData(BaseData): symbol: str exchange: Exchange orderid: str + name: str = "" sys_orderid: str = "" accountid: str = "" type: OrderType = OrderType.LIMIT @@ -153,6 +154,8 @@ class OrderData(BaseData): self.vt_symbol = f"{self.symbol}.{self.exchange.value}" self.vt_orderid = f"{self.gateway_name}.{self.orderid}" self.vt_accountid = f"{self.gateway_name}.{self.accountid}" + if len(self.name) == 0: + self.name = self.vt_symbol def is_active(self) -> bool: """ @@ -184,6 +187,7 @@ class TradeData(BaseData): exchange: Exchange orderid: str tradeid: str + name: str = "" sys_orderid: str = "" accountid: str = "" @@ -208,7 +212,8 @@ class TradeData(BaseData): self.vt_orderid = f"{self.gateway_name}.{self.orderid}" self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}" self.vt_accountid = f"{self.gateway_name}.{self.accountid}" - + if len(self.name) == 0: + self.name = self.vt_symbol @dataclass class PositionData(BaseData): diff --git a/vnpy/trader/utility.py b/vnpy/trader/utility.py index c371d1e5..c5c5389f 100644 --- a/vnpy/trader/utility.py +++ b/vnpy/trader/utility.py @@ -347,6 +347,9 @@ def get_csv_last_dt(file_name, dt_index=0, dt_format='%Y-%m-%d %H:%M:%S', line_l :param line_length: 行数据的长度 :return: None,文件不存在,或者时间格式不正确 """ + if not os.path.exists(file_name): + return False + with open(file_name, 'r') as f: f_size = os.path.getsize(file_name) if f_size < line_length: @@ -1442,3 +1445,4 @@ def get_remote_file(remote_ip, remote_file_path, mode='rb'): except Exception as ex: print(u'打开远程目录异常:{}'.format(str(ex))) return None +