diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 342a0b302..65997e752 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -5,7 +5,7 @@ name: Python package on: push: - branches: [ master, V0.9.22 ] + branches: [ master, V0.9.23 ] pull_request: branches: [ master ] diff --git a/.gitignore b/.gitignore index f2b96a623..aa38b0aad 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ __pycache__/ *.py[cod] *$py.class - +*/docs/source/api */gmcache .idea/ manuscrips/ diff --git a/czsc/__init__.py b/czsc/__init__.py index 808a685c0..6fd74fbc4 100644 --- a/czsc/__init__.py +++ b/czsc/__init__.py @@ -25,10 +25,10 @@ from czsc.utils.signal_analyzer import SignalAnalyzer, SignalPerformance -__version__ = "0.9.22" +__version__ = "0.9.23" __author__ = "zengbin93" __email__ = "zeng_bin8888@163.com" -__date__ = "20230616" +__date__ = "20230626" def welcome(): diff --git a/czsc/objects.py b/czsc/objects.py index 3c5cfb5d8..11d3a08a2 100644 --- a/czsc/objects.py +++ b/czsc/objects.py @@ -745,9 +745,10 @@ def __init__( :param T0: 是否允许T0交易,默认为 False 表示不允许T0交易 :param name: 仓位名称,默认值为第一个开仓事件的名称 """ + assert name, "name 是必须的参数" self.symbol = symbol self.opens = opens - self.name = name if name else opens[0].name + self.name = name self.exits = exits if exits else [] self.events = self.opens + self.exits for event in self.events: diff --git a/czsc/signals/__init__.py b/czsc/signals/__init__.py index d80feac1c..74c5b67f4 100644 --- a/czsc/signals/__init__.py +++ b/czsc/signals/__init__.py @@ -48,6 +48,7 @@ byi_bi_end_V230106, byi_bi_end_V230107, byi_second_bs_V230324, + byi_fx_num_V230628, ) from czsc.signals.coo import ( @@ -91,6 +92,10 @@ bar_single_V230506, bar_triple_V230506, bar_zt_count_V230504, + bar_tnr_V230629, + bar_tnr_V230630, + bar_shuang_fei_V230507, + bar_limit_down_V230525, ) from czsc.signals.jcc import ( @@ -168,12 +173,15 @@ tas_kdj_evc_V230401, tas_atr_break_V230424, - tas_sar_base_V230425, - + tas_macd_bs1_V230411, + tas_macd_bs1_V230412, tas_cross_status_V230619, tas_cross_status_V230624, tas_cross_status_V230625, + tas_low_trend_V230627, + tas_atr_V230630, + tas_accelerate_V230531, ) from czsc.signals.pos import ( diff --git a/czsc/signals/bar.py b/czsc/signals/bar.py index 06b96a40b..9ab89f317 100644 --- a/czsc/signals/bar.py +++ b/czsc/signals/bar.py @@ -161,7 +161,7 @@ def bar_end_V221211(c: CZSC, freq1='60分钟', **kwargs) -> OrderedDict: def bar_operate_span_V221111(c: CZSC, **kwargs) -> OrderedDict: - """日内操作时间区间,c 必须是 + """日内操作时间区间,c 必须是基础周期的 CZSC 对象 参数模板:"{freq}_T{t1}#{t2}_时间区间" @@ -1215,3 +1215,188 @@ def bar_channel_V230508(c: CZSC, **kwargs) -> OrderedDict: v1 = "看空" return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + +def bar_tnr_V230630(c: CZSC, **kwargs) -> OrderedDict: + """趋势噪音指标(TNR,Trend to Noise Rate) + + 参数模板:"{freq}_D{di}TNR{timeperiod}K{k}_趋势V230630" + + **信号逻辑:** + + TNR计算公式:取N根K线,首尾两个close的绝对差值 除以 相邻两个close的绝对差值累计。 + + 噪音变化判断,如果 t 时刻的 TNR > 过去k个TNR的均值,则说明噪音在减少,此时趋势较强;反之,噪音在增加,此时趋势较弱。 + + **信号列表:** + + - Signal('15分钟_D1TNR14K3_趋势V230630_噪音减少_任意_任意_0') + - Signal('15分钟_D1TNR14K3_趋势V230630_噪音增加_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: TNR指标的参数 + - k: 过去k个TNR的均值 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + k = int(kwargs.get('k', 3)) + freq = c.freq.value + + # 更新缓存 + cache_key = f"TNR{timeperiod}" + for i, bar in enumerate(c.bars_raw, 0): + if cache_key in bar.cache: + continue + if i < timeperiod: + bar.cache[cache_key] = 0 + else: + _bars = c.bars_raw[max(0, i - timeperiod):i + 1] + sum_abs = sum([abs(_bars[j].close - _bars[j - 1].close) for j in range(1, len(_bars))]) + bar.cache[cache_key] = 0 if sum_abs == 0 else abs(_bars[-1].close - _bars[0].close) / sum_abs + + k1, k2, k3 = f"{freq}_D{di}TNR{timeperiod}K{k}_趋势V230630".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=k) + delta_tnr = bars[-1].cache[cache_key] - np.mean([bar.cache[cache_key] for bar in bars]) + v1 = "噪音减少" if delta_tnr > 0 else "噪音增加" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + +def bar_tnr_V230629(c: CZSC, **kwargs) -> OrderedDict: + """趋势噪音指标(TNR,Trend to Noise Rate)分层 + + 参数模板:"{freq}_D{di}TNR{timeperiod}_趋势V230629" + + **信号逻辑:** + + TNR计算公式:取N根K线,首尾两个close的绝对差值 除以 相邻两个close的绝对差值累计。 + + 取最近100个bar的TNR进行分层。 + + **信号列表:** + + - Signal('15分钟_D1TNR14_趋势V230629_第7层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第6层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第8层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第9层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第10层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第5层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第2层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第1层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第3层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第4层_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: TNR指标的参数 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + freq = c.freq.value + + # 更新缓存 + cache_key = f"TNR{timeperiod}" + for i, bar in enumerate(c.bars_raw, 0): + if cache_key in bar.cache: + continue + if i < timeperiod: + bar.cache[cache_key] = 0 + else: + _bars = c.bars_raw[max(0, i - timeperiod):i + 1] + sum_abs = sum([abs(_bars[j].close - _bars[j - 1].close) for j in range(1, len(_bars))]) + bar.cache[cache_key] = 0 if sum_abs == 0 else abs(_bars[-1].close - _bars[0].close) / sum_abs + + k1, k2, k3 = f"{freq}_D{di}TNR{timeperiod}_趋势V230629".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=100) + tnr = [bar.cache[cache_key] for bar in bars] + lev = pd.qcut(tnr, 10, labels=False, duplicates='drop')[-1] + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=f"第{int(lev+1)}层") + + +def bar_shuang_fei_V230507(c: CZSC, **kwargs) -> OrderedDict: + """双飞涨停,贡献者:琅盎 + + 参数模板:"{freq}_D{di}双飞_短线V230507" + + **信号逻辑:** + + 1. 今天涨停; + 2. 昨天收阴,且跌幅大于5% + 3. 前天涨停 + + **信号列表:** + + - Signal('日线_D1双飞_短线V230507_看多_任意_任意_0') + + :param c: CZSC对象 + :param kwargs: 参数字典 + + - di: 信号计算截止倒数第i根K线 + + :return: 信号识别结果 + """ + di = int(kwargs.get("di", 1)) + freq = c.freq.value + k1, k2, k3 = f"{freq}_D{di}双飞_短线V230507".split('_') + v1 = "其他" + if len(c.bars_raw) < di + 10: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + b4, b3, b2, b1 = get_sub_elements(c.bars_raw, di=di, n=4) + first_zt = b3.close == b3.high and b3.close / b4.close - 1 > 0.07 + last_zt = b1.close / b2.close - 1 > 0.07 and b1.upper < max(b1.lower, b1.solid) / 2 + bar2_down = b2.close < b2.open and b2.close / b3.close - 1 < -0.05 + + if first_zt and last_zt and b1.close > b2.high and bar2_down: + v1 = "看多" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + +def bar_limit_down_V230525(c: CZSC, **kwargs) -> OrderedDict: + """跌停后出现无下影线长实体阳线做多 + + 参数模板:"{freq}_跌停后无下影线长实体阳线_短线V230525" + + **信号逻辑:** + + 1. 跌停后出现无下影线长实体阳线做多 + + **信号列表:** + + - Signal('日线_跌停后无下影线长实体阳线_短线V230525_满足_任意_任意_0') + + :param c: CZSC对象 + :param kwargs: 参数字典 + :return: 返回信号结果 + """ + freq = c.freq.value + assert freq == '日线', "该信号只能用于日线级别,仅适用于A股" + + k1, k2, k3 = f"{freq}_跌停后无下影线长实体阳线_短线V230525".split('_') + v1 = '其他' + if len(c.bars_raw) < 10: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + b1, b2, b3 = c.bars_raw[-3:] + b2_condition = b2.low == b2.close < b1.close and b2.close / b1.close < 0.95 + b3_condition = b3.low == b3.open and b3.close > b3.open and b3.solid > b3.upper * 2 and b3.close / b3.open > 1.07 + if b2_condition and b3_condition and b3.low < b2.low: + v1 = '满足' + + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) diff --git a/czsc/signals/byi.py b/czsc/signals/byi.py index 0bc4cda4f..75c642431 100644 --- a/czsc/signals/byi.py +++ b/czsc/signals/byi.py @@ -223,3 +223,35 @@ def byi_second_bs_V230324(c: CZSC, **kwargs) -> OrderedDict: return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) +def byi_fx_num_V230628(c: CZSC, **kwargs) -> OrderedDict: + """白仪前面下跌或上涨一笔次级别笔结构数量满足条件;贡献者:谌意勇 + + 参数模板:"{freq}_D{di}笔分型数大于{num}_BE辅助V230628" + + **信号逻辑:** + + 对于采用分型停顿或者分型验证开开仓,前一笔内部次级别笔结构尽量带结构, + 此信号函数为当分型笔数量判断大于 num 为满足条件 + + **信号列表:** + + - Signal('15分钟_D1笔分型数大于4_BE辅助V230628_向下_满足_任意_0') + - Signal('15分钟_D1笔分型数大于4_BE辅助V230628_向上_满足_任意_0') + + :param c: CZSC对象 + :param di: 从倒数第几笔开始检查 + :param num: 前笔内部次级别笔数量 + :return: 信号识别结果 + """ + di = int(kwargs.get("di", 1)) + num = int(kwargs.get('num', 4)) + freq = c.freq.value + k1, k2, k3 = f"{freq}_D{di}笔分型数大于{num}_BE辅助V230628".split('_') + v1 = "其他" + if len(c.bi_list) < di + 1 or len(c.bars_ubi) > 7: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bi = c.bi_list[-di] + v1 = bi.direction.value + v2 = "满足" if len(bi.fxs) >= num else "其他" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1, v2=v2) diff --git a/czsc/signals/tas.py b/czsc/signals/tas.py index 3dab76730..692c68fe8 100644 --- a/czsc/signals/tas.py +++ b/czsc/signals/tas.py @@ -12,8 +12,9 @@ try: import talib as ta except: - logger.warning(f"ta-lib 没有正确安装,相关信号函数无法正常执行。" - f"请参考安装教程 https://blog.csdn.net/qaz2134560/article/details/98484091") + logger.warning(f"ta-lib 没有正确安装,相关信号函数无法正常执行。" f"请参考安装教程 https://blog.csdn.net/qaz2134560/article/details/98484091") +import math +import pandas as pd import numpy as np from collections import OrderedDict from deprecated import deprecated @@ -33,8 +34,14 @@ def update_ma_cache(c: CZSC, **kwargs): :return: cache_key """ ma_type_map = { - 'SMA': ta.MA_Type.SMA, 'EMA': ta.MA_Type.EMA, 'WMA': ta.MA_Type.WMA, 'KAMA': ta.MA_Type.KAMA, - 'TEMA': ta.MA_Type.TEMA, 'DEMA': ta.MA_Type.DEMA, 'MAMA': ta.MA_Type.MAMA, 'TRIMA': ta.MA_Type.TRIMA, + 'SMA': ta.MA_Type.SMA, + 'EMA': ta.MA_Type.EMA, + 'WMA': ta.MA_Type.WMA, + 'KAMA': ta.MA_Type.KAMA, + 'TEMA': ta.MA_Type.TEMA, + 'DEMA': ta.MA_Type.DEMA, + 'MAMA': ta.MA_Type.MAMA, + 'TRIMA': ta.MA_Type.TRIMA, } timeperiod = int(kwargs["timeperiod"]) ma_type = kwargs.get("ma_type", 'SMA').upper() @@ -58,7 +65,7 @@ def update_ma_cache(c: CZSC, **kwargs): else: # 增量更新最近5个K线缓存 - close = np.array([x.close for x in c.bars_raw[-timeperiod - 10:]]) + close = np.array([x.close for x in c.bars_raw[-timeperiod - 10 :]]) ma = ta.MA(close, timeperiod=timeperiod, matype=ma_type_map[ma_type.upper()]) for i in range(1, 6): _c = dict(c.bars_raw[-i].cache) if c.bars_raw[-i].cache else dict() @@ -98,7 +105,7 @@ def update_macd_cache(c: CZSC, **kwargs): else: # 增量更新最近5个K线缓存 - close = np.array([x.close for x in c.bars_raw[-min_count - 10:]]) + close = np.array([x.close for x in c.bars_raw[-min_count - 10 :]]) dif, dea, macd = ta.MACD(close, fastperiod=fastperiod, slowperiod=slowperiod, signalperiod=signalperiod) for i in range(1, 6): _c = dict(c.bars_raw[-i].cache) if c.bars_raw[-i].cache else dict() @@ -139,7 +146,7 @@ def update_boll_cache_V230228(c: CZSC, **kwargs): else: # 增量更新最近5个K线缓存 - close = np.array([x.close for x in c.bars_raw[-timeperiod - 10:]]) + close = np.array([x.close for x in c.bars_raw[-timeperiod - 10 :]]) u1, m, l1 = ta.BBANDS(close, timeperiod=timeperiod, nbdevup=nbdev, nbdevdn=nbdev, matype=0) for i in range(1, 6): @@ -176,32 +183,41 @@ def update_boll_cache(c: CZSC, **kwargs): _c = dict(c.bars_raw[i].cache) if c.bars_raw[i].cache else dict() if not m[i]: _data = { - "上轨3": close[i], "上轨2": close[i], "上轨1": close[i], "中线": close[i], "下轨1": close[i], - "下轨2": close[i], "下轨3": close[i] + "上轨3": close[i], + "上轨2": close[i], + "上轨1": close[i], + "中线": close[i], + "下轨1": close[i], + "下轨2": close[i], + "下轨3": close[i], } else: - _data = { - "上轨3": u3[i], "上轨2": u2[i], "上轨1": u1[i], "中线": m[i], "下轨1": l1[i], "下轨2": l2[i], - "下轨3": l3[i] - } + _data = {"上轨3": u3[i], "上轨2": u2[i], "上轨1": u1[i], "中线": m[i], "下轨1": l1[i], "下轨2": l2[i], "下轨3": l3[i]} _c.update({cache_key: _data}) c.bars_raw[i].cache = _c else: # 增量更新最近5个K线缓存 - close = np.array([x.close for x in c.bars_raw[-timeperiod - 10:]]) + close = np.array([x.close for x in c.bars_raw[-timeperiod - 10 :]]) u1, m, l1 = ta.BBANDS(close, timeperiod=timeperiod, nbdevup=dev_seq[0], nbdevdn=dev_seq[0], matype=0) u2, m, l2 = ta.BBANDS(close, timeperiod=timeperiod, nbdevup=dev_seq[1], nbdevdn=dev_seq[1], matype=0) u3, m, l3 = ta.BBANDS(close, timeperiod=timeperiod, nbdevup=dev_seq[2], nbdevdn=dev_seq[2], matype=0) for i in range(1, 6): _c = dict(c.bars_raw[-i].cache) if c.bars_raw[-i].cache else dict() - _c.update({ - cache_key: { - "上轨3": u3[-i], "上轨2": u2[-i], "上轨1": u1[-i], "中线": m[-i], "下轨1": l1[-i], - "下轨2": l2[-i], "下轨3": l3[-i] + _c.update( + { + cache_key: { + "上轨3": u3[-i], + "上轨2": u2[-i], + "上轨1": u1[-i], + "中线": m[-i], + "下轨1": l1[-i], + "下轨2": l2[-i], + "下轨3": l3[-i], + } } - }) + ) c.bars_raw[-i].cache = _c return cache_key @@ -283,7 +299,7 @@ def tas_macd_base_V221028(c: CZSC, **kwargs) -> OrderedDict: k1, k2, k3 = f"{c.freq.value}_D{di}MACD{fastperiod}#{slowperiod}#{signalperiod}#{key}_BS辅助V221028".split('_') - macd = [x.cache[cache_key][key.lower()] for x in c.bars_raw[-5 - di:]] + macd = [x.cache[cache_key][key.lower()] for x in c.bars_raw[-5 - di :]] v1 = "多头" if macd[-di] >= 0 else "空头" v2 = "向上" if macd[-di] >= macd[-di - 1] else "向下" @@ -471,7 +487,7 @@ def tas_macd_first_bs_V221216(c: CZSC, **kwargs): dea = [x.cache[cache_key]['dea'] for x in bars] macd = [x.cache[cache_key]['macd'] for x in bars] n_bars = bars[-10:] - m_bars = bars[-100: -10] + m_bars = bars[-100:-10] high_n = max([x.high for x in n_bars]) low_n = min([x.low for x in n_bars]) high_m = max([x.high for x in m_bars]) @@ -703,8 +719,7 @@ def tas_macd_change_V221105(c: CZSC, **kwargs) -> OrderedDict: signalperiod = int(kwargs.get('signalperiod', 9)) cache_key = update_macd_cache(c, **kwargs) - k1, k2, k3 = f"{c.freq.value}_D{di}K{n}#MACD{fastperiod}#{slowperiod}#{signalperiod}变色次数_BS辅助V221105".split( - '_') + k1, k2, k3 = f"{c.freq.value}_D{di}K{n}#MACD{fastperiod}#{slowperiod}#{signalperiod}变色次数_BS辅助V221105".split('_') bars = get_sub_elements(c.bars_raw, di=di, n=n) dif = [x.cache[cache_key]['dif'] for x in bars] @@ -743,6 +758,7 @@ def tas_macd_change_V221105(c: CZSC, **kwargs) -> OrderedDict: # MA信号计算函数 # ====================================================================================================================== + def tas_ma_base_V221101(c: CZSC, **kwargs) -> OrderedDict: """MA 多空和方向信号 @@ -1002,7 +1018,7 @@ def tas_double_ma_V230511(c: CZSC, **kwargs): if len(c.bars_raw) < t2 + 10: return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) - bars = get_sub_elements(c.bars_raw, di=di, n=t2+1) + bars = get_sub_elements(c.bars_raw, di=di, n=t2 + 1) mean_solid = np.mean([x.solid for x in bars]) bar = c.bars_raw[-di] solid_th = max(bar.upper, bar.lower, mean_solid) @@ -1177,8 +1193,9 @@ def update_kdj_cache(c: CZSC, **kwargs): low = np.array([x.low for x in bars]) close = np.array([x.close for x in bars]) - k, d = ta.STOCH(high, low, close, fastk_period=fastk_period, slowk_period=slowk_period, - slowd_period=slowd_period) + k, d = ta.STOCH( + high, low, close, fastk_period=fastk_period, slowk_period=slowk_period, slowd_period=slowd_period + ) j = list(map(lambda x, y: 3 * x - 2 * y, k, d)) for i in range(len(close)): @@ -1187,12 +1204,13 @@ def update_kdj_cache(c: CZSC, **kwargs): c.bars_raw[i].cache = _c else: - bars = c.bars_raw[-min_count - 10:] + bars = c.bars_raw[-min_count - 10 :] high = np.array([x.high for x in bars]) low = np.array([x.low for x in bars]) close = np.array([x.close for x in bars]) - k, d = ta.STOCH(high, low, close, fastk_period=fastk_period, slowk_period=slowk_period, - slowd_period=slowd_period) + k, d = ta.STOCH( + high, low, close, fastk_period=fastk_period, slowk_period=slowk_period, slowd_period=slowd_period + ) j = list(map(lambda x, y: 3 * x - 2 * y, k, d)) for i in range(1, 6): @@ -1284,8 +1302,10 @@ def tas_kdj_evc_V221201(c: CZSC, **kwargs) -> OrderedDict: c1, c2 = count_range assert c2 > c1 - k1, k2, k3 = f"{c.freq.value}_D{di}T{th}KDJ{fastk_period}#{slowk_period}" \ - f"#{slowd_period}#{key}值突破{c1}#{c2}_KDJ极值V221201".split('_') + k1, k2, k3 = ( + f"{c.freq.value}_D{di}T{th}KDJ{fastk_period}#{slowk_period}" + f"#{slowd_period}#{key}值突破{c1}#{c2}_KDJ极值V221201".split('_') + ) bars = get_sub_elements(c.bars_raw, di=di, n=3 + c2) v1 = "其他" @@ -1345,7 +1365,7 @@ def update_rsi_cache(c: CZSC, **kwargs): else: # 增量更新最近5个K线缓存 - close = np.array([x.close for x in c.bars_raw[-timeperiod - 10:]]) + close = np.array([x.close for x in c.bars_raw[-timeperiod - 10 :]]) rsi = ta.RSI(close, timeperiod=timeperiod) for i in range(1, 6): @@ -1565,15 +1585,20 @@ def tas_second_bs_V230303(c: CZSC, **kwargs): first_bar: RawBar = last_bi.raw_bars[0] last_bar: RawBar = last_bi.raw_bars[-1] - if last_bi.direction == Direction.Down and last_bar.low < last_bar.cache[key] and min( - [x.low for x in _bi_list[-5:]]) == min([x.low for x in _bi_list]) \ - and first_bar.cache[key] < last_bar.cache[key]: + if ( + last_bi.direction == Direction.Down + and last_bar.low < last_bar.cache[key] + and min([x.low for x in _bi_list[-5:]]) == min([x.low for x in _bi_list]) + and first_bar.cache[key] < last_bar.cache[key] + ): v1 = "二买" - if last_bi.direction == Direction.Up and last_bar.high > last_bar.cache[key] and max( - [x.high for x in _bi_list[-5:]]) == max([x.high for x in _bi_list]) and first_bar.cache[key] > \ - last_bar.cache[ - key]: + if ( + last_bi.direction == Direction.Up + and last_bar.high > last_bar.cache[key] + and max([x.high for x in _bi_list[-5:]]) == max([x.high for x in _bi_list]) + and first_bar.cache[key] > last_bar.cache[key] + ): v1 = "二卖" return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) @@ -1697,13 +1722,21 @@ def tas_macd_bs1_V230312(c: CZSC, **kwargs): last_fx: FX = bis[-1].fx_b bi_low = max([x.low for x in bis[:-1] if x.direction == Direction.Up]) - if bis[-1].direction == Direction.Down and bis[-1].low == min([x.low for x in bis]) and bis[-1].high < bi_low and \ - last_fx.raw_bars[-1].cache[cache_key]['macd'] > last_fx.raw_bars[0].cache[cache_key]['macd']: + if ( + bis[-1].direction == Direction.Down + and bis[-1].low == min([x.low for x in bis]) + and bis[-1].high < bi_low + and last_fx.raw_bars[-1].cache[cache_key]['macd'] > last_fx.raw_bars[0].cache[cache_key]['macd'] + ): v1 = "看多" bi_high = min([x.high for x in bis[:-1] if x.direction == Direction.Down]) - if bis[-1].direction == Direction.Up and bis[-1].high == max([x.high for x in bis]) and bis[-1].low > bi_high and \ - last_fx.raw_bars[-1].cache[cache_key]['macd'] < last_fx.raw_bars[0].cache[cache_key]['macd']: + if ( + bis[-1].direction == Direction.Up + and bis[-1].high == max([x.high for x in bis]) + and bis[-1].low > bi_high + and last_fx.raw_bars[-1].cache[cache_key]['macd'] < last_fx.raw_bars[0].cache[cache_key]['macd'] + ): v1 = "看空" return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) @@ -1750,7 +1783,7 @@ def tas_macd_bs1_V230313(c: CZSC, **kwargs): macd.append(x.cache[cache_key]['macd']) n_bars = bars[-10:] - m_bars = bars[-100: -10] + m_bars = bars[-100:-10] high_n = max([x.high for x in n_bars]) low_n = min([x.low for x in n_bars]) high_m = max([x.high for x in m_bars]) @@ -1760,15 +1793,35 @@ def tas_macd_bs1_V230313(c: CZSC, **kwargs): up = [x for x in cross if x['类型'] == "金叉"] dn = [x for x in cross if x['类型'] == "死叉"] - b1_con1a = len(cross) > 3 and cross[-1]['类型'] == '死叉' and cross[-1]['面积'] < cross[-2]['面积'] or cross[-1]['面积'] < cross[-3]['面积'] - b1_con1b = len(cross) > 3 and cross[-1]['类型'] == '金叉' and cross[-1]['面积'] > cross[-2]['面积'] or cross[-1]['面积'] < cross[-3]['面积'] - b1_con2 = len(dn) > 3 and dn[-2]['面积'] < dn[-3]['面积'] # 三次死叉面积逐渐减小 - b1_con3 = len(macd) > 10 and macd[-1] > macd[-2] # MACD向上 + b1_con1a = ( + len(cross) > 3 + and cross[-1]['类型'] == '死叉' + and cross[-1]['面积'] < cross[-2]['面积'] + or cross[-1]['面积'] < cross[-3]['面积'] + ) + b1_con1b = ( + len(cross) > 3 + and cross[-1]['类型'] == '金叉' + and cross[-1]['面积'] > cross[-2]['面积'] + or cross[-1]['面积'] < cross[-3]['面积'] + ) + b1_con2 = len(dn) > 3 and dn[-2]['面积'] < dn[-3]['面积'] # 三次死叉面积逐渐减小 + b1_con3 = len(macd) > 10 and macd[-1] > macd[-2] # MACD向上 if low_n < low_m and (b1_con1a or b1_con1b) and b1_con2 and b1_con3: v1 = "一买" - s1_con1a = len(cross) > 3 and cross[-1]['类型'] == '金叉' and cross[-1]['面积'] > cross[-2]['面积'] or cross[-1]['面积'] > cross[-3]['面积'] - s1_con1b = len(cross) > 3 and cross[-1]['类型'] == '死叉' and cross[-1]['面积'] < cross[-2]['面积'] or cross[-1]['面积'] < cross[-3]['面积'] + s1_con1a = ( + len(cross) > 3 + and cross[-1]['类型'] == '金叉' + and cross[-1]['面积'] > cross[-2]['面积'] + or cross[-1]['面积'] > cross[-3]['面积'] + ) + s1_con1b = ( + len(cross) > 3 + and cross[-1]['类型'] == '死叉' + and cross[-1]['面积'] < cross[-2]['面积'] + or cross[-1]['面积'] < cross[-3]['面积'] + ) s1_con2 = len(up) > 3 and up[-2]['面积'] > up[-3]['面积'] s1_con3 = len(macd) > 10 and macd[-1] < macd[-2] if high_n > high_m and (s1_con1a or s1_con1b) and s1_con2 and s1_con3: @@ -1854,7 +1907,7 @@ def update_cci_cache(c: CZSC, **kwargs): bars = c.bars_raw else: # 增量更新最近5个K线缓存 - bars = c.bars_raw[-timeperiod - 10:] + bars = c.bars_raw[-timeperiod - 10 :] high = np.array([x.high for x in bars]) low = np.array([x.low for x in bars]) @@ -1956,8 +2009,7 @@ def tas_kdj_evc_V230401(c: CZSC, **kwargs) -> OrderedDict: assert 0 < th < 100, "th 必须在 0 到 100 之间" cache_key = update_kdj_cache(c, **kwargs) - k1, k2, k3 = f"{freq}_D{di}T{th}{cache_key}#{key}值突破{min_count}#{max_count}_BS辅助V230401".split( - '_') + k1, k2, k3 = f"{freq}_D{di}T{th}{cache_key}#{key}值突破{min_count}#{max_count}_BS辅助V230401".split('_') v1 = "其他" if len(c.bars_raw) < di + max_count + 2: return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) @@ -2005,7 +2057,7 @@ def update_atr_cache(c: CZSC, **kwargs): bars = c.bars_raw else: # 增量更新最近5个K线缓存 - bars = c.bars_raw[-timeperiod - 80:] + bars = c.bars_raw[-timeperiod - 80 :] high = np.array([x.high for x in bars]) low = np.array([x.low for x in bars]) @@ -2489,7 +2541,7 @@ def tas_cross_status_V230619(c: CZSC, **kwargs) -> OrderedDict: - Signal('日线_D1MACD12#26#9_金死叉V230619_0轴上金叉第1次_任意_任意_0') - Signal('日线_D1MACD12#26#9_金死叉V230619_0轴下死叉第3次_任意_任意_0') - Signal('日线_D1MACD12#26#9_金死叉V230619_0轴下金叉第4次_任意_任意_0') - + :param c: CZSC对象 :param kwargs: 参数字典 @@ -2510,11 +2562,11 @@ def tas_cross_status_V230619(c: CZSC, **kwargs) -> OrderedDict: bars = get_sub_elements(c.bars_raw, di=di, n=100) k1, k2, k3 = f"{freq}_D{di}MACD{fastperiod}#{slowperiod}#{signalperiod}_金死叉V230619".split('_') v1 = "其他" - if len(bars)>=100: + if len(bars) >= 100: dif = [x.cache[cache_key]['dif'] for x in bars] dea = [x.cache[cache_key]['dea'] for x in bars] - num_k = cross_zero_axis(dif, dea) # type: ignore + num_k = cross_zero_axis(dif, dea) # type: ignore dif_temp = get_sub_elements(dif, di=di, n=num_k) dea_temp = get_sub_elements(dea, di=di, n=num_k) @@ -2526,7 +2578,6 @@ def tas_cross_status_V230619(c: CZSC, **kwargs) -> OrderedDict: elif dif[-1] < dea[-1] and dif[-2] > dea[-2]: v1 = f'0轴下死叉第{down_num_sc}次' - elif dif[-1] > 0 and dea[-1] > 0: up_num_sc = down_cross_count(dif_temp, dea_temp) up_num_jc = down_cross_count(dea_temp, dif_temp) @@ -2536,7 +2587,7 @@ def tas_cross_status_V230619(c: CZSC, **kwargs) -> OrderedDict: v1 = f'0轴上死叉第{up_num_sc}次' return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) - + def tas_cross_status_V230624(c: CZSC, **kwargs) -> OrderedDict: """指定金死叉数值信号函数,以此来确定MACD交易区间 贡献者:谌意勇 @@ -2671,3 +2722,104 @@ def tas_cross_status_V230625(c: CZSC, **kwargs) -> OrderedDict: v1 = f'0轴上第{s}次死叉以后' return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + +def tas_low_trend_V230627(c: CZSC, **kwargs) -> OrderedDict: + """阴跌趋势、小阳趋势 + + 参数模板:"{freq}_D{di}N{n}TH{th}_趋势230627" + + **信号逻辑:** + + 1、阴跌趋势:在连续N根K线上rolling计数,如果当前最低价小于rolling min close,min_count + 1 + ,当 min_count > 0.8 * n 且 N根K线中振幅超过TH的K线数量小于0.2 * N,则为阴跌趋势; + 2. 小阳趋势:在连续N根K线上rolling计数,如果当前最高价大于rolling max close,max_count + 1 + ,当 max_count > 0.8 * n 且 N根K线中振幅超过TH的K线数量小于0.2 * N,则为小阳趋势; + + **信号列表:** + + - Signal('15分钟_D1N13TH500_趋势230627_阴跌趋势_任意_任意_0') + - Signal('15分钟_D1N13TH500_趋势230627_小阳趋势_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - n: 从dik往前数n根k线(此数值不需要精确,函数会自动截取最后上下0轴以后的数据) + - th: 实体振幅阈值,单位为 BP + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + n = int(kwargs.get('n', 13)) + th = int(kwargs.get('th', 300)) + freq = c.freq.value + k1, k2, k3 = f"{freq}_D{di}N{n}TH{th}_趋势230627".split('_') + v1 = "其他" + if len(c.bars_raw) < di + n + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=n + 5) + solid_zf = [abs(x.close / x.open - 1) * 10000 for x in bars[5:]] + if len([x for x in solid_zf if x > th]) > max(0.2 * n, 3): + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + min_count = 0 + max_count = 0 + for i in range(5, len(bars)): + bar, w5 = bars[i], bars[:i] + if bar.low <= min([x.close for x in w5]): + min_count += 1 + if bar.high >= max([x.close for x in w5]): + max_count += 1 + + if min_count >= 0.8 * n: + v1 = "阴跌趋势" + if max_count >= 0.8 * n: + v1 = "小阳趋势" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + +def tas_atr_V230630(c: CZSC, **kwargs) -> OrderedDict: + """ATR波动强弱 + + 参数模板:"{freq}_D{di}ATR{timeperiod}_波动V230630" + + **信号逻辑:** + + ATR与收盘价的比值衡量了价格振幅比率的大小,对这个值进行分层。 + + **信号列表:** + + - Signal('日线_D1ATR14_波动V230630_第7层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第6层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第8层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第9层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第10层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第5层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第4层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第3层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第2层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第1层_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: ATR指标的参数 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + freq = c.freq.value + cache_key = update_atr_cache(c, timeperiod=timeperiod) + k1, k2, k3 = f"{freq}_D{di}ATR{timeperiod}_波动V230630".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=100) + lev = [bar.cache[cache_key] / bar.close for bar in bars] + lev = pd.qcut(lev, 10, labels=False, duplicates='drop')[-1] + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=f"第{int(lev+1)}层") diff --git a/czsc/traders/base.py b/czsc/traders/base.py index 8b8daf66b..7194fddc2 100644 --- a/czsc/traders/base.py +++ b/czsc/traders/base.py @@ -407,6 +407,21 @@ def get_ensemble_pos(self, method: Union[AnyStr, Callable] = None) -> float: return pos + def get_position(self, name: str) -> Position: + """获取指定名称的仓位策略对象 + + :param name: 仓位名称 + :return: Position + """ + if not self.positions: + return None + + for position in self.positions: + if position.name == name: + return position + + return None + def take_snapshot(self, file_html=None, width: str = "1400px", height: str = "580px"): """获取快照 diff --git a/czsc/utils/sig.py b/czsc/utils/sig.py index 2bfab226f..fcb477afa 100644 --- a/czsc/utils/sig.py +++ b/czsc/utils/sig.py @@ -34,7 +34,7 @@ def is_symmetry_zs(bis: List[BI], th: float = 0.3) -> bool: if len(bis) % 2 == 0: return False - zs = ZS(symbol=bis[0].symbol, bis=bis) + zs = ZS(bis=bis) if zs.zd > zs.zg or max([x.low for x in bis]) > min([x.high for x in bis]): return False @@ -307,7 +307,7 @@ def get_zs_seq(bis: List[BI]) -> List[ZS]: for bi in bis: if not zs_list: - zs_list.append(ZS(symbol=bi.symbol, bis=[bi])) + zs_list.append(ZS(bis=[bi])) continue zs = zs_list[-1] @@ -317,7 +317,7 @@ def get_zs_seq(bis: List[BI]) -> List[ZS]: else: if (bi.direction == Direction.Up and bi.high < zs.zd) \ or (bi.direction == Direction.Down and bi.low > zs.zg): - zs_list.append(ZS(symbol=bi.symbol, bis=[bi])) + zs_list.append(ZS(bis=[bi])) else: zs.bis.append(bi) zs_list[-1] = zs diff --git a/docs/source/czsc.data.rst b/docs/source/czsc.data.rst index 1b43fce61..c15030517 100644 --- a/docs/source/czsc.data.rst +++ b/docs/source/czsc.data.rst @@ -7,13 +7,6 @@ czsc.data package :no-main-docstr: :no-inheritance-diagram: - -.. automodapi:: czsc.data.jq - :members: - :undoc-members: - :skip: - :no-main-docstr: - .. automodapi:: czsc.data :members: :undoc-members: diff --git a/examples/signals_dev/bar_end_V221211.py b/examples/signals_dev/bar_end_V221211.py deleted file mode 100644 index 615bf2b8f..000000000 --- a/examples/signals_dev/bar_end_V221211.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -# @Time : 2023/6/18 16:11 -# @Author : 琅盎 -# @FileName: BIAS_V1.py -# @Software: PyCharm -from collections import OrderedDict -import numpy as np -from datetime import datetime -from czsc.connectors import research -from czsc import CZSC, check_signals_acc, get_sub_elements -from czsc.utils import create_single_signal, freq_end_time - - -def bar_end_V221211(c: CZSC, freq1='60分钟', **kwargs) -> OrderedDict: - """判断分钟 K 线是否结束 - - 参数模板:"{freq}_{freq1}结束_BS辅助221211" - - **信号逻辑:** - - 以 freq 为基础周期,freq1 为大周期,判断 freq1 K线是否结束。 - 如果结束,返回信号值为 "闭合",否则返回 "未闭x",x 为未闭合的次数。 - - **信号列表:** - - - Signal('15分钟_60分钟结束_BS辅助221211_未闭1_任意_任意_0') - - Signal('15分钟_60分钟结束_BS辅助221211_未闭2_任意_任意_0') - - Signal('15分钟_60分钟结束_BS辅助221211_未闭3_任意_任意_0') - - Signal('15分钟_60分钟结束_BS辅助221211_闭合_任意_任意_0') - - :param c: 基础周期的 CZSC 对象 - :param freq1: 分钟周期名称 - :return: s - """ - freq = c.freq.value - k1, k2, k3 = f"{freq}_{freq1}结束_BS辅助221211".split('_') - assert "分钟" in freq1 - - c1_dt = freq_end_time(c.bars_raw[-1].dt, freq1) - i = 0 - for bar in c.bars_raw[::-1]: - _edt = freq_end_time(bar.dt, freq1) - if _edt != c1_dt: - break - i += 1 - - if c1_dt == c.bars_raw[-1].dt: - v = "闭合" - else: - v = "未闭{}".format(i) - return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v) - - -def main(): - symbols = research.get_symbols('A股主要指数') - bars = research.get_raw_bars(symbols[0], '15分钟', '20181101', '20210101', fq='前复权') - - signals_config = [ - {'name': bar_end_V221211, 'freq': '15分钟', 'freq1': '60分钟', 'di': 1}, - ] - check_signals_acc(bars, signals_config=signals_config, delta_days=0) - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/examples/signals_dev/bar_tnr_V230629.py b/examples/signals_dev/bar_tnr_V230629.py new file mode 100644 index 000000000..207322d5d --- /dev/null +++ b/examples/signals_dev/bar_tnr_V230629.py @@ -0,0 +1,91 @@ +import sys + +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '../..') +sys.path.insert(0, '../../..') +import math +import numpy as np +import pandas as pd +from collections import OrderedDict +from czsc import CZSC +from loguru import logger +from czsc.signals.tas import update_atr_cache +from czsc.utils import create_single_signal, get_sub_elements + + +# 定义信号函数 +# ---------------------------------------------------------------------------------------------------------------------- +def bar_tnr_V230629(c: CZSC, **kwargs) -> OrderedDict: + """趋势噪音指标(TNR,Trend to Noise Rate)分层 + + 参数模板:"{freq}_D{di}TNR{timeperiod}_趋势V230629" + + **信号逻辑:** + + TNR计算公式:取N根K线,首尾两个close的绝对差值 除以 相邻两个close的绝对差值累计。 + + 取最近100个bar的TNR进行分层。 + + **信号列表:** + + - Signal('15分钟_D1TNR14_趋势V230629_第7层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第6层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第8层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第9层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第10层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第5层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第2层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第1层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第3层_任意_任意_0') + - Signal('15分钟_D1TNR14_趋势V230629_第4层_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: TNR指标的参数 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + freq = c.freq.value + + # 更新缓存 + cache_key = f"TNR{timeperiod}" + for i, bar in enumerate(c.bars_raw, 0): + if cache_key in bar.cache: + continue + if i < timeperiod: + bar.cache[cache_key] = 0 + else: + _bars = c.bars_raw[max(0, i - timeperiod):i + 1] + sum_abs = sum([abs(_bars[j].close - _bars[j - 1].close) for j in range(1, len(_bars))]) + bar.cache[cache_key] = 0 if sum_abs == 0 else abs(_bars[-1].close - _bars[0].close) / sum_abs + + k1, k2, k3 = f"{freq}_D{di}TNR{timeperiod}_趋势V230629".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=100) + tnr = [bar.cache[cache_key] for bar in bars] + lev = pd.qcut(tnr, 10, labels=False, duplicates='drop')[-1] + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=f"第{int(lev+1)}层") + + + +def check(): + from czsc.connectors import research + from czsc.traders.base import check_signals_acc + + symbols = research.get_symbols('中证500成分股') + symbol = symbols[10] + bars = research.get_raw_bars(symbol, '15分钟', '20181101', '20210101', fq='前复权') + signals_config = [{'name': bar_tnr_V230629, 'freq': '15分钟', 'di': 1}] + check_signals_acc(bars, signals_config=signals_config, height='780px') # type: ignore + + +if __name__ == '__main__': + check() diff --git a/examples/signals_dev/bar_tnr_V230630.py b/examples/signals_dev/bar_tnr_V230630.py new file mode 100644 index 000000000..df82f3bdb --- /dev/null +++ b/examples/signals_dev/bar_tnr_V230630.py @@ -0,0 +1,84 @@ +import sys + +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '../..') +sys.path.insert(0, '../../..') +import math +import numpy as np +from collections import OrderedDict +from czsc import CZSC +from loguru import logger +from czsc.signals.tas import update_atr_cache +from czsc.utils import create_single_signal, get_sub_elements + + +# 定义信号函数 +# ---------------------------------------------------------------------------------------------------------------------- +def bar_tnr_V230630(c: CZSC, **kwargs) -> OrderedDict: + """趋势噪音指标(TNR,Trend to Noise Rate) + + 参数模板:"{freq}_D{di}TNR{timeperiod}K{k}_趋势V230630" + + **信号逻辑:** + + TNR计算公式:取N根K线,首尾两个close的绝对差值 除以 相邻两个close的绝对差值累计。 + + 噪音变化判断,如果 t 时刻的 TNR > 过去k个TNR的均值,则说明噪音在减少,此时趋势较强;反之,噪音在增加,此时趋势较弱。 + + **信号列表:** + + - Signal('15分钟_D1TNR14K3_趋势V230630_噪音减少_任意_任意_0') + - Signal('15分钟_D1TNR14K3_趋势V230630_噪音增加_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: TNR指标的参数 + - k: 过去k个TNR的均值 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + k = int(kwargs.get('k', 3)) + freq = c.freq.value + + # 更新缓存 + cache_key = f"TNR{timeperiod}" + for i, bar in enumerate(c.bars_raw, 0): + if cache_key in bar.cache: + continue + if i < timeperiod: + bar.cache[cache_key] = 0 + else: + _bars = c.bars_raw[max(0, i - timeperiod):i + 1] + sum_abs = sum([abs(_bars[j].close - _bars[j - 1].close) for j in range(1, len(_bars))]) + bar.cache[cache_key] = 0 if sum_abs == 0 else abs(_bars[-1].close - _bars[0].close) / sum_abs + + k1, k2, k3 = f"{freq}_D{di}TNR{timeperiod}K{k}_趋势V230630".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=k) + delta_tnr = bars[-1].cache[cache_key] - np.mean([bar.cache[cache_key] for bar in bars]) + v1 = "噪音减少" if delta_tnr > 0 else "噪音增加" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + + +def check(): + from czsc.connectors import research + from czsc.traders.base import check_signals_acc + + symbols = research.get_symbols('中证500成分股') + symbol = symbols[10] + bars = research.get_raw_bars(symbol, '15分钟', '20181101', '20210101', fq='前复权') + signals_config = [{'name': bar_tnr_V230630, 'freq': '15分钟', 'di': 1}] + check_signals_acc(bars, signals_config=signals_config, height='780px') # type: ignore + + +if __name__ == '__main__': + check() diff --git a/examples/signals_dev/byi_fx_num_V230628.py b/examples/signals_dev/byi_fx_num_V230628.py new file mode 100644 index 000000000..ba8daaa01 --- /dev/null +++ b/examples/signals_dev/byi_fx_num_V230628.py @@ -0,0 +1,65 @@ +import sys + +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '../..') +sys.path.insert(0, '../../..') +import math +import numpy as np +import pandas as pd +from collections import OrderedDict +from czsc import CZSC +from loguru import logger +from czsc.signals.tas import update_atr_cache +from czsc.utils import create_single_signal, get_sub_elements + + +# 定义信号函数 +# ---------------------------------------------------------------------------------------------------------------------- +def byi_fx_num_V230628(c: CZSC, **kwargs) -> OrderedDict: + """白仪前面下跌或上涨一笔次级别笔结构数量满足条件;贡献者:谌意勇 + + 参数模板:"{freq}_D{di}笔分型数大于{num}_BE辅助V230628" + + **信号逻辑:** + + 对于采用分型停顿或者分型验证开开仓,前一笔内部次级别笔结构尽量带结构, + 此信号函数为当分型笔数量判断大于 num 为满足条件 + + **信号列表:** + + - Signal('15分钟_D1笔分型数大于4_BE辅助V230628_向下_满足_任意_0') + - Signal('15分钟_D1笔分型数大于4_BE辅助V230628_向上_满足_任意_0') + + :param c: CZSC对象 + :param di: 从倒数第几笔开始检查 + :param num: 前笔内部次级别笔数量 + :return: 信号识别结果 + """ + di = int(kwargs.get("di", 1)) + num = int(kwargs.get('num', 4)) + freq = c.freq.value + k1, k2, k3 = f"{freq}_D{di}笔分型数大于{num}_BE辅助V230628".split('_') + v1 = "其他" + if len(c.bi_list) < di + 1 or len(c.bars_ubi) > 7: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bi = c.bi_list[-di] + v1 = bi.direction.value + v2 = "满足" if len(bi.fxs) >= num else "其他" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1, v2=v2) + + +def check(): + from czsc.connectors import research + from czsc.traders.base import check_signals_acc + + symbols = research.get_symbols('中证500成分股') + symbol = symbols[10] + bars = research.get_raw_bars(symbol, '15分钟', '20181101', '20210101', fq='前复权') + signals_config = [{'name': byi_fx_num_V230628, 'freq': '15分钟', 'di': 1}] + check_signals_acc(bars, signals_config=signals_config, height='780px') # type: ignore + + +if __name__ == '__main__': + check() diff --git a/examples/signals_dev/demakder_up_dw_line_V230605.py b/examples/signals_dev/demakder_up_dw_line_V230605.py deleted file mode 100644 index 520d2bdc4..000000000 --- a/examples/signals_dev/demakder_up_dw_line_V230605.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -# @Time : 2023/6/10 13:56 -# @Author : 琅盎 -# @FileName: ER.py -# @Software: PyCharm -from collections import OrderedDict - -import numpy as np -import pandas as pd -from loguru import logger -from czsc.connectors import research -from czsc import CZSC, check_signals_acc, get_sub_elements -from czsc.utils import create_single_signal - - -def er_up_dw_line_V230604(c: CZSC, **kwargs) -> OrderedDict: - """ER价格动量指标,贡献者:琅盎 - - 参数模板:"{freq}_D{di}W{w}N{n}_ER价格动量V230604" - - **信号逻辑:** - - er 为动量指标。用来衡量市场的多空力量对比。在多头市场, - 人们会更贪婪地在接近高价的地方买入,BullPower 越高则当前 - 多头力量越强;而在空头市场,人们可能因为恐惧而在接近低价 - 的地方卖出。BearPower 越低则当前空头力量越强。当两者都大 - 于 0 时,反映当前多头力量占据主导地位;两者都小于 0 则反映 - 空头力量占据主导地位。 - 如果 BearPower 上穿 0,则产生买入信号; - 如果 BullPower 下穿 0,则产生卖出信号。 - - **信号列表:** - - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第10层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第9层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第8层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第5层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第1层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第10层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第2层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第6层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第7层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第8层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第9层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第4层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第5层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第7层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第3层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第2层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第6层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第1层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第4层_任意_0') - - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第3层_任意_0') - - :param c: CZSC对象 - :param kwargs: 参数字典 - - :param di: 信号计算截止倒数第i根K线 - - :param n: 获取K线的根数,默认为105 - - :return: 信号识别结果 - """ - di = int(kwargs.get("di", 1)) - w = int(kwargs.get("w", 60)) - n = int(kwargs.get("n", 10)) - freq = c.freq.value - k1, k2, k3 = f"{freq}_D{di}W{w}N{n}_ER价格动量V230604".split('_') - - cache_key = f"ER{w}" - for i, bar in enumerate(c.bars_raw, 1): - if cache_key in bar.cache: - continue - _bars = c.bars_raw[i-w:i] - ma = np.mean([x.close for x in _bars]) - bull_power = bar.high - ma if bar.high > ma else bar.low - ma - bar.cache.update({cache_key: bull_power}) - - v1 = "其他" - if len(c.bars_raw) < di + w + 10: - return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) - - _bars = get_sub_elements(c.bars_raw, di=di, n=w*10) - factors = [x.cache[cache_key] for x in _bars] - factors = [x for x in factors if x * factors[-1] > 0] - - v1 = "均线上方" if factors[-1] > 0 else "均线下方" - q = pd.cut(factors, n, labels=list(range(1, n+1)), precision=5, duplicates='drop')[-1] - return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1, v2=f"第{q}层") - - -def main(): - symbols = research.get_symbols('A股主要指数') - bars = research.get_raw_bars(symbols[0], '15分钟', '20171101', '20210101', fq='前复权') - - signals_config = [ - {'name': er_up_dw_line_V230604, 'freq': '日线', 'di': 1, 'w': 21, 'n': 10}, - ] - check_signals_acc(bars, signals_config=signals_config) - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/examples/signals_dev/merged/bar_end_V221211.py b/examples/signals_dev/merged/bar_end_V221211.py index 4012d7b83..615bf2b8f 100644 --- a/examples/signals_dev/merged/bar_end_V221211.py +++ b/examples/signals_dev/merged/bar_end_V221211.py @@ -1,10 +1,14 @@ -import talib as ta +# -*- coding: utf-8 -*- +# @Time : 2023/6/18 16:11 +# @Author : 琅盎 +# @FileName: BIAS_V1.py +# @Software: PyCharm +from collections import OrderedDict import numpy as np -from czsc import CZSC from datetime import datetime -from collections import OrderedDict -from czsc.utils.bar_generator import freq_end_time -from czsc.utils import create_single_signal, get_sub_elements +from czsc.connectors import research +from czsc import CZSC, check_signals_acc, get_sub_elements +from czsc.utils import create_single_signal, freq_end_time def bar_end_V221211(c: CZSC, freq1='60分钟', **kwargs) -> OrderedDict: @@ -12,10 +16,17 @@ def bar_end_V221211(c: CZSC, freq1='60分钟', **kwargs) -> OrderedDict: 参数模板:"{freq}_{freq1}结束_BS辅助221211" + **信号逻辑:** + + 以 freq 为基础周期,freq1 为大周期,判断 freq1 K线是否结束。 + 如果结束,返回信号值为 "闭合",否则返回 "未闭x",x 为未闭合的次数。 + **信号列表:** - - Signal('60分钟_K线_结束_否_任意_任意_0') - - Signal('60分钟_K线_结束_是_任意_任意_0') + - Signal('15分钟_60分钟结束_BS辅助221211_未闭1_任意_任意_0') + - Signal('15分钟_60分钟结束_BS辅助221211_未闭2_任意_任意_0') + - Signal('15分钟_60分钟结束_BS辅助221211_未闭3_任意_任意_0') + - Signal('15分钟_60分钟结束_BS辅助221211_闭合_任意_任意_0') :param c: 基础周期的 CZSC 对象 :param freq1: 分钟周期名称 @@ -25,21 +36,29 @@ def bar_end_V221211(c: CZSC, freq1='60分钟', **kwargs) -> OrderedDict: k1, k2, k3 = f"{freq}_{freq1}结束_BS辅助221211".split('_') assert "分钟" in freq1 - dt: datetime = c.bars_raw[-1].dt - v = "是" if freq_end_time(dt, freq1) == dt else "否" - return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v) + c1_dt = freq_end_time(c.bars_raw[-1].dt, freq1) + i = 0 + for bar in c.bars_raw[::-1]: + _edt = freq_end_time(bar.dt, freq1) + if _edt != c1_dt: + break + i += 1 + if c1_dt == c.bars_raw[-1].dt: + v = "闭合" + else: + v = "未闭{}".format(i) + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v) -def check(): - from czsc.connectors import research - from czsc.traders.base import check_signals_acc +def main(): symbols = research.get_symbols('A股主要指数') bars = research.get_raw_bars(symbols[0], '15分钟', '20181101', '20210101', fq='前复权') - signals_config = [{'name': bar_end_V221211, 'freq': '15分钟', 'freq1': '60分钟'}] - check_signals_acc(bars, signals_config=signals_config, height='780px', delta_days=0) - + signals_config = [ + {'name': bar_end_V221211, 'freq': '15分钟', 'freq1': '60分钟', 'di': 1}, + ] + check_signals_acc(bars, signals_config=signals_config, delta_days=0) if __name__ == '__main__': - check() + main() \ No newline at end of file diff --git a/examples/signals_dev/bias_up_dw_line_V230604.py b/examples/signals_dev/merged/bias_up_dw_line_V230604.py similarity index 100% rename from examples/signals_dev/bias_up_dw_line_V230604.py rename to examples/signals_dev/merged/bias_up_dw_line_V230604.py diff --git a/examples/signals_dev/cxt_eleven_bi_V230622.py b/examples/signals_dev/merged/cxt_eleven_bi_V230622.py similarity index 100% rename from examples/signals_dev/cxt_eleven_bi_V230622.py rename to examples/signals_dev/merged/cxt_eleven_bi_V230622.py diff --git a/examples/signals_dev/cxt_five_bi_V230619.py b/examples/signals_dev/merged/cxt_five_bi_V230619.py similarity index 100% rename from examples/signals_dev/cxt_five_bi_V230619.py rename to examples/signals_dev/merged/cxt_five_bi_V230619.py diff --git a/examples/signals_dev/cxt_nine_bi_V230621.py b/examples/signals_dev/merged/cxt_nine_bi_V230621.py similarity index 100% rename from examples/signals_dev/cxt_nine_bi_V230621.py rename to examples/signals_dev/merged/cxt_nine_bi_V230621.py diff --git a/examples/signals_dev/cxt_range_oscillation_V230620.py b/examples/signals_dev/merged/cxt_range_oscillation_V230620.py similarity index 100% rename from examples/signals_dev/cxt_range_oscillation_V230620.py rename to examples/signals_dev/merged/cxt_range_oscillation_V230620.py diff --git a/examples/signals_dev/cxt_seven_bi_V230620.py b/examples/signals_dev/merged/cxt_seven_bi_V230620.py similarity index 100% rename from examples/signals_dev/cxt_seven_bi_V230620.py rename to examples/signals_dev/merged/cxt_seven_bi_V230620.py diff --git a/examples/signals_dev/cxt_three_bi_V230618.py b/examples/signals_dev/merged/cxt_three_bi_V230618.py similarity index 100% rename from examples/signals_dev/cxt_three_bi_V230618.py rename to examples/signals_dev/merged/cxt_three_bi_V230618.py diff --git a/examples/signals_dev/dema_up_dw_line_V230605.py b/examples/signals_dev/merged/dema_up_dw_line_V230605.py similarity index 100% rename from examples/signals_dev/dema_up_dw_line_V230605.py rename to examples/signals_dev/merged/dema_up_dw_line_V230605.py diff --git a/examples/signals_dev/merged/demakder_up_dw_line_V230605.py b/examples/signals_dev/merged/demakder_up_dw_line_V230605.py index aea49422c..520d2bdc4 100644 --- a/examples/signals_dev/merged/demakder_up_dw_line_V230605.py +++ b/examples/signals_dev/merged/demakder_up_dw_line_V230605.py @@ -1,4 +1,10 @@ +# -*- coding: utf-8 -*- +# @Time : 2023/6/10 13:56 +# @Author : 琅盎 +# @FileName: ER.py +# @Software: PyCharm from collections import OrderedDict + import numpy as np import pandas as pd from loguru import logger @@ -7,75 +13,89 @@ from czsc.utils import create_single_signal -def demakder_up_dw_line_V230605(c: CZSC, **kwargs) -> OrderedDict: - """DEM多空,贡献者:琅盎 +def er_up_dw_line_V230604(c: CZSC, **kwargs) -> OrderedDict: + """ER价格动量指标,贡献者:琅盎 - 参数模板:"{freq}_D{di}N{n}H{th}L{tl}_DEM多空V230605" + 参数模板:"{freq}_D{di}W{w}N{n}_ER价格动量V230604" **信号逻辑:** - "Demark Indicator"(也称为 "DeMarker Indicator" 或 "DeM")是一种基于价格和时间的技术分析指标, - 用于衡量市场的过度买入或卖出。它是由 Tom Demark 开发的,旨在识别市场的顶部和底部。 - - 当 demaker>0.7 时上升趋势强烈,当 demaker<0.3 时下跌趋势强烈。 - 当 demaker 上穿 0.7/下穿 0.3 时产生买入/卖出信号。 + er 为动量指标。用来衡量市场的多空力量对比。在多头市场, + 人们会更贪婪地在接近高价的地方买入,BullPower 越高则当前 + 多头力量越强;而在空头市场,人们可能因为恐惧而在接近低价 + 的地方卖出。BearPower 越低则当前空头力量越强。当两者都大 + 于 0 时,反映当前多头力量占据主导地位;两者都小于 0 则反映 + 空头力量占据主导地位。 + 如果 BearPower 上穿 0,则产生买入信号; + 如果 BullPower 下穿 0,则产生卖出信号。 **信号列表:** - - Signal('日线_D1N105TH7TL7_V230605demakder_看空_任意_任意_0') - - Signal('日线_D1N105TH7TL7_V230605demakder_看多_任意_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第10层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第9层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第8层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第5层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第1层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第10层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第2层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第6层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第7层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第8层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第9层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第4层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第5层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第7层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第3层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第2层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第6层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第1层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线下方_第4层_任意_0') + - Signal('日线_D1W21N10_ER价格动量V230604_均线上方_第3层_任意_0') :param c: CZSC对象 :param kwargs: 参数字典 - :param di: 信号计算截止倒数第i根K线 - - :param n: 获取K线的根数,默认为100 - - :param th: 开多预值,默认为7,即demaker上穿0.7 - - :param tl: 开空预值,默认为3,即demaker下穿0.3 + - :param n: 获取K线的根数,默认为105 + :return: 信号识别结果 """ di = int(kwargs.get("di", 1)) - n = int(kwargs.get("n", 100)) - th = int(kwargs.get("th", 7)) - tl = int(kwargs.get("tl", 3)) + w = int(kwargs.get("w", 60)) + n = int(kwargs.get("n", 10)) freq = c.freq.value - k1, k2, k3 = f"{freq}_D{di}N{n}H{th}L{tl}_DEM多空V230605".split('_') + k1, k2, k3 = f"{freq}_D{di}W{w}N{n}_ER价格动量V230604".split('_') + + cache_key = f"ER{w}" + for i, bar in enumerate(c.bars_raw, 1): + if cache_key in bar.cache: + continue + _bars = c.bars_raw[i-w:i] + ma = np.mean([x.close for x in _bars]) + bull_power = bar.high - ma if bar.high > ma else bar.low - ma + bar.cache.update({cache_key: bull_power}) v1 = "其他" - if len(c.bars_raw) < di + n + 10: + if len(c.bars_raw) < di + w + 10: return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) - _bars = get_sub_elements(c.bars_raw, di=di, n=n) - # highs = np.array([bar.high for bar in _bars]) - # lows = np.array([bar.low for bar in _bars]) - - # diff_highs = np.diff(highs) - # diff_lows = np.diff(lows) - - # demax = np.mean(diff_highs[diff_highs > 0]) - # demin = np.mean(diff_lows[diff_lows < 0]) - - demax = np.mean([_bars[i].high - _bars[i-1].high for i in range(1, len(_bars)) if _bars[i].high - _bars[i-1].high > 0]) - demin = np.mean([_bars[i-1].low - _bars[i].low for i in range(1, len(_bars)) if _bars[i-1].low - _bars[i].low > 0]) - demaker = demax / (demax + demin) - - # logger.info(f"demaker:{demaker}, demax:{demax}, demin:{demin}") - if demaker > th / 10: - v1 = "看多" - if demaker < tl / 10: - v1 = "看空" + _bars = get_sub_elements(c.bars_raw, di=di, n=w*10) + factors = [x.cache[cache_key] for x in _bars] + factors = [x for x in factors if x * factors[-1] > 0] - return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + v1 = "均线上方" if factors[-1] > 0 else "均线下方" + q = pd.cut(factors, n, labels=list(range(1, n+1)), precision=5, duplicates='drop')[-1] + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1, v2=f"第{q}层") def main(): symbols = research.get_symbols('A股主要指数') - bars = research.get_raw_bars(symbols[0], '15分钟', '20181101', '20210101', fq='前复权') + bars = research.get_raw_bars(symbols[0], '15分钟', '20171101', '20210101', fq='前复权') signals_config = [ - {'name': demakder_up_dw_line_V230605, 'freq': '30分钟', 'di': 1}, + {'name': er_up_dw_line_V230604, 'freq': '日线', 'di': 1, 'w': 21, 'n': 10}, ] - check_signals_acc(bars, signals_config=signals_config) # type: ignore + check_signals_acc(bars, signals_config=signals_config) if __name__ == '__main__': - main() + main() \ No newline at end of file diff --git a/examples/signals_dev/pos_fix_exit_V230624.py b/examples/signals_dev/merged/pos_fix_exit_V230624.py similarity index 100% rename from examples/signals_dev/pos_fix_exit_V230624.py rename to examples/signals_dev/merged/pos_fix_exit_V230624.py diff --git a/examples/signals_dev/pos_profit_loss_V230624.py b/examples/signals_dev/merged/pos_profit_loss_V230624.py similarity index 100% rename from examples/signals_dev/pos_profit_loss_V230624.py rename to examples/signals_dev/merged/pos_profit_loss_V230624.py diff --git a/examples/signals_dev/tas_cross_status_V230619.py b/examples/signals_dev/merged/tas_cross_status_V230619.py similarity index 100% rename from examples/signals_dev/tas_cross_status_V230619.py rename to examples/signals_dev/merged/tas_cross_status_V230619.py diff --git a/examples/signals_dev/tas_cross_status_V230624.py b/examples/signals_dev/merged/tas_cross_status_V230624.py similarity index 100% rename from examples/signals_dev/tas_cross_status_V230624.py rename to examples/signals_dev/merged/tas_cross_status_V230624.py diff --git a/examples/signals_dev/signal_match.py b/examples/signals_dev/signal_match.py index b0172d56c..192ba3466 100644 --- a/examples/signals_dev/signal_match.py +++ b/examples/signals_dev/signal_match.py @@ -7,15 +7,20 @@ """ import os import sys -# sys.path.insert(0, r'..\..') +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '...') +sys.path.insert(0, '../../..') # # 插入用户自定义信号函数模块所在目录 # sys.path.insert(0, os.path.join(os.path.expanduser('~'), '.czsc/czsc_usr_signals')) import re +import czsc from loguru import logger from czsc.utils import import_by_name from czsc import SignalsParser +czsc.welcome() signals_module_name = 'czsc.signals' signals_seq = [] @@ -39,14 +44,13 @@ conf = sp.parse(signals_seq) parsed_name = {x['name'] for x in conf} print(f"total signal functions: {len(sp.sig_name_map)}; parsed: {len(parsed_name)}") - - # # # 测试信号配置生成信号 - # from czsc import generate_czsc_signals, get_signals_freqs, get_signals_config - # from test.test_analyze import read_1min - # bars = read_1min() - - # conf = get_signals_config(signals_seq) - # freqs = get_signals_freqs(signals_seq) - - # sigs = generate_czsc_signals(bars, signals_config=conf, sdt='20180101', df=True) - + # total signal functions: 147; parsed: 147 + + # 测试信号配置生成信号 + from czsc import generate_czsc_signals, get_signals_freqs, get_signals_config + from test.test_analyze import read_1min + bars = read_1min() + conf = get_signals_config(signals_seq) + freqs = get_signals_freqs(signals_seq) + sigs = generate_czsc_signals(bars, signals_config=conf, sdt='20180101', df=True) + print(sigs.shape) diff --git a/examples/signals_dev/tas_atr_V230630.py b/examples/signals_dev/tas_atr_V230630.py new file mode 100644 index 000000000..407b52cc3 --- /dev/null +++ b/examples/signals_dev/tas_atr_V230630.py @@ -0,0 +1,76 @@ +import sys + +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '../..') +sys.path.insert(0, '../../..') +import math +import pandas as pd +from collections import OrderedDict +from czsc import CZSC +from loguru import logger +from czsc.signals.tas import update_atr_cache +from czsc.utils import create_single_signal, get_sub_elements + + +# 定义信号函数 +# ---------------------------------------------------------------------------------------------------------------------- + +def tas_atr_V230630(c: CZSC, **kwargs) -> OrderedDict: + """ATR波动强弱 + + 参数模板:"{freq}_D{di}ATR{timeperiod}_波动V230630" + + **信号逻辑:** + + ATR与收盘价的比值衡量了价格振幅比率的大小,对这个值进行分层。 + + **信号列表:** + + - Signal('日线_D1ATR14_波动V230630_第7层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第6层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第8层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第9层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第10层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第5层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第4层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第3层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第2层_任意_任意_0') + - Signal('日线_D1ATR14_波动V230630_第1层_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - timeperiod: ATR指标的参数 + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + timeperiod = int(kwargs.get('timeperiod', 14)) + freq = c.freq.value + cache_key = update_atr_cache(c, timeperiod=timeperiod) + k1, k2, k3 = f"{freq}_D{di}ATR{timeperiod}_波动V230630".split('_') + v1 = "其他" + if len(c.bars_raw) < di + timeperiod + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=100) + lev = [bar.cache[cache_key] / bar.close for bar in bars] + lev = pd.qcut(lev, 10, labels=False, duplicates='drop')[-1] + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=f"第{int(lev+1)}层") + + +def check(): + from czsc.connectors import research + from czsc.traders.base import check_signals_acc + + symbols = research.get_symbols('中证500成分股') + symbol = symbols[10] + bars = research.get_raw_bars(symbol, '15分钟', '20151101', '20210101', fq='前复权') + signals_config = [{'name': tas_atr_V230630, 'freq': '日线', 'di': 1}] + check_signals_acc(bars, signals_config=signals_config, height='780px', sdt='20200101') # type: ignore + + +if __name__ == '__main__': + check() diff --git a/examples/signals_dev/tas_low_trend_V230627.py b/examples/signals_dev/tas_low_trend_V230627.py new file mode 100644 index 000000000..3f675fe8a --- /dev/null +++ b/examples/signals_dev/tas_low_trend_V230627.py @@ -0,0 +1,90 @@ +import sys + +sys.path.insert(0, '.') +sys.path.insert(0, '..') +sys.path.insert(0, '../..') +sys.path.insert(0, '../../..') +import pandas as pd +import numpy as np +from collections import OrderedDict +from czsc import CZSC +from loguru import logger +from czsc.signals.tas import update_macd_cache, update_ma_cache +from czsc.utils import get_sub_elements, create_single_signal, fast_slow_cross +from czsc.utils.sig import cross_zero_axis, cal_cross_num +from czsc.objects import Direction +from typing import List, Union + + +# 定义信号函数 +# ---------------------------------------------------------------------------------------------------------------------- +def tas_low_trend_V230627(c: CZSC, **kwargs) -> OrderedDict: + """阴跌趋势、小阳趋势 + + 参数模板:"{freq}_D{di}N{n}TH{th}_趋势230627" + + **信号逻辑:** + + 1、阴跌趋势:在连续N根K线上rolling计数,如果当前最低价小于rolling min close,min_count + 1 + ,当 min_count > 0.8 * n 且 N根K线中振幅超过TH的K线数量小于0.2 * N,则为阴跌趋势; + 2. 小阳趋势:在连续N根K线上rolling计数,如果当前最高价大于rolling max close,max_count + 1 + ,当 max_count > 0.8 * n 且 N根K线中振幅超过TH的K线数量小于0.2 * N,则为小阳趋势; + + **信号列表:** + + - Signal('15分钟_D1N13TH500_趋势230627_阴跌趋势_任意_任意_0') + - Signal('15分钟_D1N13TH500_趋势230627_小阳趋势_任意_任意_0') + + :param c: czsc对象 + :param kwargs: + + - di: 倒数第i根K线 + - n: 从dik往前数n根k线(此数值不需要精确,函数会自动截取最后上下0轴以后的数据) + - th: 实体振幅阈值,单位为 BP + + :return: 信号字典 + """ + di = int(kwargs.get('di', 1)) + n = int(kwargs.get('n', 13)) + th = int(kwargs.get('th', 300)) + freq = c.freq.value + k1, k2, k3 = f"{freq}_D{di}N{n}TH{th}_趋势230627".split('_') + v1 = "其他" + if len(c.bars_raw) < di + n + 8: + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + bars = get_sub_elements(c.bars_raw, di=di, n=n+5) + solid_zf = [abs(x.close / x.open - 1) * 10000 for x in bars[5:]] + if len([x for x in solid_zf if x > th]) > max(0.2 * n, 3): + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + min_count = 0 + max_count = 0 + for i in range(5, len(bars)): + bar, w5 = bars[i], bars[:i] + if bar.low <= min([x.close for x in w5]): + min_count += 1 + if bar.high >= max([x.close for x in w5]): + max_count += 1 + + if min_count >= 0.8 * n: + v1 = "阴跌趋势" + if max_count >= 0.8 * n: + v1 = "小阳趋势" + return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1) + + + +def check(): + from czsc.connectors import research + from czsc.traders.base import check_signals_acc + + symbols = research.get_symbols('中证500成分股') + symbol = symbols[0] + bars = research.get_raw_bars(symbol, '15分钟', '20181101', '20210101', fq='前复权') + signals_config = [{'name': tas_low_trend_V230627, 'freq': '日线', 'di': 1, 'th': 500, 'n': 21}] + check_signals_acc(bars, signals_config=signals_config, height='780px') # type: ignore + + +if __name__ == '__main__': + check()