Revert chan to the most valuable one

This commit is contained in:
jackyu66git
2025-06-06 17:43:35 +08:00
parent 8662b633ae
commit 6fbdf65422
4 changed files with 93 additions and 136 deletions
+7 -93
View File
@@ -31,10 +31,6 @@ class ChanKLC():
self.rsi = klu.rsi
self.volume_ratio = klu.volume_ratio
self.macdhist = 0
# === 新增:KLC类型 ===
self.klc_type = None # KLC类型:大阳线、大阴线、小阳线、小阴线
def set_klc_fx_type(self, klc_fx_type):
#print(self.start_time, klc_fx_type, self.get_feature_data()['klu_macd'], self.get_feature_data()['klu_macdhist'], self.get_feature_data()['klu_rsi'])
self.klc_fx_type = klc_fx_type
@@ -53,13 +49,7 @@ class ChanKLC():
self.volume_ratio = self.volume_ratio / len(self.klus)
self.volume = self.volume / len(self.klus)
self.macdhist = self.macdhist / len(self.klus)
def contain_klu_fx(self):
if len(self.klus) > 0:
for klu in self.klus:
klu.update_realtime_analysis()
if klu.fx_type == self.fx and klu.fx_strength > 1.8:
return True
return False
def set_next(self, klc):
self.next = klc
def set_pre(self, klc):
@@ -1157,78 +1147,6 @@ class ChanKLC():
return features
def check_fx_klu_strength(self):
if self.pre and self.next and self.next.end_klu:
strength = 0
klc1 = self.pre
klc2 = self
klc3 = self.next
# 检查包含关系
inc = self.check_include_relation(klc1, klc2, klc3)
if not inc:
# 检查分型类型
if self.fx == Chan_FX_TYPE.TOP:
# (1)没有包含关系+1
strength += 0
print(self.start_time, "1")
# (2)第1条K线是一条大阳线,而第2、3条K线是小阴线、小阳线,那么这个分型结构的意义就不大了,强度 -1
if self.pre.cal_klu_min_max() > 0.5 and self.cal_klu_min_max() < 0.2 and self.next.cal_klu_min_max() < 0.2:
strength += -1
print(self.start_time, "2")
# (3)第2条K线有长上影线或者就是大阴线,而第3条K线不能以阳线收在第2条K线区间的一半之上,那么该顶分型的力度就比较大
if ((self.close < self.open and self.cal_klu_min_max() > 0.5) or self.cal_klu_upper_shadow() > 0.6) and not (self.next.close > self.next.open and self.next.close > (self.high-self.low)/2):
strength += 1
print(self.start_time, "3")
else:
if self.fx == Chan_FX_TYPE.TOP:
# (4)第2条K线和第3条K线为包含关系,而第3条K线为大阴线(直接把为阳线的第2条K线“吃掉”),这是最坏的一种包含关系。
if self.start_klu.index - self.end_klu.index < 0:
klu1 = self.start_klu
klu2 = self.klus[1]
print(self.start_time, self.end_time, "length: ", len(self.klus), klu1.index, klu2.index)
if klu2.close < klu2.open and 100*abs(klu2.open - klu2.close) / klu2.open > 0.5 and klu1.high <= klu2.high and klu1.low >= klu2.low:
strength += 1
print(self.start_time, "4")
# (5)第3条K线如果跌破第1条K线的底而且不能高于第1条K线区间的一半之上,则属于较弱的一种,也就是说这种顶分型出现后股价见顶的可能性不大。
if self.next.low < self.pre.low and self.next.high < (self.pre.high + self.pre.low)/2:
strength += -1
print(self.start_time, "5")
return strength
else:
return 0
def cal_klu_upper_shadow(self):
"""
计算KLC的上影线长度
上影线 = 最高价 - max(开盘价, 收盘价)
"""
if self.high <= 0: # 避免无效数据
return 0
# 计算上影线长度
upper_shadow = self.high - max(self.open, self.close)
# 计算相对上影线长度(相对于整个K线区间)
total_range = self.high - self.low
if total_range > 0:
upper_shadow_ratio = upper_shadow / total_range
else:
upper_shadow_ratio = 0
return upper_shadow_ratio
def cal_klu_min_max(self):
"""
计算KLC类型:大阳线、大阴线、小阳线、小阴线
"""
if self.open <= 0: # 避免除零错误
return 0
line_type = 1000*abs(self.open - self.close) / self.open
print(self.start_time, line_type)
return line_type
def check_include_relation(self, klc1, klc2, klc3):
if klc1.start_klu.index - klc1.end_klu.index == 0 and klc2.start_klu.index - klc2.end_klu.index == 0 and klc3.start_klu.index - klc3.end_klu.index == 0:
return False
else:
return True
def cal_fx_strength(self):
"""
用self.pre和self.next实现分型强弱判断
@@ -1246,12 +1164,11 @@ class ChanKLC():
-2: 弱分型(明显中继)
-3: 极弱分型(无效分型)
"""
#return self.check_fx_klu_strength()
# 检查是否为分型,且有前后K线数据
if self.fx == Chan_FX_TYPE.UNKNOWN or self.klc_fx_type == Chan_FX_TYPE.UNKNOWN:
if self.fx == Chan_FX_TYPE.UNKNOWN:
return 0
if not self.pre or not self.next:
return -100
return 100
# === 核心判断:分型在笔中的位置 ===
# 1. 检查这个分型是否能够终结当前笔
@@ -1283,7 +1200,6 @@ class ChanKLC():
# 分型质量调整
base_score += fx_quality
#base_score += self.check_fx_klu_strength()
#print(self.start_time, base_score, is_bi_end, post_fx_confirmation, fx_quality)
# 限制在-3到3范围内
return max(-3, min(3, base_score))
@@ -1334,7 +1250,7 @@ class ChanKLC():
first_low = self.pre.low
middle_low = self.low
key_support = min(first_low, middle_low)
last_klc = None
for i, klc in enumerate(subsequent_klcs):
# 检查是否跌破关键支撑
if klc.low < key_support:
@@ -1347,8 +1263,7 @@ class ChanKLC():
# 检查下跌趋势
if i > 0 and klc.close < subsequent_klcs[i-1].close:
downward_trend += 1
last_klc = klc
print(last_klc.start_time, last_klc.end_time)
# 强烈笔终结:跌破关键位且无新高
if broken_key_levels >= 1 and new_highs == 0 and downward_trend >= 2:
return 2
@@ -1381,7 +1296,7 @@ class ChanKLC():
first_high = self.pre.high
middle_high = self.high
key_resistance = max(first_high, middle_high)
last_klc = None
for i, klc in enumerate(subsequent_klcs):
# 检查是否突破关键阻力
if klc.high > key_resistance:
@@ -1394,8 +1309,7 @@ class ChanKLC():
# 检查上涨趋势
if i > 0 and klc.close > subsequent_klcs[i-1].close:
upward_trend += 1
last_klc = klc
print(last_klc.start_time, last_klc.end_time)
# 强烈笔终结:突破关键位且无新低
if broken_key_levels >= 1 and new_lows == 0 and upward_trend >= 2:
return 2
+8 -16
View File
@@ -67,12 +67,12 @@ class ChanLun():
else:
print(bi.start_klc.end_time, bi.dir, bi.is_sure)
def check_fx(self, klc):
if klc.pre and klc.next and klc.next.end_klu:
if klc.pre and klc.next:
if klc.high > klc.pre.high and klc.high > klc.next.high:
klc.set_fx(Chan_FX_TYPE.TOP)
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP")
return Chan_FX_TYPE.TOP
if klc.pre and klc.next and klc.next.end_klu:
if klc.pre and klc.next:
if klc.low < klc.pre.low and klc.low < klc.next.low:
klc.set_fx(Chan_FX_TYPE.BOTTOM)
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM")
@@ -149,9 +149,9 @@ class ChanLun():
klc = klc_list[klc_index]
if klc.end_klu and klc.end_klu.idx == index:
klc_index += 1
if (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2) and klc.contain_klu_fx():
if klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2:
fx_list.append(1)
elif (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2) and klc.contain_klu_fx():
elif klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2:
fx_list.append(-1)
else:
fx_list.append(0)
@@ -235,7 +235,6 @@ class ChanLun():
def get_kl_data(self, dataframe:DataFrame):
fields = "time,open,high,low,close,volume"
klu_list = []
last_klu = None
for i in range(0, len(dataframe)):
item = dataframe.iloc[i]
date = item['date']
@@ -259,13 +258,8 @@ class ChanLun():
klu = ChanKLU(time_str, o, h, l, c, v)
klu.set_idx(i)
klu_list.append(klu)
if last_klu:
klu.set_pre(last_klu)
last_klu.set_next(klu)
last_klu.detect_realtime_fx()
if 'macd' in item:
klu.set_indicators(item)
last_klu = klu
return klu_list
def cal_volume_ratio(self, dataframe, window=10):
df = dataframe.copy()
@@ -697,11 +691,10 @@ class ChanLun():
else:
# A new top found
#last_top.set_fx(Chan_FX_TYPE.UNKNOWN)
#print(klc.start_time, last_top.start_time, klc.cal_fx_strength(), "一类卖点Sell 1")
last_top = klc
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1")
klc.set_klc_fx_type(Chan_KLC_FX.TOP1)
#print(klc.start_time, klc.fx, "一类卖点Sell 1")
#klc.set_fx(fx)
#klc.set_state("10")
bi_list[-1].add_klc(klc)
@@ -732,7 +725,6 @@ class ChanLun():
#klc.set_state("10")
#print(klc.start_time, klc.fx, "笔卖点Sell 1")
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
#print(klc.start_time, last_top.start_time, klc.fx, "二类卖点Sell 2")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
else:
@@ -757,7 +749,7 @@ class ChanLun():
#klc.set_state('30')
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, last_top.start_time, "Normal TOP Found, Confirm down bi 4")
#print(klc.start_time, last_bottom.start_time, "Normal TOP Found, Confirm down bi 4")
#print(klc.start_time, klc.fx, "笔卖点Sell 2")
# last bottom = None
else:
@@ -1222,8 +1214,8 @@ class ChanLun():
klc.set_pre(last_klc)
last_klc.set_end_klu(last_klu)
klc.set_pre_fx()
#else:
#last_klc.add_klu(klu)
else:
last_klc.add_klu(klu)
else:
ddir = Chan_KLINE_DIR.UP
if klu.open > klu.close:
+21 -27
View File
@@ -14,23 +14,20 @@ import talib.abstract as ta
from pandas import DataFrame
from datetime import datetime, timedelta
from freqtrade.persistence import Trade
from typing import Optional, List, Dict
from typing import Optional
import logging
logger = logging.getLogger(__name__)
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal
### Now you can use logger.info('asfd') to log
# freqtrade plot-dataframe --strategy ChanLun_BTC_15 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_15.json --timerange=20250309-
# freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --export none --strategy-path ./user_data/Chan/strategies --timerange=20250525-
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json -t 1m --pairs SOL/USDT:USDT --timerange=20250405-
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250501
# freqtrade live-backtest -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405-
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250401
# sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
# sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
# sudo docker compose run --rm chanlun_btc trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies
# sudo docker compose run --rm chan_btc backtesting -c ./user_data/Chan/config/ChanLun_SOL.json --strategy ChanLun_SOL --strategy-path ./user_data/Chan/strategies --timerange=20250101-
# sudo docker compose run --rm chan_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
# sudo docker compose run --rm chan_btc trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies
class ChanLun_BTC_15(IStrategy):
INTERFACE_VERSION: int = 3
@@ -38,10 +35,10 @@ class ChanLun_BTC_15(IStrategy):
# This attribute will be overridden if the config file contains "minimal_roi"
# 30m and 1h
minimal_roi = {
"0": 0.15,
"240": 0.1,
"480": 0.02,
"960": 0
"0": 0.60,
"360": 0.2,
"640": 0.1,
"1200": 0
}
# 5m and 15m
minimal_roi_1 = {
@@ -64,13 +61,14 @@ class ChanLun_BTC_15(IStrategy):
"3600": 0
}
can_short = True
lev = 10
stoploss = -0.8
lev = 50.0
stoploss = -0.3
trailing_stop = False
trailing_stop_positive = 0.025
trailing_stop_positive_offset = 0.045
trailing_only_offset_is_reached = False
position_adjustment_enable = True
startup_candle_count = 600
time5 = 5
@@ -173,13 +171,11 @@ class ChanLun_BTC_15(IStrategy):
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5)
fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time5)
#state_str = 'state'
#fx_str = 'fx'
dataframe.loc[
(
#(dataframe['state'] == "-30")
(dataframe[state_str].shift(self.time5*2) > 0) &
(dataframe[fx_str].shift(self.time5*2) == -1)
(dataframe[state_str].shift(self.time5) > 1.0) &
(dataframe[fx_str].shift(self.time5) == -1)
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
@@ -189,8 +185,8 @@ class ChanLun_BTC_15(IStrategy):
dataframe.loc[
(
#(dataframe['state'] == "-30")
(dataframe[state_str].shift(self.time5*2) > 0) &
(dataframe[fx_str].shift(self.time5*2) == 1)
(dataframe[state_str].shift(self.time5) > 1.0) &
(dataframe[fx_str].shift(self.time5) == 1)
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
@@ -201,13 +197,11 @@ class ChanLun_BTC_15(IStrategy):
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5)
fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time5)
#state_str = 'state'
#fx_str = 'fx'
dataframe.loc[
(
#(dataframe['state']== "30")
(dataframe[state_str].shift(self.time5*2) > 0) &
(dataframe[fx_str].shift(self.time5*2) == 1)
(dataframe[state_str].shift(self.time5) > 1.0) &
(dataframe[fx_str].shift(self.time5) == 1)
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
),
@@ -215,8 +209,8 @@ class ChanLun_BTC_15(IStrategy):
dataframe.loc[
(
#(dataframe['state']== "30")
(dataframe[state_str].shift(self.time5*2) > 0) &
(dataframe[fx_str].shift(self.time5*2) == -1)
(dataframe[state_str].shift(self.time5) > 1.0) &
(dataframe[fx_str].shift(self.time5) == -1)
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
),
+57
View File
@@ -16,3 +16,60 @@
2025/05/27 01:57:54 [notice] 1#1: start worker process 29
2025/05/27 01:57:54 [notice] 1#1: start worker process 30
2025/05/27 01:57:54 [notice] 1#1: start worker process 31
2025/06/05 02:20:20 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2025/06/05 02:20:20 [notice] 20#20: gracefully shutting down
2025/06/05 02:20:20 [notice] 20#20: exiting
2025/06/05 02:20:20 [notice] 21#21: gracefully shutting down
2025/06/05 02:20:20 [notice] 21#21: exiting
2025/06/05 02:20:20 [notice] 22#22: gracefully shutting down
2025/06/05 02:20:20 [notice] 23#23: gracefully shutting down
2025/06/05 02:20:20 [notice] 23#23: exiting
2025/06/05 02:20:20 [notice] 29#29: gracefully shutting down
2025/06/05 02:20:20 [notice] 29#29: exiting
2025/06/05 02:20:20 [notice] 30#30: gracefully shutting down
2025/06/05 02:20:20 [notice] 30#30: exiting
2025/06/05 02:20:20 [notice] 28#28: gracefully shutting down
2025/06/05 02:20:20 [notice] 28#28: exiting
2025/06/05 02:20:20 [notice] 31#31: gracefully shutting down
2025/06/05 02:20:20 [notice] 31#31: exiting
2025/06/05 02:20:20 [notice] 24#24: gracefully shutting down
2025/06/05 02:20:20 [notice] 24#24: exiting
2025/06/05 02:20:20 [notice] 25#25: gracefully shutting down
2025/06/05 02:20:20 [notice] 25#25: exiting
2025/06/05 02:20:20 [notice] 27#27: gracefully shutting down
2025/06/05 02:20:20 [notice] 27#27: exiting
2025/06/05 02:20:20 [notice] 26#26: gracefully shutting down
2025/06/05 02:20:20 [notice] 26#26: exiting
2025/06/05 02:20:20 [notice] 20#20: exit
2025/06/05 02:20:20 [notice] 21#21: exit
2025/06/05 02:20:20 [notice] 22#22: exiting
2025/06/05 02:20:20 [notice] 23#23: exit
2025/06/05 02:20:20 [notice] 29#29: exit
2025/06/05 02:20:20 [notice] 30#30: exit
2025/06/05 02:20:20 [notice] 28#28: exit
2025/06/05 02:20:20 [notice] 31#31: exit
2025/06/05 02:20:20 [notice] 24#24: exit
2025/06/05 02:20:20 [notice] 25#25: exit
2025/06/05 02:20:20 [notice] 27#27: exit
2025/06/05 02:20:20 [notice] 26#26: exit
2025/06/05 02:20:20 [notice] 22#22: exit
2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 25
2025/06/05 02:20:20 [notice] 1#1: worker process 21 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 22 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 24 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 25 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 26 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 29 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 31 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: signal 29 (SIGIO) received
2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 24
2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 27
2025/06/05 02:20:20 [notice] 1#1: worker process 27 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 28 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: signal 29 (SIGIO) received
2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 28
2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 20
2025/06/05 02:20:20 [notice] 1#1: worker process 20 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 23 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: worker process 30 exited with code 0
2025/06/05 02:20:20 [notice] 1#1: exit