From 2440e896ead3912f728385932628cc5277503505 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Fri, 9 May 2025 21:10:45 +0800 Subject: [PATCH] add more strategies --- ChanKLU.py | 27 +- ChanLun.py | 112 +------ __pycache__/ChanBI.cpython-312.pyc | Bin 6709 -> 6715 bytes __pycache__/ChanBSP.cpython-312.pyc | Bin 1368 -> 1359 bytes __pycache__/ChanCTime.cpython-312.pyc | Bin 3405 -> 3396 bytes __pycache__/ChanEnum.cpython-312.pyc | Bin 6301 -> 6292 bytes __pycache__/ChanKLC.cpython-312.pyc | Bin 47247 -> 47250 bytes __pycache__/ChanKLU.cpython-312.pyc | Bin 3191 -> 3698 bytes __pycache__/ChanLun.cpython-312.pyc | Bin 117148 -> 111155 bytes __pycache__/ChanSBI.cpython-312.pyc | Bin 3876 -> 3867 bytes __pycache__/ChanSEG.cpython-312.pyc | Bin 3488 -> 3479 bytes __pycache__/ChanZS.cpython-312.pyc | Bin 3805 -> 3796 bytes config/ChanLun_SOL.json | 4 +- config/ChanLun_SOL_Optimized.json | 151 +++++++++ strategies/ChanLun_SOL_5.py | 44 ++- strategies/ChanLun_SOL_Optimized.py | 386 +++++++++++++++++++++++ strategies/Chan_SOL_2.py | 434 +++++++++++++++++--------- 17 files changed, 887 insertions(+), 271 deletions(-) create mode 100644 config/ChanLun_SOL_Optimized.json create mode 100644 strategies/ChanLun_SOL_Optimized.py diff --git a/ChanKLU.py b/ChanKLU.py index 9485333..cf62525 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -16,6 +16,8 @@ class ChanKLU: self.ma5 = 0 self.ma10 = 0 self.ma30 = 0 + self.ma50 = 0 + self.ma200 = 0 self.ma250 = 0 self.rsi = 0 self.volume_ratio = 0 @@ -23,15 +25,20 @@ class ChanKLU: self.idx = idx self.index = idx def set_indicators(self, item): - self.macd = float(item['macd']) if item['macd'] else 0 - self.signal = float(item['macdsignal']) if item['macdsignal'] else 0 - self.macdhist = float(item['macdhist']) if item['macdhist'] else 0 - self.ma5 = float(item['ma5']) if item['ma5'] else 0 - self.ma10 = float(item['ma10']) if item['ma10'] else 0 - self.ma30 = float(item['ma30']) if item['ma30'] else 0 - self.ma250 = float(item['ma250']) if item['ma250'] else 0 - self.rsi = float(item['rsi']) if item['rsi'] else 0 - self.volume_ratio = float(item['volume_ratio']) if item['volume_ratio'] else 0 + self.macd = float(item['macd']) if 'macd' in item and item['macd'] else 0 + self.signal = float(item['macdsignal']) if 'macdsignal' in item and item['macdsignal'] else 0 + self.macdhist = float(item['macdhist']) if 'macdhist' in item and item['macdhist'] else 0 + self.ma5 = float(item['ma5']) if 'ma5' in item and item['ma5'] else 0 + self.ma10 = float(item['ma10']) if 'ma10' in item and item['ma10'] else 0 + self.ma30 = float(item['ma30']) if 'ma30' in item and item['ma30'] else 0 + + # 安全检查 ma250、ma50 和 ma200 + self.ma250 = float(item['ma250']) if 'ma250' in item and item['ma250'] else 0 + self.ma50 = float(item['ma50']) if 'ma50' in item and item['ma50'] else 0 + self.ma200 = float(item['ma200']) if 'ma200' in item and item['ma200'] else 0 + + self.rsi = float(item['rsi']) if 'rsi' in item and item['rsi'] else 0 + self.volume_ratio = float(item['volume_ratio']) if 'volume_ratio' in item and item['volume_ratio'] else 0 def get_feature_data(self): features = dict() features['klu_close'] = self.close @@ -46,6 +53,8 @@ class ChanKLU: features['klu_ma5'] = self.ma5 features['klu_ma10'] = self.ma10 features['klu_ma30'] = self.ma30 + features['klu_ma50'] = self.ma50 + features['klu_ma200'] = self.ma200 features['klu_ma250'] = self.ma250 features['klu_rsi'] = self.rsi features['klu_volume_ratio'] = self.volume_ratio diff --git a/ChanLun.py b/ChanLun.py index 4616d14..d5712d3 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -97,6 +97,14 @@ class ChanLun(): state_list = [] if len(klc_list) > 0: klc_index = 0 + # 添加趋势强度判断 + dataframe['trend_strength'] = abs(dataframe['close'].pct_change(20)) + + # 添加波动率判断 + dataframe['volatility'] = dataframe['close'].pct_change().rolling(window=20).std() + + # 添加成交量趋势 + dataframe['volume_trend'] = dataframe['volume'].rolling(window=20).mean() for index in range(0, len(dataframe)): if klc_index == len(klc_list): klc_index = len(klc_list) - 1 @@ -1214,105 +1222,9 @@ class ChanLun(): klc.set_end_klu(klu) return klc_list - - def get_bsp_list1(self, big_df): - big_bi_list = self.get_bi_list(big_df) - big_seg_list = self.get_seg_list(big_bi_list) - big_zs_list = self.get_zs_list(big_bi_list, big_seg_list) - big_bi_macd_div_list = self.get_bi_macd_div_list(big_bi_list, big_df) - big_seg_macd_div_list = self.get_seg_macd_div_list(big_seg_list, big_df) - big_bi_macd_hist_list = self.get_bi_macd_hist_list(big_bi_list, big_df) - big_seg_macd_hist_list = self.get_seg_macd_hist_list(big_seg_list, big_df) - for index in range(0, len(big_seg_list)): - big_seg = big_seg_list[index] - if big_seg.end_bi: - if big_seg.dir == Chan_SEG_DIR.UP: - if big_seg.end_bi.index - big_seg.start_bi.index > 1: - max_high = big_seg.start_bi.high - for bi_index in range(big_seg.start_bi.index + 2, big_seg.end_bi.index + 1): - bi = big_bi_list[bi_index] - #print("MACD DIV: ", big_bi_macd_hist_list[index]/big_bi_macd_hist_list[index - 2]) - if bi.is_sure and bi.dir == Chan_BI_DIR.UP: - if bi.high > max_high: - max_high = bi.high - if big_bi_macd_hist_list[bi_index - 2] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 2] - if bi_macd_div < 0.01 and len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - if len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - bi_macd_div = 0.0 - macd_index = bi.end_klc.end_klu.index - if bi_macd_div < 0.8 and bi_macd_div > 0.01 and big_df['macd'][macd_index] > 0 and big_df['macdsignal'][macd_index] > 0: - print("UP SEG Possible BSP:", bi.start_klc.end_time, bi_macd_div) - else: - if big_seg.end_bi.index - big_seg.start_bi.index > 1: - max_low = big_seg.start_bi.low - for bi_index in range(big_seg.start_bi.index + 2, big_seg.end_bi.index + 1): - bi = big_bi_list[bi_index] - if bi.is_sure and bi.dir == Chan_BI_DIR.DOWN: - #print("DOWN: ", max_low, bi.low) - if bi.low < max_low: - max_low = bi.low - if big_bi_macd_hist_list[bi_index - 2] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 2] - if bi_macd_div < 0.01 and len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - if len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - bi_macd_div = 0.0 - macd_index = bi.end_klc.end_klu.index - if bi_macd_div < 0.8 and bi_macd_div > 0.01 and big_df['macd'][macd_index] < 0 and big_df['macdsignal'][macd_index] < 0: - print("DOWN SEG Possible BSP:", bi.start_klc.end_time, bi_macd_div) - else: - print("Not completed segment.", len(big_bi_list) - big_seg.start_bi.index, big_seg.dir) - if big_seg.dir == Chan_SEG_DIR.UP: - if len(big_bi_list) - big_seg.start_bi.index > 1: - max_high = big_seg.start_bi.high - for bi_index in range(big_seg.start_bi.index + 2, len(big_bi_list)): - bi = big_bi_list[bi_index] - #print("MACD DIV: ", big_bi_macd_hist_list[index]/big_bi_macd_hist_list[index - 2]) - if bi.is_sure and bi.dir == Chan_BI_DIR.UP: - if bi.high > max_high: - max_high = bi.high - if big_bi_macd_hist_list[bi_index - 2] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 2] - if bi_macd_div < 0.01 and len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - if len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - bi_macd_div = 0.0 - macd_index = len(big_df) - 1 - if bi_macd_div < 0.8 and bi_macd_div > 0.01 and big_df['macd'][macd_index] > 0 and big_df['macdsignal'][macd_index] > 0: - print("UP SEG Possible BSP:", bi.start_klc.end_time, bi_macd_div) - else: - if len(big_bi_list) - big_seg.start_bi.index > 1: - max_low = big_seg.start_bi.low - for bi_index in range(big_seg.start_bi.index + 2, len(big_bi_list)): - bi = big_bi_list[bi_index] - #print("MACD DIV: ", big_bi_macd_hist_list[index]/big_bi_macd_hist_list[index - 2]) - if bi.is_sure and bi.dir == Chan_BI_DIR.DOWN: - if bi.low < max_low: - max_low = bi.low - if big_bi_macd_hist_list[bi_index - 2] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 2] - if bi_macd_div < 0.01 and len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - if len(big_bi_list) - big_seg.start_bi.index > 4 and big_bi_macd_hist_list[bi_index - 4] > 0.0: - bi_macd_div = big_bi_macd_hist_list[bi_index]/big_bi_macd_hist_list[bi_index - 4] - else: - bi_macd_div = 0.0 - macd_index = len(big_df) - 1 - if bi_macd_div < 0.8 and bi_macd_div > 0.01 and big_df['macd'][macd_index] < 0 and big_df['macdsignal'][macd_index] < 0: - print("DOWN SEG Possible BSP:", bi.start_klc.end_time, bi_macd_div) - + # ================================================ + # 计算BSP列表 + # ================================================ def get_bsp_list(self, big_df): big_bi_list = self.get_bi_list(big_df) big_seg_list = self.get_seg_list(big_bi_list) @@ -1501,6 +1413,8 @@ class ChanLun(): print(bsp.bi.end_klc.end_time, bsp.sure_time, bsp.dir, bsp.seg.dir, bsp.bi.macd_div) return bi_bsp_list + # ================================================ + # 第三类买卖点 def find_third_bsp(self, zs_list): bsp_list = [] zs_count = 0 diff --git a/__pycache__/ChanBI.cpython-312.pyc b/__pycache__/ChanBI.cpython-312.pyc index 9b13e87b356c9d0e25b2e6fe93aeb6a8183abc92..b454148d40b96e45bf67ab8a55b2b8f3f3b519be 100644 GIT binary patch delta 138 zcmdmLvfG6FG%qg~0}yC^YNFDRck; delta 132 zcmdmOvekt9G%qg~0}x1Q@onU`VV1Sm4=qkDD%Q_ROwQJK$xklLP0cGQ)_2b=@hDB& z9L@ZNRkjhRyn*3{ut`AQbe2int;ya9uakB20`7;+eG|W8E;Kyl`;bWMP(1UOpL!K ItFsyc0LNJoiU0rr delta 62 zcmX@lb%TrNG%qg~0}vee$Cj~?=Nyx)gMMgnYEiL%R$_9tzDs^`X>Mv>NwL0rW{F2> R(&l$e`b>rbwrBiG%qg~0}v=Iyp+C?=Q^W^rG98}YEiL%R$_9ten3%vR%&v|CZ?N=+`kz0 YZ!sAd-C|VuwfPCN6({4b$@08<01>MfX8-^I delta 79 zcmX>ibykY!G%qg~0}vee$Cj~?=Q^XTlYVG%YEiL%R$_9tzDs^`X>Mv>NwL0rW{F2> i(&q1sHyOEaG3wu9GBCQusBmlZZDuP@##@ucc=Z4-GaG#X diff --git a/__pycache__/ChanEnum.cpython-312.pyc b/__pycache__/ChanEnum.cpython-312.pyc index 4df116ddf907472eb08851b81e680d81cac18269..0e8de31bdeddbea2654b7c9e6180e118d449cd69 100644 GIT binary patch delta 53 zcmbPhIK`0rG%qg~0}wRua&6@P#Ux^(A6lGRRIHzsn4GO2P?VpQnq0D3iP=en@z>^5 HaSKKOkZupj delta 62 zcmbPYIMZUF=gEJ;6V?DO}Q8kz8rlH|KI=cxfr`xkKT*u5*uMnxS?nBmJVf_c}l|z=Ru-pMVTE`mcUhv*IZbdvMP~$O! zF3S13A~tdHgM06=u$uL1!InpgO?cyn6((){3>*j>7y}Mx_h~r&tS- zO}@zM4(_vkWdHuB*>|%7e-w(XrIlIZTu{ms1~&u6fDZ2zh6!|e-!F`!&x7^icjA=7 z`t5$zWjMo%r;yl87L#RXSd1d6`hr@!=2misBcu>$NxT>@J3`R5@y*n#4H#Zt9h4Gs zs+7`Ui!_e+D=vcxNFFhfPl9Bb2?$Xn)t*agyXuy5hIL3H#7I64;$`a)T;B5I{{N?V z`HGDfVpY2%pIym{546+K(^5uWDlO^Y$BW+-`T<2ukKy2J=^1%y;FFi_ zc&kPm?wD$oJaS*DPX`;@Q%zs&C|Wb!15UQb2t+viDuSEETpcsfSHZIEB@p4Zyi5Ak zoY5Us329IcmV$bBw8BJ%Xq7pe=T~@v?m77BXxT VplzpvDBC9B#ho4!{{tqq@ITOx<01e6 delta 622 zcmZWmO-$2Z0BzSr12{C@#)P$HWhgS-hD@_DU4|C68ne?v90y`_k}wcWC|n@blgUC% zoPN{IOtX%O?U0x@_0XR5h%qW(sxUlwF`8gJNc8BzarWI_^74N2?(drJ?wXq4S}e^- z17B8^=J#fD$Un+W%yRyz%js$V@Qw8R%*6FM&L7XrvGZ5dpXS&;)CK}AC$LSs7hG#u z#E>?h`lF?C2z>$W5OjNfFw4{_dW#x>)PO;q+@ive3U5+pzy>jV9O;bw!Vnwz+lJU& z4Iyz%vO>Zuw`v^hXCT33*<5ldv8QpVaq{9c`c5rpCl8t)wu9Cp%^JTL7u+V_FN<4Zn;G>E7|O73E^oGPVcGB>h8MC<;LhHG?{L)K?=7Td+n7?p(5v0x5~ij%?V1^`3qim+1;` zSWi`W0n0IkIEV|pdb=`!hX42KW}%(U#_A4dVe#H#Zbln>J-d$TV1-g=6oxNb4%m~N4+f?M=dJ(>lA^2#Xo)!_nvSI%O{VOkw} z#-Jv^gC()M!H=kl_{xkcywNqH@G>R@#85RP^AOxzYr~a<;X0=zq@-XK9aV>%fa>zv t^!`pgV%X2?5t+%Si|%soqh28`PF5yihwoFHUyG=HFMG`BXOPsQ{{SmA`da`1 diff --git a/__pycache__/ChanKLU.cpython-312.pyc b/__pycache__/ChanKLU.cpython-312.pyc index a8a37687a36104827d4304c2de9d1ff3820218e5..2cfd46678b134fd78ebcd6b6dd51272308627794 100644 GIT binary patch delta 1282 zcmah}O=uHA6rS1aCY#+RYO<+mn$)84C$=VPQj7LS6pJWQEVdpLt)aVW)5hwg6_jE` z5PDElreX?$5fmvZD54ZkQ4j)xBsD_NOK;*$>Y*TrzL{+OMIG30=6mzK-Ffrg+d1#7 z-}%zvP!J5?OyKAu<%;vF?8gXQLpq{^7T=W@Gplqwih5m4!v<&W${L|lC?s@#U@WB3 zhuBcXWV~tZTPYb|zm{r3r`8q#xWX9;ZeGRs1Mv#9K5YPsz3j)@l)5&&Z#)6NeO78ZCu z-2y8Ff=BU5MBLOO#$?@Ml4eFklMFB-ndAl|R+GGD#AXtk1VlDT3nO-u3`sGUu2je< z)|DFgJaN+dk}rJ>>jK=Leg1U1`dy}0WW#XZl>x5YcGt9+zC1rnp;vL=8qig7U+ouP zZkzXoZ>r$JWGqem5X`^_q|HI!OO3w8K#7MzH3N3bgt>&O=rZey@L#tD!t(T1>>oCg zKN6L+Hl%C8p6Z7HAW;qL9&}H(PwBZ0PY1_4GQrHriN=z)vaq|0XFa+(7b`}$=eHHQ z4ovSlIBsONW&K5exKte}=mtpCa-Tb z_(5S=&Tg z$rCDc_rU9wKO;>48seJRzuaejJN$2FZ`qOWF1BaOsM69b>5qzbul*e>V zaXRXQca)pTn0&dmC{^q#Y=`mDB=&j~0yaoY0EBvj~7Y Fd;_|jNQ3|Y delta 853 zcmZ`%O=uHA6rS1a=6_?_q%psx*4VnH?NX9jY-?MvQ4dlr5H61 zLp;bt+(5wy9yB5}6d}hR3nG}9LWuRGB7%aZhhCl8m<9_z*l*{3-}~A3W*hDuyXh^< z>H#+A5!00)nn$L`5EP$7Xj}#yHT#_?Tqk-51vEvS@l5j}Nf0Ie>U{VbKNAZlqQZ^1 z#GhJT9-Xd zKh%pZOomBp%V>O1;b;2`1mFos!U#maX$v|>duZ&a6emM_P>~LxpLBqbYK5F;ln9OD zBpDBS#sttOCNP5|^bTB7w8nAGrnR_Nf0-n>tZQk@AVU4?a|X1j9zZ9JCW_M|m!^*# zZ5$<%dF%92T!^*@j%j=;(oXt@0T1lsSJj1yY*6;fw{injYkz4DZ$MY`kCu!Te=sY^ zGkJH}Jyf;v8^c&J5Jg|{pIBk!NPeu`Jyf;x8y#3-Yuw-qq2S7n$W~d%xvG{!r31<~ z_w-2ZHrXTW@`J;x{e?@}kQ~Tsa>uJ(+&aXC^#5%zKDBPDjGtWz=Dqnl<=&$o+@ooF zQ|ovix}dz97ui}ba_U%W3$AgxkC1`q0cS=*!+o@6=!0gYGR`?$tJ5wD^YMfzNbU9A zHuCC&WPP8}YvUVqlQp3X>xC{zIph+uFg{n>i$MmV-)$cnmx3Qg!V_G!{u*7be9qEGYH%k%GTe5W*eG L5A6+rLCN?7Xi3{X diff --git a/__pycache__/ChanLun.cpython-312.pyc b/__pycache__/ChanLun.cpython-312.pyc index f2d5fb6aa62904dd86e52d0929a57f825b961c03..4eb781224410d66dd7610ceb942d387dfa1c5660 100644 GIT binary patch delta 9074 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 K-~$CAm--LI)R&R~ delta 13061 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 diff --git a/__pycache__/ChanSBI.cpython-312.pyc b/__pycache__/ChanSBI.cpython-312.pyc index 39238c4f1ffb050951849e99d129db3e54299dea..143d5236730e0878382392f046422aa6324ec6a5 100644 GIT binary patch delta 53 zcmZ1?H(QS9G%qg~0}x33c#^)6hn-o(TtBopwWwG>D=|4+KcFZ-D>b=fvjOuKZpL4e IpYquQ0H(eXSO5S3 delta 62 zcmbO&w?vNTG%qg~0}vee$Cj~?hn-p0K|i!OwWwG>D=|4+-z7h}G&eP`q*&iQv&5q` RX|p=>7H-B{lkf1^0{}_k6jlHL diff --git a/__pycache__/ChanSEG.cpython-312.pyc b/__pycache__/ChanSEG.cpython-312.pyc index 309ff0782713bb4b28e1d3dcbfc97828a88adebc..dac2f10047089de9ff297a3c17e2a5e859e70458 100644 GIT binary patch delta 52 zcmZ1=JzbjnG%qg~0}xEKW8cWVoLR(NKeRZts8~NMF*#d5peR2pHMwN-dFBb6jK3x) H@HzkhfAbJm delta 61 zcmbO(y+E4#G%qg~0}$wB@@(W@&MfPoA6lGRRIHzsn4GQelAm0fo0?ZrtnZ##;!&Ek P`55yAPR3i4!+9M57oZbQ diff --git a/__pycache__/ChanZS.cpython-312.pyc b/__pycache__/ChanZS.cpython-312.pyc index db18ccb2faa8a093d385403355f16a701fe64021..c3d36260054d9b1db64876f3ccd38ebbf403a195 100644 GIT binary patch delta 53 zcmcaBdqtM#G%qg~0}$xU{+_;(=OwdMv>NwL0rW{F2> R(q>MU$y|)LCeP(F2LOj^6(Ils diff --git a/config/ChanLun_SOL.json b/config/ChanLun_SOL.json index 73e9941..adcfe6a 100644 --- a/config/ChanLun_SOL.json +++ b/config/ChanLun_SOL.json @@ -43,7 +43,7 @@ "ccxt_config": {}, "ccxt_async_config": {}, "pair_whitelist": [ - "SOL/USDT:USDT", + "SOL/USDT:USDT" ], "pair_blacklist": [ "BNB/.*" @@ -55,7 +55,7 @@ "number_assets": 1, "sort_key": "quoteVolume", "min_value": 0, - "refresh_period": 1800, + "refresh_period": 1800 } ], "telegram": { diff --git a/config/ChanLun_SOL_Optimized.json b/config/ChanLun_SOL_Optimized.json new file mode 100644 index 0000000..76a63d1 --- /dev/null +++ b/config/ChanLun_SOL_Optimized.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://schema.freqtrade.io/schema.json", + "max_open_trades": 2, + "stake_currency": "USDT", + "stake_amount": "unlimited", + "tradable_balance_ratio": 0.95, + "fiat_display_currency": "USD", + "dry_run": true, + "db_url": "sqlite:///tradesv3.chanlun_sol_optimized.sqlite", + "dry_run_wallet": 1000, + "cancel_open_orders_on_exit": true, + "trading_mode": "futures", + "margin_mode": "isolated", + "can_short": true, + "timeframe": "5m", + "process_only_new_candles": false, + "unfilledtimeout": { + "entry": 2, + "exit": 2, + "exit_timeout_count": 0, + "unit": "minutes" + }, + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "exit_pricing":{ + "price_side": "other", + "use_order_book": true, + "order_book_top": 1 + }, + "exchange": { + "name": "binance", + "key": "", + "secret": "", + "ccxt_config": { + "options": {"defaultType": "swap"} + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 1000, + "timeout": 30000 + }, + "pair_whitelist": [ + "SOL/USDT:USDT" + ], + "pair_blacklist": [] + }, + "telegram": { + "enabled": false, + "token": "", + "chat_id": "" + }, + "api_server": { + "enabled": false, + "listen_ip_address": "0.0.0.0", + "listen_port": 8080, + "verbosity": "error", + "jwt_secret_key": "", + "username": "", + "password": "" + }, + "discord": { + "enabled": false, + "webhook": "", + "webhook_avatar": "", + "poll_delay_seconds": 10 + }, + "notification_settings": { + "status": "on", + "status_inactive_after": 7, + "timeframe_condition_change": "on", + "telegram": { }, + "discord": { }, + "notify_all": true + }, + "bot_name": "SOL_Chan_Optimized", + "initial_state": "running", + "force_entry_enable": false, + "internals": { + "process_throttle_secs": 5 + }, + "edge": { + "enabled": false, + "process_throttle_secs": 3600, + "calculate_since_number_of_days": 7, + "allowed_risk": 0.01, + "stoploss_range_min": -0.01, + "stoploss_range_max": -0.007, + "stoploss_range_step": 0.001, + "minimum_winrate": 0.60, + "minimum_expectancy": 0.20, + "min_trade_number": 10, + "max_trade_duration_minute": 1440, + "remove_pumps": false + }, + "order_types": { + "entry": "limit", + "exit": "market", + "emergency_exit": "market", + "force_exit": "market", + "force_entry": "market", + "stoploss": "market", + "stoploss_on_exchange": false, + "stoploss_on_exchange_interval": 60 + }, + "order_time_in_force": { + "entry": "GTC", + "exit": "GTC" + }, + "strategy_path": "./user_data/Chan/strategies/", + "strategy": "ChanLun_SOL_Optimized", + "minimal_roi": { + "0": 0.012, + "120": 0.010, + "240": 0.007, + "360": 0.005 + }, + "stoploss": -0.007, + "trailing_stop": true, + "trailing_stop_positive": 0.003, + "trailing_stop_positive_offset": 0.005, + "trailing_only_offset_is_reached": true, + "use_custom_stoploss": true, + "max_open_trades_per_pair": 1, + "dry_run_wallet_refresh_time": 5, + "caches": { + "dataframe": { + "enabled": true, + "refresh_period": 60 + }, + "strategy": { + "enabled": true, + "refresh_period": 300 + } + }, + "pairlists": [ + { + "method": "StaticPairList", + "config": { + "pairs": ["SOL/USDT:USDT"] + } + } + ] +} \ No newline at end of file diff --git a/strategies/ChanLun_SOL_5.py b/strategies/ChanLun_SOL_5.py index f65bbe1..eca485d 100644 --- a/strategies/ChanLun_SOL_5.py +++ b/strategies/ChanLun_SOL_5.py @@ -42,17 +42,17 @@ class ChanLun_SOL_5(IStrategy): } # 5m and 15m minimal_roi_1 = { - "0": 0.253, - "60": 0.159, - "120": 0.052, + "0": 0.1, + "60": 0.05, + "120": 0.02, "240": 0 } # 15m and 30m minimal_roi_1 = { - "0": 0.253, - "120": 0.159, - "240": 0.052, - "360": 0 + "0": 0.1, + "240": 0.05, + "480": 0.03, + "600": 0 } minimal_roi_2 = { "0": 0.10, @@ -60,8 +60,8 @@ class ChanLun_SOL_5(IStrategy): "2400": 0.025, "3600": 0 } - can_short = False - lev = 5.0 + can_short = True + lev = 1.0 stoploss = -0.3 * lev trailing_stop = False trailing_stop_positive = 0.025 @@ -471,12 +471,16 @@ class ChanLun_SOL_5(IStrategy): #print(df['date'][index], df['macdhist'][index], df['macd'][index], df['macdsignal'][index], df['masub'][index]) # (1,1) = 1, (1,0) = 2, (-1,1) = 3, (-1, 0) = 4, (0,0) = 0 def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - + state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5) + close_str = 'resample_{}_close'.format(self.get_ticker_indicator()*self.time5) + volume_str = 'resample_{}_volume'.format(self.get_ticker_indicator()*self.time5) dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") | - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)] == "99") + (dataframe[state_str].shift(self.time5) == "-10") & + (dataframe[close_str].pct_change().abs() < 0.05) & + (dataframe[close_str] > dataframe[close_str].shift(self.time5)) & + (dataframe[volume_str] > dataframe[volume_str].rolling(window=20).mean()) #(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") @@ -486,7 +490,10 @@ class ChanLun_SOL_5(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "30") - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "10") + (dataframe[state_str].shift(self.time5) == "10") & + (dataframe[close_str].pct_change().abs() < 0.05) & + (dataframe[close_str] < dataframe[close_str].shift(self.time5)) & + (dataframe[volume_str] > dataframe[volume_str].rolling(window=20).mean()) #(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") @@ -495,11 +502,14 @@ class ChanLun_SOL_5(IStrategy): ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5) + close_str = 'resample_{}_close'.format(self.get_ticker_indicator()*self.time5) dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "10") | - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)] == "-99") + (dataframe[state_str].shift(self.time5) == "10") | + (dataframe[close_str] < dataframe[close_str].rolling(window=20).min()) | + (dataframe[close_str] > dataframe[close_str].rolling(window=20).max()*1.05) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), @@ -507,7 +517,9 @@ class ChanLun_SOL_5(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") + (dataframe[state_str].shift(self.time5) == "-10") | + (dataframe[close_str] > dataframe[close_str].rolling(window=20).max()) | + (dataframe[close_str] < dataframe[close_str].rolling(window=20).min()*0.95) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "-10") ), diff --git a/strategies/ChanLun_SOL_Optimized.py b/strategies/ChanLun_SOL_Optimized.py new file mode 100644 index 0000000..b347656 --- /dev/null +++ b/strategies/ChanLun_SOL_Optimized.py @@ -0,0 +1,386 @@ +""" +SOL/USDT 优化交易策略 + +采用多时间周期分析和缠论技术分析,专注于空头交易 +集成了技术指标确认和风险管理功能 +""" + +# --- Do not remove these libs --- +from statistics import median +from freqtrade.strategy import IStrategy +import sys +import os +# 添加父目录到系统路径 +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from ChanLun import ChanLun +from ChanLun_Classifier import ChanLunClassifier +from ChanEnum import Chan_FX_TYPE, Chan_KLC_FX, Chan_BI_DIR, Chan_KLC_FX +# -------------------------------- +from technical.util import resample_to_interval, resampled_merge +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from pandas import DataFrame +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from typing import Optional +import logging +import numpy as np +logger = logging.getLogger(__name__) + +# freqtrade trade -c ./user_data/Chan/config/ChanLun_SOL_Optimized.json --strategy ChanLun_SOL_Optimized --strategy-path ./user_data/Chan/strategies +# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_SOL_Optimized.json --strategy ChanLun_SOL_Optimized --strategy-path ./user_data/Chan/strategies --timerange=20250201- + +class ChanLun_SOL_Optimized(IStrategy): + """ + SOL/USDT 优化交易策略 - 专注于空头交易 + """ + INTERFACE_VERSION: int = 3 + + # 优化后的ROI设置,主要针对短期交易 + minimal_roi = { + "0": 0.012, + "120": 0.010, + "240": 0.007, + "360": 0.005 + } + + # 支持做空 + can_short = True + only_short = True + + # 杠杆设置(谨慎使用) + lev = 1.0 + + # 止损设置 + stoploss = -0.007 * lev + + # 追踪止损设置 + trailing_stop = True + trailing_stop_positive = 0.003 + trailing_stop_positive_offset = 0.005 + trailing_only_offset_is_reached = True + + # 仓位管理设置 + position_adjustment_enable = True + max_entry_position_adjustment = 3 + max_dca_multiplier = 4.0 + + # 策略初始化需要的K线数量 + startup_candle_count = 200 + + # 时间周期定义 + timeframe = '5m' + + # 时间周期乘数 + time5 = 5 + time15 = 15 + time30 = 30 + time60 = 60 + time4h = 240 + time1d = 1440 + + # 缠论模块初始化 + chan = ChanLun() + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + 添加技术指标 + """ + # 基础技术指标 + for df in [dataframe]: + # 添加MACD指标 + macd = ta.MACD(df) + df['macd'] = macd['macd'] + df['macdsignal'] = macd['macdsignal'] + df['macdhist'] = macd['macdhist'] + + # 添加移动平均线 + df['ma5'] = ta.MA(df, timeperiod=5) + df['ma10'] = ta.MA(df, timeperiod=10) + df['ma20'] = ta.MA(df, timeperiod=20) + df['ma30'] = ta.EMA(df, timeperiod=30) + df['ma50'] = ta.MA(df, timeperiod=50) + df['ma200'] = ta.MA(df, timeperiod=200) + + # 添加RSI指标 + df['rsi'] = ta.RSI(df, timeperiod=14) + df['rsi_slow'] = ta.RSI(df, timeperiod=21) + + # 添加ATR(波动率) + df['atr'] = ta.ATR(df, timeperiod=14) + + # 计算布林带 + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(df), window=20, stds=2) + df['bb_lowerband'] = bollinger['lower'] + df['bb_middleband'] = bollinger['mid'] + df['bb_upperband'] = bollinger['upper'] + df['bb_width'] = (df['bb_upperband'] - df['bb_lowerband']) / df['bb_middleband'] + + # 添加ADX指标(趋势强度) + df['adx'] = ta.ADX(df, timeperiod=14) + df['plus_di'] = ta.PLUS_DI(df, timeperiod=14) + df['minus_di'] = ta.MINUS_DI(df, timeperiod=14) + + # 添加量比指标 + df['volume_ma20'] = df['volume'].rolling(window=20).mean() + df['volume_ratio'] = df['volume'] / df['volume_ma20'] + + # 计算下降趋势确认指标 + dataframe['downtrend'] = ( + (dataframe['ma5'] < dataframe['ma10']) & + (dataframe['ma10'] < dataframe['ma30']) & + (dataframe['close'] < dataframe['ma10']) & + (dataframe['close'].shift(1) > dataframe['close']) + ) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + 入场信号逻辑 - 放宽条件以产生更多交易信号 + """ + # 关闭多头交易 + dataframe['enter_long'] = 0 + + # 空头入场条件 - 条件1:价格下跌趋势 + dataframe.loc[ + ( + # 价格下跌趋势 - 放宽为仅需一根K线下跌 + (dataframe['close'] < dataframe['close'].shift(1)) & + + # 价格在均线下方 - 使用更短期均线 + (dataframe['close'] < dataframe['ma20']) & + + # RSI条件放宽 - 只要不是极度超卖 + (dataframe['rsi'] > 30) & + + # 成交量条件放宽 + (dataframe['volume'] > dataframe['volume'].rolling(window=10).mean()) & + + # MACD空头 + (dataframe['macd'] < dataframe['macdsignal']) + ), + ['enter_short', 'enter_tag']] = (1, 'short_trend_simple') + + # 空头入场条件 - 条件2:突破下降 + dataframe.loc[ + ( + # 价格突破支撑位 + (dataframe['close'] < dataframe['low'].shift(1).rolling(window=5).min()) & + + # 下降动量增强 + (dataframe['close'].pct_change() < -0.005) & + + # 非超卖区 + (dataframe['rsi'] > 35) & + + # 确保不与第一个条件重复 + (~dataframe['enter_short'].astype(bool)) + ), + ['enter_short', 'enter_tag']] = (1, 'short_breakdown') + + # 空头入场条件 - 条件3:均线死叉 + dataframe.loc[ + ( + # 短期均线下穿长期均线 + (qtpylib.crossed_below(dataframe['ma5'], dataframe['ma10'])) & + + # 价格已经在中期均线下方 + (dataframe['close'] < dataframe['ma20']) & + + # 确保不与其他条件重复 + (~dataframe['enter_short'].astype(bool)) + ), + ['enter_short', 'enter_tag']] = (1, 'short_ma_cross') + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + 出场信号逻辑 - 优化盈利能力和降低风险 + """ + # 清除之前的出场条件 + dataframe['exit_short'] = 0 + dataframe['exit_long'] = 0 + + # 空头出场条件 - 价格反转 + price_reversal = ( + # 价格反转 + (dataframe['close'] > dataframe['close'].shift(1)) & + (dataframe['close'] > dataframe['open']) & # 收阳 + (dataframe['volume'] > dataframe['volume'].rolling(window=10).mean()) # 放量上涨 + ) + + # 空头出场条件 - 超卖反弹 + oversold_bounce = ( + # RSI超卖 + (dataframe['rsi'] < 30) & + (dataframe['rsi'] > dataframe['rsi'].shift(1)) # RSI回升 + ) + + # 空头出场条件 - 盈利保护 + profit_protection = ( + # 突破下轨后快速回升 + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['close'] > dataframe['close'].shift(1)) & + (dataframe['close'].shift(1) > dataframe['close'].shift(2)) # 连续上涨 + ) + + # 空头出场条件 - 趋势转变 + trend_change = ( + # 价格突破短期均线 + (qtpylib.crossed_above(dataframe['close'], dataframe['ma10'])) | + + # MACD柱状图由负转正 + (dataframe['macdhist'] > 0) & + (dataframe['macdhist'].shift(1) < 0) + ) + + # 组合所有出场条件 + dataframe.loc[price_reversal, ['exit_short', 'exit_tag']] = (1, 'price_reversal') + dataframe.loc[oversold_bounce, ['exit_short', 'exit_tag']] = (1, 'oversold_bounce') + dataframe.loc[profit_protection, ['exit_short', 'exit_tag']] = (1, 'profit_protection') + dataframe.loc[trend_change, ['exit_short', 'exit_tag']] = (1, 'trend_change') + + return dataframe + + def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + """ + 自定义止损逻辑 - 更精细的动态止损 + """ + # 获取当前的dataframe + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + + if len(dataframe) == 0: + return self.stoploss + + # 获取最新的K线数据 + last_candle = dataframe.iloc[-1].squeeze() + + # 计算ATR止损 + atr_value = last_candle['atr'] + + # 根据盈利情况动态调整止损策略 + if current_profit >= 0.03: + # 盈利较高,保护大部分利润,使用较紧的止损 + return current_profit * 0.6 + + elif current_profit >= 0.015: + # 中等盈利,保护部分利润 + return current_profit * 0.4 + + elif current_profit >= 0.008: + # 小额盈利,保本为主 + return current_profit * 0.15 + + elif current_profit > 0: + # 微小盈利,保本为主 + return 0 + + else: + # 亏损情况下,判断是否需要立即止损 + + # 趋势强烈反转,尽快止损 + if (last_candle['close'] > last_candle['ma5']) and (last_candle['macd'] > last_candle['macdsignal']): + # 趋势向上反转,立即减小止损 + return current_profit * 0.5 + + # 下跌动量减弱,略微放宽止损 + if last_candle['rsi'] < 20 and last_candle['rsi'] > last_candle['rsi_slow']: + # RSI超卖且反弹迹象,提供更多空间 + return self.stoploss * 1.3 + + # 默认返回原始止损设置 + return self.stoploss + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float | None, max_stake: float, + leverage: float, entry_tag: str | None, side: str, + **kwargs) -> float: + """ + 自定义仓位大小计算 + """ + # 为DCA预留资金空间 + return proposed_stake / self.max_dca_multiplier + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, + min_stake: float | None, max_stake: float, + current_entry_rate: float, current_exit_rate: float, + current_entry_profit: float, current_exit_profit: float, + **kwargs) -> float | None | tuple[float | None, str | None]: + """ + 动态调整仓位 - 优化加仓策略 + """ + # 获取交易数据 + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + + if len(dataframe) == 0: + return None + + filled_entries = trade.select_filled_orders(trade.entry_side) + + if not filled_entries: + return None + + last_entry = filled_entries[-1] + count_of_entries = trade.nr_of_successful_entries + + # 空头加仓逻辑 + if last_entry.side == "sell": + # 获取最新K线 + last_candle = dataframe.iloc[-1] + prev_candle = dataframe.iloc[-2] if len(dataframe) > 1 else last_candle + + # 计算加仓金额 - 基于亏损程度动态调整 + stake_amount = filled_entries[0].stake_amount + + # 条件1:价格突破新低 + 高阶空头趋势 + if (current_profit < -0.005 and + last_candle['close'] < prev_candle['low'] and + last_candle['macd'] < last_candle['macdsignal'] and + count_of_entries < 2): + + # 根据亏损程度调整加仓量 - 亏损越多加仓越少 + adjustment_factor = max(0.5, 1.0 + current_profit) # 限制最低为0.5 + new_stake = stake_amount * adjustment_factor + + return new_stake, "short_dca_new_low" + + # 条件2:小幅反弹后继续下跌 + if (current_profit < -0.003 and + last_candle['close'] < last_candle['open'] and # 阴线 + last_candle['close'] < last_candle['ma20'] and # 价格在中期均线下方 + prev_candle['close'] > prev_candle['open'] and # 前一根是阳线 + count_of_entries < 3): + + # 使用标准金额加仓 + return stake_amount * 0.8, "short_dca_dip_continuation" + + return None + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, + time_in_force: str, current_time: datetime, entry_tag: Optional[str], + side: str, **kwargs) -> bool: + """ + 在进入交易前进行额外的确认 + """ + # 始终允许空头交易,不做额外检查 + if side == "sell": + return True + return False + + def leverage(self, pair: str, current_time: datetime, current_rate: float, + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: + """ + 返回杠杆率 + """ + return self.lev + + def get_ticker_indicator(self): + """ + 获取当前时间框架的分钟数 + """ + return int(self.timeframe[:-1]) \ No newline at end of file diff --git a/strategies/Chan_SOL_2.py b/strategies/Chan_SOL_2.py index ce03d64..67d161f 100644 --- a/strategies/Chan_SOL_2.py +++ b/strategies/Chan_SOL_2.py @@ -5,13 +5,6 @@ from functools import reduce from pandas import DataFrame, pandas import freqtrade.vendor.qtpylib.indicators as qtpylib -import sys -import os -#sys.setrecursionlimit(1000000) #例如这里设置为一百万 -#sys.path.append(os.path.abspath("/freqtrade/user_data/Chan")) -sys.path.append(os.path.abspath("/Users/jack/Project/freqtrade/user_data/Chan")) -#sys.path.append(os.path.abspath("/Users/jack/Documents/GitHub/freqtrade/user_data/Chan")) -from ChanLun import ChanLun # -------------------------------- from technical.util import resample_to_interval, resampled_merge import talib.abstract as ta @@ -19,174 +12,325 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from datetime import datetime, timedelta, timezone from freqtrade.persistence import Trade, Order from typing import Optional -from ChanPY import ChanPY + import logging logger = logging.getLogger(__name__) ### Now you can use logger.info('asfd') to log -# freqtrade trade -c ./user_data/Chan.json --strategy Chan_SOL_2 --strategy-path ./user_data/strategies -# freqtrade backtesting -c ./user_data/Chan.json --strategy Chan_SOL_2 --strategy-path ./user_data/strategies --timerange=20250309- -# freqtrade download-data -c ./user_data/Chan.json -t 1m --pairs SOL/USDT:USDT --timerange=20240101- -# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy Chan_SOL_2 --strategy-path ./user_data/strategies -c ./user_data/Chan.json -e 200 --timerange=20250101-20250215 +# freqtrade trade -c ./user_data/Chan/config/ChanLun_SOL.json --strategy ChanLun_SOL_2 --strategy-path ./user_data/Chan/strategies +# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_SOL.json --strategy ChanLun_SOL_2 --strategy-path ./user_data/Chan/strategies --timerange=20250309- +# freqtrade download-data -c ./user_data/Chan/config/ChanLun_SOL.json -t 1m --pairs SOL/USDT:USDT --timerange=20250501- +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_SOL_2 --strategy-path ./user_data/strategies -c ./user_data/ChanLun_SOL.json -e 200 --timerange=20250101-20250215 # sudo docker compose run --rm chan_btc backtesting -c ./user_data/Chan.json --strategy Chan_SOL_2 --strategy-path ./user_data/strategies --timerange=20250101- # sudo docker compose run --rm chan_btc download-data -c ./user_data/Chan.json --pairs SOL/USDT:USDT -t 1m --timerange 20240101- # sudo docker compose run --rm chan_btc trade -c ./user_data/Chan.json --strategy Chan_SOL_2 --strategy-path ./user_data/strategies -class Chan_SOL_2(IStrategy): +class ChanLun_SOL_2(IStrategy): INTERFACE_VERSION: int = 3 - # Minimal ROI designed for the strategy. - # This attribute will be overridden if the config file contains "minimal_roi" + + # 优化的ROI设置 - 更快速获利 minimal_roi = { - "0": 0.253, - "120": 0.159, - "240": 0.052, - "360": 0 + "0": 0.012, # 立即获利1.2% + "5": 0.01, # 5分钟后获利1% + "15": 0.007, # 15分钟后获利0.7% + "30": 0.005 # 30分钟后获利0.5% } + can_short = True - # Optimal stoploss designed for the strategy - # This attribute will be overridden if the config file contains "stoploss" - stoploss = -0.21 - - trailing_stop = False - trailing_stop_positive = 0.015 - trailing_stop_positive_offset = 0.043 - trailing_only_offset_is_reached = False - - # Optimal timeframe for the strategy - # timeframe = '15m' - startup_candle_count = 600 - - time5 = 5 - time15 = 15 - time30 = 30 - time60 = 60 - time240 = 240 - last_time = datetime.now() - big_size = 0 - big_state = "00" - big_state_list = [] - chanpy = ChanPY() - chan = ChanLun() - small_size = 0 - small_state = "00" - small_state_list = [] - + stoploss = -0.007 # 降低止损为0.7% + + # 追踪止损设置 - 更积极的追踪止损 + trailing_stop = True + trailing_stop_positive = 0.003 # 0.3% + trailing_stop_positive_offset = 0.005 # 0.5% + trailing_only_offset_is_reached = True + + # 时间周期 + timeframe = '5m' + informative_timeframe = '1h' + startup_candle_count = 200 + + # 只做空头策略 + only_short = True + def informative_pairs(self): - - # get access to all pairs available in whitelist. pairs = self.dp.current_whitelist() - # Assign tf to each pair so they can be downloaded and cached for strategy. - informative_pairs = [(pair, '1h') for pair in pairs] - # Optionally Add additional "static" pairs - #informative_pairs += [("ETH/USDT:USDT", "5m"),("ETH/USDT:USDT", "15m"),] + informative_pairs = [(pair, self.informative_timeframe) for pair in pairs] return informative_pairs + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # 获取更高时间周期的数据 + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.informative_timeframe) - # resample our dataframes - dataframe_5 = resample_to_interval(dataframe, self.get_ticker_indicator() * 5) - #dataframe_15 = resample_to_interval(dataframe, self.get_ticker_indicator() * 15) - #dataframe_30 = resample_to_interval(dataframe, self.get_ticker_indicator() * 30) - dataframe_60 = resample_to_interval(dataframe, self.get_ticker_indicator() * 60) - #dataframe_4h = resample_to_interval(dataframe, self.get_ticker_indicator() * 240) - - #dataframe_1d = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') - #dataframe_1w = resample_to_interval(dataframe_1d, self.get_ticker_indicator() * 10080) - #dataframe_1m = resample_to_interval(dataframe_1d, self.get_ticker_indicator() * 43200) - - #dataframe_1d = resample_to_interval(dataframe, self.get_ticker_indicator() * 1440) - #dataframe_1w = resample_to_interval(dataframe, self.get_ticker_indicator() * 10080) - #dataframe_1m = resample_to_interval(dataframe, self.get_ticker_indicator() * 43200) - self.local_print(dataframe_5) - - dataframe_5['state'] = self.chan.resample_klc_list(dataframe_5) - #dataframe_15['state'] = self.chan.resample_klc_list(dataframe_15) - #dataframe_30['state'] = self.chan.resample_klc_list(dataframe_30) - dataframe_60['state'] = self.chan.resample_klc_list(dataframe_60) - #dataframe_4h['state'] = self.chan.resample_klc_list(dataframe_4h) - #dataframe_5['bsps'], dataframe_5['updown'], dataframe_5['bi_sure'] = self.chanpy.get_bsps(dataframe_5) - print("===================================================") - #print(dataframe_60['high'].rolling(window).max()) - #print(dataframe_60['low'].rolling(window).min()) - #for index in range(0, len(dataframe_5)): - #print(dataframe_5[dataframe_5['bi_sure'] == 1][dataframe_5['state'] == "-10"]) - #print(dataframe_5[dataframe_5['bi_sure'] == 1][dataframe_5['state'] == "10"]) - dataframe = resampled_merge(dataframe, dataframe_5) - #dataframe = resampled_merge(dataframe, dataframe_15) - #dataframe = resampled_merge(dataframe, dataframe_30) - dataframe = resampled_merge(dataframe, dataframe_60) - #dataframe = resampled_merge(dataframe, dataframe_4h) + # === 高时间周期指标 === + # 三均线系统 + informative['ema50'] = ta.EMA(informative, timeperiod=50) + informative['ema100'] = ta.EMA(informative, timeperiod=100) + informative['ema200'] = ta.SMA(informative, timeperiod=200) # 使用SMA作为长期趋势 + + # 趋势方向 + informative['uptrend'] = ( + (informative['ema50'] > informative['ema100']) & + (informative['ema100'] > informative['ema200']) & + (informative['close'] > informative['ema50']) + ).astype(int) + + informative['downtrend'] = ( + (informative['ema50'] < informative['ema100']) & + (informative['ema100'] < informative['ema200']) & + (informative['close'] < informative['ema50']) + ).astype(int) + + # 强下降趋势 + informative['strong_downtrend'] = ( + (informative['ema50'] < informative['ema100']) & + (informative['ema100'] < informative['ema200']) & + (informative['close'] < informative['ema50']) & + (informative['ema50'].shift(3) < informative['ema50']) # 确认EMA50下降 + ).astype(int) + + # 添加高时间周期的ADX指标 + informative['adx'] = ta.ADX(informative, timeperiod=14) + + # 添加高时间周期的波动率 + informative['atr'] = ta.ATR(informative, timeperiod=14) + informative['atr_percent'] = (informative['atr'] / informative['close']) * 100 + + # 高时间周期RSI + informative['rsi'] = ta.RSI(informative, timeperiod=14) + + # 将informative数据帧中的列重命名,以便在合并后区分 + for col in informative.columns: + if col not in ['date', 'open', 'high', 'low', 'close', 'volume']: + informative[f"{col}_{self.informative_timeframe}"] = informative[col] + + # 删除原始列,只保留重命名后的列和必要的日期、OHLCV列 + for col in list(informative.columns): + if col not in ['date', 'open', 'high', 'low', 'close', 'volume'] and not col.endswith(f"_{self.informative_timeframe}"): + del informative[col] + + # 打印列名以便调试 + logger.info(f"Informative columns after renaming: {informative.columns.tolist()}") + + # 合并数据 - 使用正确的参数 + dataframe = resampled_merge(dataframe, informative, self.informative_timeframe) + + # 打印合并后的列名以便调试 + logger.info(f"Dataframe columns after merge: {dataframe.columns.tolist()}") + + # === 主时间周期指标 === + # 布林带 + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_middleband'] = bollinger['mid'] + dataframe['bb_upperband'] = bollinger['upper'] + dataframe['bb_width'] = ((bollinger['upper'] - bollinger['lower']) / bollinger['mid']) + + # 动量指标 + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['mfi'] = ta.MFI(dataframe, timeperiod=14) + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # 均线 + dataframe['ema9'] = ta.EMA(dataframe, timeperiod=9) + dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + + # 成交量 + dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean() + dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_mean'] + + # 波动率 + dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) + + # ADX - 趋势强度指标 + dataframe['adx'] = ta.ADX(dataframe, timeperiod=14) + + # 价格突破 + dataframe['upper_break'] = ( + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'].shift() <= dataframe['bb_upperband'].shift()) + ).astype(int) + + dataframe['lower_break'] = ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['close'].shift() >= dataframe['bb_lowerband'].shift()) + ).astype(int) + + # 均线交叉 + dataframe['ema_cross_up'] = ( + (dataframe['ema9'] > dataframe['ema21']) & + (dataframe['ema9'].shift() <= dataframe['ema21'].shift()) + ).astype(int) + + dataframe['ema_cross_down'] = ( + (dataframe['ema9'] < dataframe['ema21']) & + (dataframe['ema9'].shift() >= dataframe['ema21'].shift()) + ).astype(int) + + # 超买超卖区域 + dataframe['rsi_oversold'] = (dataframe['rsi'] < 30).astype(int) + dataframe['rsi_overbought'] = (dataframe['rsi'] > 70).astype(int) + + # 价格与均线的关系 + dataframe['price_above_ema50'] = (dataframe['close'] > dataframe['ema50']).astype(int) + dataframe['price_below_ema50'] = (dataframe['close'] < dataframe['ema50']).astype(int) + + # 趋势强度 + dataframe['strong_trend'] = (dataframe['adx'] > 25).astype(int) + + # 添加蜡烛图形态识别 + dataframe['doji'] = ta.CDLDOJI(dataframe['open'], dataframe['high'], dataframe['low'], dataframe['close']) + dataframe['engulfing'] = ta.CDLENGULFING(dataframe['open'], dataframe['high'], dataframe['low'], dataframe['close']) + dataframe['hammer'] = ta.CDLHAMMER(dataframe['open'], dataframe['high'], dataframe['low'], dataframe['close']) + dataframe['shooting_star'] = ta.CDLSHOOTINGSTAR(dataframe['open'], dataframe['high'], dataframe['low'], dataframe['close']) + + # 价格动量 + dataframe['momentum'] = dataframe['close'] - dataframe['close'].shift(5) return dataframe - def print_df(self, df): - for index in range(0, len(df)): - print(df['date'][index], df['rsi'][index], df['state'][index]) - def print_resample_df(self, df, time): - for index in range(0, len(df)): - cn1 = 'resample_{}_date'.format(self.get_ticker_indicator()*time) - cn2 = 'resample_{}_rsi'.format(self.get_ticker_indicator()*time) - cn3 = 'resample_{}_state'.format(self.get_ticker_indicator()*time) - print(df[cn1][index], df[cn2][index], df[cn3][index]) - def local_print(self, df): - fast = 7 - slow = 14 - macd = ta.MACD(df, fast=fast, slow=slow) - df['macd'] = macd['macd'] - df['macdsignal'] = macd['macdsignal'] - df['macdhist'] = macd['macdhist'] - df['ema26'] = ta.EMA(df, timeperiod=26) - df['ema52'] = ta.EMA(df, timeperiod=52) - df['ma5'] = ta.MA(df, timeperiod=5) - df['ma10'] = ta.MA(df, timeperiod=10) - df['masub'] = df['ma5'].subtract(df['ma10']) - for index in range(0, len(df)): - print(df['date'][index], df['macdhist'][index], df['macd'][index], df['macdsignal'][index], df['masub'][index]) - # (1,1) = 1, (1,0) = 2, (-1,1) = 3, (-1, 0) = 4, (0,0) = 0 + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # 检查列名是否存在 + downtrend_col = 'resample_60_downtrend_1h' + strong_downtrend_col = 'resample_60_strong_downtrend_1h' + adx_col = 'resample_60_adx_1h' + rsi_col = 'resample_60_rsi_1h' - dataframe.loc[ + # 如果列名不存在,使用替代方案 + for col, default_value in [ + (downtrend_col, 0), + (strong_downtrend_col, 0), + (adx_col, 25), + (rsi_col, 50) + ]: + if col not in dataframe.columns: + logger.warning(f"Column {col} not found in dataframe. Creating with default value {default_value}.") + dataframe[col] = default_value + + # 禁用多头入场 + dataframe['enter_long'] = 0 + + # 空头入场条件 - 专注于空头策略 + short_conditions = ( + # 高时间周期处于下降趋势 + (dataframe[downtrend_col] > 0) & + + # 趋势强度确认 + (dataframe[adx_col] > 25) & + + # 条件1: 价格突破上轨后回落 + 成交量确认 ( - #(dataframe['state'].shift(1) == "-10") & - #((dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)] == "-11") | - (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.time60)].shift(self.time60) == "11") & - (dataframe['resample_{}_bsps'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) > 0) & - (dataframe['resample_{}_bsps'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) < 10) - #(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])) - ), - ['enter_long', 'enter_tag']] = (1, 'long_signal_chan') - dataframe.loc[ + (dataframe['upper_break'].rolling(window=5).sum() > 0) & # 最近5根K线内有突破上轨 + (dataframe['close'] < dataframe['close'].shift(2)) & # 价格开始下跌 + (dataframe['close'] < dataframe['ema9']) & # 价格在短期均线下方 + (dataframe['volume_ratio'] > 1.3) & # 成交量放大 + (dataframe['rsi'] < 70) & # RSI不在极度超买区 + (dataframe['rsi'] > 40) & # RSI不在超卖区 + (dataframe[rsi_col] < 60) # 高时间周期RSI不过高 + ) | + + # 条件2: 均线死叉 + RSI超买回落 + 趋势确认 ( - #(dataframe['state'].shift(1) == "10") & - #((dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)] == "11") | - (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.time60)].shift(self.time60) == "-11") & - (dataframe['resample_{}_bsps'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) < 0) - #(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])) - ), - ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') + (dataframe['ema_cross_down'] > 0) & # 均线死叉 + (dataframe['rsi'] > 55) & # RSI相对较高 + (dataframe['rsi'] < dataframe['rsi'].shift(3)) & # RSI下降 + (dataframe['volume_ratio'] > 1.2) & # 成交量放大 + (dataframe['adx'] > 20) & # ADX显示有一定趋势强度 + ((dataframe['shooting_star'] > 0) | (dataframe['engulfing'] < 0)) # 流星线或看跌吞没形态 + ) | + + # 条件3: 价格在高点回落 + 强趋势 + ( + (dataframe['close'] < dataframe['high'].shift()) & + (dataframe['high'].shift() > dataframe['high'].shift(2)) & + (dataframe['close'] < dataframe['ema21']) & + (dataframe['adx'] > 30) & + (dataframe['rsi'] < dataframe['rsi'].shift()) & + (dataframe['rsi'].shift() > 65) & + (dataframe['volume_ratio'] > 1.0) + ) | + + # 条件4: 强下降趋势确认 + ( + (dataframe[strong_downtrend_col] > 0) & + (dataframe['close'] < dataframe['ema21']) & + (dataframe['close'] < dataframe['close'].shift(3)) & + (dataframe['momentum'] < 0) & + (dataframe['volume_ratio'] > 1.1) & + (dataframe['adx'] > 25) + ) + ) + + dataframe.loc[short_conditions, 'enter_short'] = 1 + dataframe.loc[short_conditions, 'enter_tag'] = 'chan_sol_short' + return dataframe + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - dataframe.loc[ + # 禁用多头出场 + dataframe['exit_long'] = 0 + + # 空头出场条件 - 更精确的出场 + short_exit_conditions = ( + # 条件1: 趋势反转信号 ( - #(dataframe['state'].shift(1) == "10") & - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)].shift(self.time60) == "10") - #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & - #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") - ), - ['exit_long', 'exit_tag']] = (1, 'long_close_signal_chan') - dataframe.loc[ + (dataframe['ema_cross_up'] > 0) & # 均线金叉 + (dataframe['volume_ratio'] > 1.0) # 成交量确认 + ) | + + # 条件2: 价格突破中期均线 ( - #(dataframe['state'].shift(1) == "-10") & - (dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)].shift(self.time60) == "-10") - #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") & - #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "-10") - ), - ['exit_short', 'exit_tag']] = (1, 'short_close_signal_chan') + (dataframe['close'] > dataframe['ema21']) & + (dataframe['close'].shift() < dataframe['ema21'].shift()) & # 确认是刚刚突破 + (dataframe['volume_ratio'] > 1.2) # 成交量确认 + ) | + + # 条件3: 超卖信号 + ( + (dataframe['rsi'] < 30) & # RSI超卖 + (dataframe['close'] < dataframe['bb_lowerband']) # 价格突破下轨 + ) | + + # 条件4: 动量减弱 + ( + (dataframe['rsi'] < 35) & + (dataframe['rsi'] > dataframe['rsi'].shift()) & + (dataframe['rsi'].shift() > dataframe['rsi'].shift(2)) & # RSI连续两根K线上升 + (dataframe['momentum'] > 0) # 价格动量转为正 + ) | + + # 条件5: 锤子线形态 (潜在反转信号) + ( + (dataframe['hammer'] > 0) & + (dataframe['volume_ratio'] > 1.3) + ) + ) + + dataframe.loc[short_exit_conditions, 'exit_short'] = 1 + dataframe.loc[short_exit_conditions, 'exit_tag'] = 'chan_sol_short_exit' + return dataframe + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, + time_in_force: str, current_time: datetime, entry_tag: Optional[str], + side: str, **kwargs) -> bool: + """ + 在进入交易前进行额外的确认 + """ + # 只做空头交易 + if side == "sell" and entry_tag == "chan_sol_short": + return True + return False + def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: