def run(self, start=None, end=None): if self.data is None or self.data.empty: util_log_info('{} {} quote data is empty'.format( self.code, self.freq)) return self.data.apply(self.on_bar, axis=1)
def on_bar(self, bar): """ bar 格式 date 默认为 Timestamp,主要时画图函数使用 """ bar = bar.to_dict() # if 'trade' in bar: # bar['vol'] = bar.pop('trade') # bar['date'] = pd.to_datetime(bar['date']) self.bars.append(bar) try: self.update() except Exception as error: util_log_info(error)
def to_df(self): xd = self.xd_list index = 0 sig = [] while xd: df = pd.DataFrame(xd.sig_list) df['xd'] = index df['code'] = self.code df['exchange'] = self.exchange sig.append(df) xd = xd.next index = index + 1 try: sig_df = pd.concat(sig).set_index(['date', 'xd']).sort_index() return sig_df except: util_log_info("{} signal is empty!".format(self.code)) return pd.DataFrame()
def update_fx(bars, new_bars: list, fx_list: list, trade_date: list): """更新分型序列 k线中有direction,fx中没有direction字段 分型记对象样例: { 'date': Timestamp('2020-11-26 00:00:00'), 'fx_mark': -1, 低点用—1表示 'value': 138.0, 'fx_start': Timestamp('2020-11-25 00:00:00'), 'fx_end': Timestamp('2020-11-27 00:00:00'), } { 'date': Timestamp('2020-11-26 00:00:00'), 'fx_mark': +1, 高点用+1表示 'value': 150.67, 'fx_start': Timestamp('2020-11-25 00:00:00'), 'fx_end': Timestamp('2020-11-27 00:00:00'), } """ assert len(bars) > 0 bar = bars[-1].copy() if len(trade_date) > 1: if TradeDate(bar['date']) < TradeDate(trade_date[-1]): util_log_info('{} data is older than {} !'.format( bar['date'], trade_date[-1])) return trade_date.append(bar['date']) # 第1根K线没有方向,不需要任何处理 if len(bars) < 2: new_bars.append(bar) return False last_bar = new_bars[-1] cur_h, cur_l = bar['high'], bar['low'] last_h, last_l, last_dt = last_bar['high'], last_bar['low'], last_bar[ 'date'] # 处理过包含关系,只需要用一个值识别趋势 direction = identify_direction(cur_h, last_h) # 第2根K线只需要更新方向 if len(bars) < 3: bar.update(direction=direction) new_bars.append(bar) return False last_direction = last_bar.get('direction') # 没有包含关系,需要进行分型识别,趋势有可能改变 if (cur_h > last_h and cur_l > last_l) or (cur_h < last_h and cur_l < last_l): new_bars.append(bar) # 分型识别 if last_direction * direction < 0: bar.update(direction=direction) if direction < 0: fx = { "date": last_bar['date'], "fx_mark": 1, "value": last_bar['high'], "fx_start": new_bars[-3]['date'], # 记录分型的开始和结束时间 "fx_end": bar['date'], # "direction": bar['direction'], } else: fx = { "date": last_bar['date'], "fx_mark": -1, "value": last_bar['low'], "fx_start": new_bars[-3]['date'], # 记录分型的开始和结束时间 "fx_end": bar['date'], # "direction": bar['direction'], } fx_list.append(fx) return True bar.update(direction=last_direction + np.sign(last_direction)) return False # 有包含关系,不需要进行分型识别,趋势不改变,direction数值增加 bar.update(direction=last_direction + np.sign(last_direction)) new_bars.pop(-1) # 有包含关系的前一根数据被删除,这里是个技巧 # 有包含关系,按方向分别处理,同时需要更新日期 if last_direction > 0: if cur_h < last_h: bar.update(high=last_h, date=last_dt) if cur_l < last_l: bar.update(low=last_l) elif last_direction < 0: if cur_l > last_l: bar.update(low=last_l, date=last_dt) if cur_h > last_h: bar.update(high=last_h) else: logging.error('{} last_direction: {} is wrong'.format( last_dt, last_direction)) raise ValueError new_bars.append(bar) return False
def update_xd(self): """更新笔分型序列 分型记对象样例: { 'date': Timestamp('2020-11-26 00:00:00'), 'fx_mark': -8, 低点,负数,表示下降趋势持续的K线根数 'value': 138.0, 'fx_start': Timestamp('2020-11-25 00:00:00'), 'fx_end': Timestamp('2020-11-27 00:00:00'), } { 'date': Timestamp('2020-11-26 00:00:00'), 'fx_mark': 7, 高点, 正数,表示上升趋势持续的根数 'value': 150.67, 'fx_start': Timestamp('2020-11-25 00:00:00'), 'fx_end': Timestamp('2020-11-27 00:00:00'), } """ # 至少3根同类型分型才可能出现线段,最后1根bi不确定,因此最后一段也不确定 if self.next is None: self.next = XdList(self.bars, self.indicators, self.trade_date) bi_list = self.xd_list xd_list = self.next if len(bi_list) < 4: return False if len(xd_list) < 1: # 线段不存在,初始化线段,找4个点的最高和最低点组成线段 bi_list = bi_list[:-1].copy() bi_list = sorted(bi_list, key=lambda x: x['value'], reverse=False) if TradeDate(bi_list[0]['date']) < TradeDate(bi_list[-1]['date']): xd_list.append(bi_list[0]) xd_list.append(bi_list[-1]) else: xd_list.append(bi_list[-1]) xd_list.append(bi_list[0]) xd_list.update_xd_eigenvalue() return True bi3 = bi_list[-3] xd = bi_list[-1].copy() last_xd = xd_list[-1] xd2 = xd_list[-2] # if xd['date'] > pd.to_datetime('2016-07-12'): # print('test') # 非分型结尾段,直接替换成分型, 没有新增段,后续不需要处理,同一个端点确认 if 'direction' in last_xd or xd['date'] == last_xd['date']: xd_list[-1] = xd # 日期相等的情况是否已经在内存中修改过了? xd_list.update_xd_eigenvalue() return True # assert xd['date'] > last_xd['date'] if TradeDate(xd['date']) <= TradeDate(last_xd['date']): util_log_info('The {} quotes bar input maybe wrong!'.format( xd['date'])) if bi3['fx_mark'] > 0: # 同向延续 if last_xd['fx_mark'] > 0 and xd['value'] > last_xd['value']: xd_list[-1] = xd xd_list.update_xd_eigenvalue() return True # 反向判断 elif last_xd['fx_mark'] < 0: # 价格判断 if xd['value'] > xd2['value']: xd_list.append(xd) xd_list.update_xd_eigenvalue() return True # 出现三笔破坏线段,连续两笔,一笔比一笔高,寻找段之间的最高点 elif TradeDate(bi3['date']) > TradeDate( last_xd['date']) and xd['value'] > bi3['value']: index = -5 bi = bi_list[index] # 连续两个高点没有碰到段前面一个低点 try: if TradeDate(bi['date']) < TradeDate(last_xd['date']) and \ bi_list[index - 1]['value'] > bi3['value'] and \ bi_list[index]['value'] > xd['value']: return False except Exception as err: pass # util_log_info('Last xd {}:{}'.format(last_xd['date'], err)) while TradeDate(bi['date']) > TradeDate(last_xd['date']): if xd['value'] < bi['value']: xd = bi index = index - 2 bi = bi_list[index] xd_list.append(xd) xd_list.update_xd_eigenvalue() return True elif bi3['fx_mark'] < 0: # 同向延续 if last_xd['fx_mark'] < 0 and xd['value'] < last_xd['value']: xd_list[-1] = xd xd_list.update_xd_eigenvalue() return True # 反向判断 elif last_xd['fx_mark'] > 0: # 价格判断 if xd['value'] < xd2['value']: xd_list.append(xd) xd_list.update_xd_eigenvalue() return True # 出现三笔破坏线段,连续两笔,一笔比一笔低,将最低的一笔作为段的起点,避免出现最低点不是端点的问题 elif TradeDate(bi3['date']) > TradeDate( last_xd['date']) and xd['value'] < bi3['value']: index = -5 bi = bi_list[index] # 连续两个个低点没有碰到段前面一高低点 try: if TradeDate(bi['date']) < TradeDate(last_xd['date']) and \ bi_list[index - 1]['value'] < bi3['value'] and \ bi_list[index]['value'] < xd['value']: return False except Exception as err: pass # util_log_info('Last xd {}:{}'.format(last_xd['date'], err)) while TradeDate(bi['date']) > TradeDate(last_xd['date']): if xd['value'] > bi['value']: xd = bi index = index - 2 bi = bi_list[index] xd_list.append(xd) xd_list.update_xd_eigenvalue() return True return False
def calculate_bs_signals(security_df: pd.DataFrame, last_trade_date=None): sig_list = [] if last_trade_date is None: last_trade_date = util_get_real_date( datetime.today().strftime('%Y-%m-%d')) last_trade_time = pd.to_datetime(util_get_next_day(last_trade_date)) last_trade_date = pd.to_datetime(last_trade_date) index = 0 for code, item in security_df.iterrows(): exchange = item['exchange'] util_log_info("============={} {} Signal==========".format( code, exchange)) try: czsc_day = CzscMongo(code=code, end=last_trade_date, freq='day', exchange=exchange) except Exception as error: util_log_info("{} : {}".format(code, error)) continue if len(czsc_day.data) < 1: util_log_info("==========={} {} 0 Quotes==========".format( code, exchange)) continue if czsc_day.data.iloc[-1]['date'] < last_trade_date: util_log_info("=={} {} last trade date {}==".format( code, exchange, czsc_day.data.iloc[-1]['date'].strftime('%Y-%m-%d'))) continue czsc_day.run() sig_day_list = czsc_day.sig_list if len(sig_day_list) < 1: continue last_day_sig = sig_day_list[-1] if last_day_sig['date'] < last_trade_date: util_log_info("===={} {} last Signal {}====".format( code, exchange, last_day_sig['date'].strftime('%Y-%m-%d'))) continue # 流动性过滤,future为成交量过滤 if item['instrument'] == 'future': amount = czsc_day.bars[-1]['volume'] if amount < 10000: util_log_info("===={} {} volume is few!====".format( code, exchange)) continue elif exchange in ['hkconnect']: amount = czsc_day.bars[-1]['hk_stock_amount'] else: amount = czsc_day.bars[-1]['amount'] if amount < 10000000: util_log_info("===={} {} amount is few!====".format( code, exchange)) continue # 笔中枢走势的起点,如果是上升趋势的买点,从当前中枢的最高点开始计算,如果是卖点,从上升趋势的起点开始 xd_list = czsc_day.xd_list zs_list = xd_list.zs_list if len(zs_list) < 1: continue xd_mark = last_day_sig['xd_mark'] # if xd_mark < 0: # xd = zs_list[-1]['DD'][-1] # else: # xd = zs_list[-1]['GG'][-1] # # start = xd.get('fx_start') start = xd_list.sig_list[-1]['start'] czsc_min = CzscMongo(code=code, start=start, end=last_trade_time, freq='5min', exchange=exchange) try: if len(czsc_min.data) < 1: util_log_info("========={} {} 0 5min Quotes========".format( code, exchange)) continue except: util_log_info( "========={} {} 5min Quotes file is not exists!========". format(code, exchange)) continue if czsc_min.data.iloc[-1]['date'] < last_trade_date: util_log_info("==Please Update {} {} 5min Quotes from {}==".format( code, exchange, czsc_day.data.iloc[-1]['date'].strftime('%Y-%m-%d'))) continue czsc_min.run() sig_min_list = czsc_min.sig_list if len(sig_min_list) < 1: continue last_min_sig = sig_min_list[-1] if last_min_sig['date'] < last_trade_date: continue df = pd.DataFrame(sig_min_list).set_index('date') bar_df = pd.DataFrame(czsc_min.bars).set_index('date') bar_df = bar_df[bar_df.index > last_trade_date] if xd_mark > 0: idx = bar_df['low'].idxmin() else: idx = bar_df['high'].idxmax() if df.empty: util_log_info("===Please Download {} {} 5min Data===".format( code, exchange)) continue try: last_min_sig = df.loc[idx].to_dict() except: util_log_info("{} {} Have a opposite Signal=======".format( code, exchange)) continue if last_min_sig['xd_mark'] * xd_mark < 0: # 日内高低点不一定是高级别买卖点 util_log_info("{} {} Have a opposite Signal=======".format( code, exchange)) continue # 顺趋势买卖点为1,-1,逆趋势级别要大,小于0为逆趋势,或者不为笔分型 # (xd_mark * zs_list[-1]['location'] <= 0 and last_min_sig['xd'] >= 2) # (xd_mark * zs_list[-1]['location'] >= 0 and last_min_sig['xd_mark'] in [1, -1]) # ('fx_start' in xd_list[-1]) if not ((xd_mark * zs_list[-1]['location'] <= 0 and last_min_sig['xd'] >= 2) or (xd_mark * zs_list[-1]['location'] >= 0 and last_min_sig['xd_mark'] in [1, -1]) or ('fx_start' in xd_list[-1])): util_log_info("==={} xd:{}, xd_mark:{}===".format( code, last_min_sig['xd'], last_min_sig['xd_mark'])) continue try: dif = 0 if np.isnan( last_min_sig.get('dif')) else last_min_sig.get('dif') macd = 0 if np.isnan( last_min_sig.get('macd')) else last_min_sig.get('macd') last_min_sig.update(macd=dif + macd) except TypeError: util_log_info("{} {} has no macd value=======".format( code, exchange)) # if code in ['515120', '510310', '512500', '515380', '515390', '515800', '159905']: # print('ok') for idx in range(1, last_min_sig['xd'] + 1): dif = 0 if np.isnan(last_min_sig.get( 'dif{}'.format(idx))) else last_min_sig.get( 'dif{}'.format(idx)) macd = 0 if np.isnan(last_min_sig.get( 'macd{}'.format(idx))) else last_min_sig.get( 'macd{}'.format(idx)) last_min_sig.update(macd=last_min_sig.get('macd') + dif + macd) for key in last_min_sig: last_day_sig[key + '_min'] = last_min_sig[key] # last_day_sig['start'] = start last_day_sig.update(amount=amount, code=code, exchange=exchange) sig_list.append(last_day_sig) index = index + 1 util_log_info("==={:=>4d}. {} {} Have a Signal=======".format( index, code, exchange)) if len(sig_list) < 1: util_log_info("========There are 0 Signal=======") return None df = pd.DataFrame(sig_list) columns = df.columns.to_list() # order = df.columns.sort_values(ascending=False).drop(['date', 'location', 'location_min', 'exchange']) order = [ 'code', 'xd', 'real_loc', 'xd_mark', 'weight', 'boll', 'dif', 'macd', 'start', 'xd_min', 'real_loc_min', 'xd_mark_min', 'weight_min', 'boll_min', 'macd_min', 'amount', ] idx = 1 day_index = order.index('start') while 'dif{}'.format(idx) in columns: order.insert(day_index, 'dif{}'.format(idx)) if 'macd{}'.format(idx) in columns: day_index = day_index + 1 order.insert(day_index, 'macd{}'.format(idx)) day_index = day_index + 1 idx = idx + 1 df = df[order].sort_values(by=['xd', 'xd_min', 'macd_min', 'weight'], ascending=[False, False, False, False]) util_log_info("===There are {} Signal=======".format(index)) return df