coding macd

This commit is contained in:
jackyu66git
2025-08-30 01:07:03 +08:00
parent bb903a11cf
commit bfbf1781ec
8 changed files with 185 additions and 93 deletions
+13 -9
View File
@@ -78,21 +78,25 @@ class Chan_KLC_FX(Enum):
class Chan_MACD_STATE(Enum):
"""MACD状态枚举 - 包含所有可能的状态"""
# 穿越状态
CROSS0_UP = auto() # 向上穿越零轴
CROSS0_DOWN = auto() # 向下穿越零轴
CROSS0_UP = auto() # 穿零轴后快速向上,能量柱呈现一根比一根长的排列方式
CROSS0_DOWN = auto() # 穿零轴后快速向下,能量柱呈现一根比一根短的排列方式
CROSS_OS = auto() # 穿零轴后缠绕/粘合,黄白线沿着能量柱运行,黄白线在运行的过程中没有释放出反向能量柱
CROSS_REV = auto() # 穿零轴后倒挂,MACD黄白线在穿零轴的时候与零轴的距离比较近,同时黄白线沿着能量柱运行,在运行的过程中,能量柱衰减导致它跟黄白线之间形成夹角空位,同时黄白线产生交叉并释放反向能量柱。
# 趋势状态
NEAR0 = auto()
NEAR0_1 = auto() # 价格在EMA52附近/价格接触EMA52并马上离开,需要观察离开强度
NEAR0_2 = auto() # MACD白线接近零轴,价格未到EMA52
NEAR0_3 = auto() # MACD白线接近零轴和价格接触或短暂击穿EMA52,而MACD黄线不穿零轴
NEAR0_4 = auto() # MACD黄白线接近零轴和价格在EMA24附近
NEAR0_52 = auto() # 价格在EMA52附近/价格接触EMA52并马上离开,需要观察离开强度
NEAR0_DIFF = auto() # MACD白线接近零轴,价格未到EMA52
NEAR0_PERFECT = auto() # MACD白线接近零轴和价格接触或短暂击穿EMA52,而MACD黄线不穿零轴,完美形态
NEAR0_24 = auto() # MACD黄白线接近零轴和价格在EMA24附近
# 位置状态
HIGH = auto() # 高位:MACD黄白线离开零轴到高点,能量柱最大开始减弱
HIGH = auto() # 高位:MACD黄白线离开能量柱到高点,能量柱最大开始减弱
HIGH_EMPTY = auto() # 高位空:MACD黄白线处于高位,能量柱衰减,与黄白线形成空间夹角
RETURN_ZERO = auto() # 归零轴:能量柱呈现一根比一根短的排列方式
UP = auto() # 归零轴后的上涨
DOWN = auto() # 归零轴后的下跌
RZ_UP = auto() # 归零轴后的零轴上涨
RZ_DOWN = auto() # 归零轴后的零轴下跌
# 基础状态
UNKNOWN = auto() # 未知
START = auto() # 开始
+2 -2
View File
@@ -78,12 +78,12 @@ class ChanKLC():
self.klc_fx_type = Chan_KLC_FX.BOTTOM4
if self.fx == Chan_FX_TYPE.TOP:
#print(self.end_time, self.fx, self.macd, self.macdhist, len(self.klus))
if self.macdhist < 5 and self.macd > 0 and self.macd > self.pre.macd and self.macd > self.next.macd:
if self.macdhist < 5 and self.macd > 0:
self.klc_fx_type = Chan_KLC_FX.TOP5
self.bb_out = True
else:
if self.fx == Chan_FX_TYPE.BOTTOM:
if self.macdhist > -5 and self.macd < 0 and self.macd < self.pre.macd and self.macd < self.next.macd:
if self.macdhist > -5 and self.macd < 0:
self.klc_fx_type = Chan_KLC_FX.BOTTOM5
self.bb_out = True
+4 -4
View File
@@ -676,12 +676,12 @@ class ChanKLU:
# 6) 离开0轴开始上涨或者下跌阶段,高位之前的
if self.macd_state == Chan_MACD_STATE.UNKNOWN:
if self.macd > 0 and self.pre:
if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.UP) and self.signal > self.pre.signal:
self.macd_state = Chan_MACD_STATE.UP
if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_UP) and self.signal > self.pre.signal:
self.macd_state = Chan_MACD_STATE.RZ_UP
return self.macd_state
elif self.macd < 0 and self.pre:
if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.DOWN) and self.signal < self.pre.signal:
self.macd_state = Chan_MACD_STATE.DOWN
if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_DOWN) and self.signal < self.pre.signal:
self.macd_state = Chan_MACD_STATE.RZ_DOWN
return self.macd_state
# 7) 其余情况
self.macd_state = Chan_MACD_STATE.UNKNOWN
+2 -2
View File
@@ -81,7 +81,7 @@ class ChanMACD():
else:
# 4) UnitTF 状态机:用黄线Signal的归零轴
if last_klu.macd_state == Chan_MACD_STATE.NEAR0 and last_unittf.near0_count > 1:
if klu.macd_state == Chan_MACD_STATE.UP:
if klu.macd_state == Chan_MACD_STATE.RZ_UP:
last_unittf.set_end_klu(last_klu, Chan_MACDUNITTF_TYPE.NEAR0)
new_dir = Chan_MACDUNITTF_DIR.ABOVE if last_unittf.unittf_dir == Chan_MACDUNITTF_DIR.UNDER else Chan_MACDUNITTF_DIR.UNDER
unittf = ChanMACDUnitTF(klu.time, klu, last_unittf, new_dir, Chan_MACDUNITTF_TYPE.NEAR0)
@@ -90,7 +90,7 @@ class ChanMACD():
last_unittf.set_next(unittf)
last_unittf = unittf
last_seg.add_unittf(unittf)
elif klu.macd_state == Chan_MACD_STATE.DOWN:
elif klu.macd_state == Chan_MACD_STATE.RZ_DOWN:
last_unittf.set_end_klu(last_klu, Chan_MACDUNITTF_TYPE.NEAR0)
new_dir = Chan_MACDUNITTF_DIR.UNDER if last_unittf.unittf_dir == Chan_MACDUNITTF_DIR.ABOVE else Chan_MACDUNITTF_DIR.ABOVE
unittf = ChanMACDUnitTF(klu.time, klu, last_unittf, new_dir, Chan_MACDUNITTF_TYPE.NEAR0)
+38 -12
View File
@@ -6,30 +6,56 @@ class ChanMACDHistSet():
self.end_time = None
self.klu_list = []
self.klu_list.append(start_klu)
self.ref_klu = None
self.histset_dir = dir
self.next = None
self.pre = pre_histset
self.high_klu = start_klu
self.low_klu = start_klu
self.peak_klu = None
self.div_klu = None
self.area = start_klu.macdhist
self.unittf_div = False
def set_next(self, next_histset):
self.next = next_histset
def set_unittf_div(self, unittf_div):
self.unittf_div = unittf_div
def add_klu(self, klu):
self.klu_list.append(klu)
klu.set_histset(self)
if self.histset_dir == Chan_MACDHISTSET_DIR.ABOVE:
if klu.macdhist > self.high_klu.macdhist:
self.high_klu = klu
if self.peak_klu:
if klu.macdhist > self.peak_klu.macdhist:
self.peak_klu = klu
if klu.macdhist > self.klu_list[-1].macdhist and klu.macdhist < self.peak_klu.macdhist:
self.div_klu = klu
else:
if klu.macdhist < self.low_klu.macdhist:
self.low_klu = klu
if len(self.klu_list) > 0:
pre_klu = self.klu_list[-1]
if pre_klu.macdhist < klu.macdhist:
self.peak_klu = klu
else:
if klu.macdhist < self.high_klu.macdhist:
self.high_klu = klu
if self.peak_klu:
if klu.macdhist < self.peak_klu.macdhist:
self.peak_klu = klu
if klu.macdhist < self.klu_list[-1].macdhist and klu.macdhist > self.peak_klu.macdhist:
self.div_klu = klu
else:
if klu.macdhist > self.low_klu.macdhist:
self.low_klu = klu
if len(self.klu_list) > 0:
pre_klu = self.klu_list[-1]
if pre_klu.macdhist > klu.macdhist:
self.peak_klu = klu
self.klu_list.append(klu)
self.area += klu.macdhist
def set_end_klu(self, end_klu):
self.add_klu(end_klu)
self.end_klu = end_klu
self.end_time = end_klu.time
def find_peak_from(self, from_klu):
peak_klu = None
if from_klu in self.klu_list:
for i in range(from_klu.index + 1, len(self.klu_list)):
klu = self.klu_list[i]
if peak_klu:
if abs(peak_klu.macdhist) < abs(klu.macdhist):
peak_klu = klu
else:
if abs(self.klu_list[i-1].macdhist) < abs(klu.macdhist):
peak_klu = klu
return peak_klu
+14 -22
View File
@@ -1,4 +1,4 @@
from ChanEnum import Chan_MACD_STATE, Chan_MACDUNITTF_DIR
from ChanEnum import Chan_MACD_STATE, Chan_MACDUNITTF_DIR, Chan_MACDHISTSET_DIR
class ChanMACDUnitTF():
@@ -16,32 +16,24 @@ class ChanMACDUnitTF():
self.start_type = start_type
self.end_type = None
self.near0_count = 1
self.high_klu = start_klu
self.low_klu = start_klu
self.peak_klu = None
def set_next(self, next_unittf):
self.next = next_unittf
def add_histset(self, histset):
self.histset_list.append(histset)
histset.set_next(self)
def add_klu(self, klu):
if klu:
self.klu_list.append(klu)
klu.set_unittf(self)
if self.unittf_dir == Chan_MACDUNITTF_DIR.ABOVE:
if klu.macdhist > self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist < self.low_klu.macdhist:
self.low_klu = klu
else:
if klu.macdhist < self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist > self.low_klu.macdhist:
self.low_klu = klu
if klu.macd_state == Chan_MACD_STATE.NEAR0 and len(self.histset_list) > 1:
#print(self.start_time, klu.time, self.near0_count)
self.near0_count += 1
self.klu_list.append(klu)
self.cal_peak_div()
def cal_peak_div(self):
if len(self.histset_list) == 1:
self.peak_klu = self.histset_list[0].find_peak_from(self.start_klu)
else:
for histset in self.histset_list:
if histset.peak_klu and self.peak_klu:
if abs(histset.peak_klu.macdhist) > abs(self.peak_klu.macdhist):
self.peak_klu = histset.peak_klu
else:
histset.set_unittf_div(True)
def set_end_klu(self, end_klu, end_type):
self.add_klu(end_klu)
self.end_type = end_type
+36 -41
View File
@@ -6,7 +6,6 @@ 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
@@ -21,7 +20,7 @@ logger = logging.getLogger(__name__)
# freqtrade plot-dataframe --strategy ChanLun_BTC_30 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_30.json --timerange=20250309-
# freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange=20250712-
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange=20250820-
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405-
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250401
@@ -76,16 +75,16 @@ class ChanLun_BTC_30(IStrategy):
# 关闭分批止盈/仓位调整
position_adjustment_enable = False
startup_candle_count = 2880
time3 = 3
time5 = 5
time15 = 15
time30 = 30
time60 = 60
time2h = 120
time4h = 240
time30 = 15
time1d = 1440
last_time = datetime.now()
chan = ChanLun()
classifier = ChanLunClassifier(None)
last_order = None
last_trade = None
@@ -97,6 +96,7 @@ class ChanLun_BTC_30(IStrategy):
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_2h = resample_to_interval(dataframe, self.get_ticker_indicator() * 120)
dataframe_4h = resample_to_interval(dataframe, self.get_ticker_indicator() * 240)
#dataframe_1d = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d')
@@ -112,11 +112,14 @@ class ChanLun_BTC_30(IStrategy):
dataframe_15 = self.add_indicators(dataframe_15)
dataframe_30 = self.add_indicators(dataframe_30)
dataframe_60 = self.add_indicators(dataframe_60)
dataframe_2h = self.add_indicators(dataframe_2h)
dataframe_4h = self.add_indicators(dataframe_4h)
dataframe_1d = self.add_indicators(dataframe_1d)
#self.chan.plot_dual(dataframe_5, dataframe_30)
#chanpy_state = self.chanpy.get_bsp_state(dataframe_5)
#dataframe_5['chanpy_state'] = chanpy_state
state_list = self.chan.get_klc_state_list(dataframe_3)
dataframe_3['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_5)
dataframe_5['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_15)
@@ -125,6 +128,12 @@ class ChanLun_BTC_30(IStrategy):
dataframe_30['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_60)
dataframe_60['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_2h)
dataframe_2h['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_4h)
dataframe_4h['state'] = state_list
state_list = self.chan.get_klc_state_list(dataframe_1d)
dataframe_1d['state'] = state_list
#bi_list_1 = self.chan.get_bi_list(dataframe)
#bi_list_5 = self.chan.get_bi_list(dataframe_5)
#bi_list_15 = self.chan.get_bi_list(dataframe_15)
@@ -142,9 +151,11 @@ class ChanLun_BTC_30(IStrategy):
dataframe = resampled_merge(dataframe, dataframe_3)
dataframe = resampled_merge(dataframe, dataframe_5)
dataframe = resampled_merge(dataframe, dataframe_15)
#dataframe = resampled_merge(dataframe, dataframe_30)
dataframe = resampled_merge(dataframe, dataframe_30)
dataframe = resampled_merge(dataframe, dataframe_60)
#dataframe = resampled_merge(dataframe, dataframe_4h)
dataframe = resampled_merge(dataframe, dataframe_2h)
dataframe = resampled_merge(dataframe, dataframe_4h)
dataframe = resampled_merge(dataframe, dataframe_1d)
return dataframe
def print_seg(self, dataframe):
klc_list = self.chan.get_klc_list(dataframe)
@@ -313,60 +324,44 @@ class ChanLun_BTC_30(IStrategy):
logger.info(f"保存开仓时ATR值: {entry_atr}")
return None
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time15)
shift_time = self.time15
dataframe.loc[
(
(dataframe[state_str].shift(shift_time) == "-20")
),
['enter_long', 'enter_tag']] = (1, 'long_15')
dataframe.loc[
(
(dataframe[state_str].shift(shift_time) == "20")
),
['enter_short', 'enter_tag']] = (1, 'short_15')
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time30)
fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time30)
#chanpy_state_str = 'resample_{}_chanpy_state'.format(self.get_ticker_indicator()*self.time5)
shift_time = self.time30
dataframe.loc[
(
(dataframe[state_str].shift(shift_time) == "-20")
#(dataframe['state'] == "-30")
#(dataframe[state_str].shift(shift_time) == "-10")
#(dataframe[fx_str].shift(shift_time) == -1)
#(dataframe[chanpy_state_str].shift(shift_time+30) == 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")
#(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal']))
),
['enter_long', 'enter_tag']] = (1, 'long_signal_chan')
['enter_long', 'enter_tag']] = (1, 'long_30')
dataframe.loc[
(
(dataframe[state_str].shift(shift_time) == "20")
#(dataframe[fx_str].shift(shift_time) == 1)
#(dataframe[chanpy_state_str].shift(shift_time+30) == -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")
#(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal']))
),
['enter_short', 'enter_tag']] = (1, 'short_signal_chan')
['enter_short', 'enter_tag']] = (1, 'short_30')
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time30)
fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time30)
#chanpy_state_str = 'resample_{}_chanpy_state'.format(self.get_ticker_indicator()*self.time5)
shift_time = self.time30
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time15)
shift_time = self.time15
dataframe.loc[
(
#(dataframe['state']== "30")
(dataframe[state_str].shift(shift_time) == "10")
#(dataframe[fx_str].shift(shift_time) == 1)
#(dataframe[chanpy_state_str].shift(shift_time+30) == -1)
#(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')
['exit_long', 'exit_tag']] = (1, 'long_close_15')
dataframe.loc[
(
#(dataframe['state']== "30")
(dataframe[state_str].shift(shift_time) == "-10")
#(dataframe[fx_str].shift(shift_time) == -1)
#(dataframe[chanpy_state_str].shift(shift_time+30) == 1)
#(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')
['exit_short', 'exit_tag']] = (1, 'short_close_15')
return dataframe
def leverage(self, pair: str, current_time: datetime, current_rate: float,
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
+75
View File
@@ -73,3 +73,78 @@
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
2025/08/29 12:39:31 [notice] 1#1: using the "epoll" event method
2025/08/29 12:39:31 [notice] 1#1: nginx/1.27.5
2025/08/29 12:39:31 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2025/08/29 12:39:31 [notice] 1#1: OS: Linux 6.10.14-linuxkit
2025/08/29 12:39:31 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/08/29 12:39:31 [notice] 1#1: start worker processes
2025/08/29 12:39:31 [notice] 1#1: start worker process 20
2025/08/29 12:39:31 [notice] 1#1: start worker process 21
2025/08/29 12:39:31 [notice] 1#1: start worker process 22
2025/08/29 12:39:31 [notice] 1#1: start worker process 23
2025/08/29 12:39:31 [notice] 1#1: start worker process 24
2025/08/29 12:39:31 [notice] 1#1: start worker process 25
2025/08/29 12:39:31 [notice] 1#1: start worker process 26
2025/08/29 12:39:31 [notice] 1#1: start worker process 27
2025/08/29 12:39:31 [notice] 1#1: start worker process 28
2025/08/29 12:39:31 [notice] 1#1: start worker process 29
2025/08/29 12:39:31 [notice] 1#1: start worker process 30
2025/08/29 12:39:31 [notice] 1#1: start worker process 31
2025/08/29 12:39:35 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2025/08/29 12:39:35 [notice] 20#20: gracefully shutting down
2025/08/29 12:39:35 [notice] 25#25: gracefully shutting down
2025/08/29 12:39:35 [notice] 22#22: gracefully shutting down
2025/08/29 12:39:35 [notice] 22#22: exiting
2025/08/29 12:39:35 [notice] 21#21: gracefully shutting down
2025/08/29 12:39:35 [notice] 21#21: exiting
2025/08/29 12:39:35 [notice] 23#23: gracefully shutting down
2025/08/29 12:39:35 [notice] 23#23: exiting
2025/08/29 12:39:35 [notice] 24#24: gracefully shutting down
2025/08/29 12:39:35 [notice] 24#24: exiting
2025/08/29 12:39:35 [notice] 20#20: exiting
2025/08/29 12:39:35 [notice] 26#26: gracefully shutting down
2025/08/29 12:39:35 [notice] 26#26: exiting
2025/08/29 12:39:35 [notice] 28#28: gracefully shutting down
2025/08/29 12:39:35 [notice] 28#28: exiting
2025/08/29 12:39:35 [notice] 27#27: gracefully shutting down
2025/08/29 12:39:35 [notice] 27#27: exiting
2025/08/29 12:39:35 [notice] 25#25: exiting
2025/08/29 12:39:35 [notice] 30#30: gracefully shutting down
2025/08/29 12:39:35 [notice] 30#30: exiting
2025/08/29 12:39:35 [notice] 29#29: gracefully shutting down
2025/08/29 12:39:35 [notice] 29#29: exiting
2025/08/29 12:39:35 [notice] 31#31: gracefully shutting down
2025/08/29 12:39:35 [notice] 31#31: exiting
2025/08/29 12:39:35 [notice] 22#22: exit
2025/08/29 12:39:35 [notice] 21#21: exit
2025/08/29 12:39:35 [notice] 23#23: exit
2025/08/29 12:39:35 [notice] 24#24: exit
2025/08/29 12:39:35 [notice] 20#20: exit
2025/08/29 12:39:35 [notice] 26#26: exit
2025/08/29 12:39:35 [notice] 28#28: exit
2025/08/29 12:39:35 [notice] 27#27: exit
2025/08/29 12:39:35 [notice] 25#25: exit
2025/08/29 12:39:35 [notice] 30#30: exit
2025/08/29 12:39:35 [notice] 29#29: exit
2025/08/29 12:39:35 [notice] 31#31: exit
2025/08/29 12:39:35 [notice] 1#1: signal 17 (SIGCHLD) received from 27
2025/08/29 12:39:35 [notice] 1#1: worker process 21 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 26 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 27 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: signal 29 (SIGIO) received
2025/08/29 12:39:35 [notice] 1#1: signal 17 (SIGCHLD) received from 26
2025/08/29 12:39:35 [notice] 1#1: signal 17 (SIGCHLD) received from 22
2025/08/29 12:39:35 [notice] 1#1: worker process 20 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 22 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 23 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 24 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 25 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 28 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 29 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: signal 29 (SIGIO) received
2025/08/29 12:39:35 [notice] 1#1: signal 17 (SIGCHLD) received from 24
2025/08/29 12:39:35 [notice] 1#1: signal 17 (SIGCHLD) received from 30
2025/08/29 12:39:35 [notice] 1#1: worker process 30 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: worker process 31 exited with code 0
2025/08/29 12:39:35 [notice] 1#1: exit