openfund-maker 2.3.1__py3-none-any.whl → 2.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maker/BestTopDownStrategyMaker.py +145 -65
- maker/StrategyMaker.py +67 -18
- {openfund_maker-2.3.1.dist-info → openfund_maker-2.3.2.dist-info}/METADATA +1 -1
- {openfund_maker-2.3.1.dist-info → openfund_maker-2.3.2.dist-info}/RECORD +6 -6
- {openfund_maker-2.3.1.dist-info → openfund_maker-2.3.2.dist-info}/WHEEL +0 -0
- {openfund_maker-2.3.1.dist-info → openfund_maker-2.3.2.dist-info}/entry_points.txt +0 -0
@@ -38,8 +38,8 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
38
38
|
|
39
39
|
"""
|
40
40
|
try:
|
41
|
-
#
|
42
|
-
if self.
|
41
|
+
# 是否有持仓,有持仓不进行下单
|
42
|
+
if self.fetch_position(symbol=symbol) :
|
43
43
|
self.reset_all_cache(symbol)
|
44
44
|
self.logger.info(f"{symbol} : 有持仓合约,不进行下单。")
|
45
45
|
return
|
@@ -67,9 +67,8 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
67
67
|
htf_df = self.get_historical_klines_df_by_cache(symbol=symbol, tf=htf)
|
68
68
|
htf_struct =self.build_struct(symbol=symbol, data=htf_df)
|
69
69
|
|
70
|
-
htf_latest_struct = self.
|
71
|
-
|
72
|
-
htf_trend = self.BULLISH_TREND if htf_latest_struct[self.STRUCT_DIRECTION_COL] == 1 else self.BEARISH_TREND
|
70
|
+
htf_latest_struct = self.get_latest_struct(symbol=symbol, data=htf_struct)
|
71
|
+
htf_trend = htf_latest_struct[self.STRUCT_DIRECTION_COL]
|
73
72
|
htf_side = self.BUY_SIDE if htf_trend == self.BULLISH_TREND else self.SELL_SIDE
|
74
73
|
# 1.1. Price's Current Trend 市场趋势(HTF)
|
75
74
|
step = "1.1"
|
@@ -86,15 +85,15 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
86
85
|
self.logger.debug(f"{symbol} : {step}. HTF {htf} 未找到OB。")
|
87
86
|
return
|
88
87
|
else:
|
89
|
-
self.logger.debug(f"{symbol} : {step}. HTF {htf} 找到OB。")
|
88
|
+
# self.logger.debug(f"{symbol} : {step}. HTF {htf} 找到OB。")
|
90
89
|
|
91
|
-
htf_support_OB = self.
|
90
|
+
htf_support_OB = self.get_latest_OB(symbol=symbol,data=htf_OBs_df,trend=self.BULLISH_TREND)
|
92
91
|
if htf_support_OB :
|
93
92
|
htf_support_price = htf_support_OB.get(self.OB_MID_COL)
|
94
93
|
else:
|
95
94
|
htf_support_price = htf_struct.at[htf_struct.index[-1], self.STRUCT_LOW_COL]
|
96
95
|
|
97
|
-
htf_resistance_OB = self.
|
96
|
+
htf_resistance_OB = self.get_latest_OB(symbol=symbol,data=htf_OBs_df,trend=self.BEARISH_TREND)
|
98
97
|
if htf_resistance_OB :
|
99
98
|
htf_resistance_price = htf_resistance_OB.get(self.OB_MID_COL)
|
100
99
|
else:
|
@@ -114,17 +113,37 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
114
113
|
|
115
114
|
# 1.5. 检查当前价格是否在关键支撑位和阻力位,支撑位可以做多,阻力位可以做空。
|
116
115
|
step = "1.5"
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
116
|
+
htf_support_OB_top = None
|
117
|
+
if htf_support_OB :
|
118
|
+
htf_support_OB_top = htf_support_OB.get(self.OB_HIGH_COL)
|
119
|
+
htf_resistance_OB_bottom = None
|
120
|
+
if htf_resistance_OB :
|
121
|
+
htf_resistance_OB_bottom = htf_resistance_OB.get(self.OB_LOW_COL)
|
122
|
+
|
123
|
+
# 检查支撑位做多条件
|
124
|
+
if htf_support_OB_top is not None:
|
125
|
+
if market_price <= htf_support_OB_top:
|
126
|
+
# 价格进入支撑OB,可以开始做多
|
127
|
+
if htf_side != self.BUY_SIDE:
|
128
|
+
htf_side = self.BUY_SIDE
|
129
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 当前价格{market_price:.{precision}f} <= HTF_OB_TOP{htf_support_OB_top:.{precision}f}, 开始做多{htf_side}。")
|
130
|
+
else:
|
131
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 当前价格{market_price:.{precision}f} > HTF_OB_TOP{htf_support_OB_top:.{precision}f}, 无需做多{htf_side}。")
|
125
132
|
else:
|
126
|
-
self.logger.info(f"{symbol} : {step}. HTF {htf}
|
127
|
-
|
133
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 未找到HTF_OB_TOP。")
|
134
|
+
|
135
|
+
# 检查阻力位做空条件
|
136
|
+
if htf_resistance_OB_bottom is not None:
|
137
|
+
if market_price >= htf_resistance_OB_bottom:
|
138
|
+
# 价格进入阻力OB,可以开始做空
|
139
|
+
if htf_side != self.SELL_SIDE:
|
140
|
+
htf_side = self.SELL_SIDE
|
141
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 当前价格{market_price:.{precision}f} >= HTF_OB_BOTTOM{htf_resistance_OB_bottom:.{precision}f}, 开始做空{htf_side}。")
|
142
|
+
else:
|
143
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 当前价格{market_price:.{precision}f} < HTF_OB_BOTTOM{htf_resistance_OB_bottom:.{precision}f}, 无需做空{htf_side}。")
|
144
|
+
else:
|
145
|
+
self.logger.info(f"{symbol} : {step}. HTF {htf} 未找到HTF_OB_BOTTOM。")
|
146
|
+
|
128
147
|
"""
|
129
148
|
step 2 : Analysis Time Frames
|
130
149
|
"""
|
@@ -134,7 +153,11 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
134
153
|
atf_side, atf_struct, atf_trend = None, None, None
|
135
154
|
atf_df = self.get_historical_klines_df(symbol=symbol, tf=atf)
|
136
155
|
atf_struct =self.build_struct(symbol=symbol, data=atf_df)
|
137
|
-
|
156
|
+
# 获取最新的市场结构,如果为空则返回None
|
157
|
+
atf_latest_struct = self.get_latest_struct(symbol=symbol, data=atf_struct)
|
158
|
+
if atf_latest_struct is None:
|
159
|
+
self.logger.info(f"{symbol} : {step}. ATF {atf} 未形成结构,等待... ")
|
160
|
+
return
|
138
161
|
atf_trend = atf_latest_struct[self.STRUCT_DIRECTION_COL]
|
139
162
|
atf_side = self.BUY_SIDE if atf_trend == self.BULLISH_TREND else self.SELL_SIDE
|
140
163
|
# 2.1. Price's Current Trend 市场趋势(HTF )
|
@@ -143,22 +166,79 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
143
166
|
# 2.2. Who's In Control 供需控制,Bullish 或者 Bearish | Choch 或者 BOS
|
144
167
|
step = "2.2"
|
145
168
|
self.logger.info(f"{symbol} : {step}. ATF {atf} struct is {atf_latest_struct[self.STRUCT_COL]}。")
|
146
|
-
#
|
169
|
+
# 2.3. 检查关键支撑位和阻力位之间是否有利润空间。
|
170
|
+
step = "2.3"
|
171
|
+
atf_OBs_df = self.find_OBs(symbol=symbol,struct=atf_struct)
|
172
|
+
atf_support_OB = self.get_latest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BULLISH_TREND)
|
173
|
+
if atf_support_OB :
|
174
|
+
atf_support_price = atf_support_OB.get(self.OB_MID_COL)
|
175
|
+
else:
|
176
|
+
atf_support_price = atf_struct.at[atf_struct.index[-1], self.STRUCT_LOW_COL]
|
177
|
+
|
178
|
+
atf_resistance_OB = self.get_latest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BEARISH_TREND)
|
179
|
+
if atf_resistance_OB :
|
180
|
+
atf_resistance_price = atf_resistance_OB.get(self.OB_MID_COL)
|
181
|
+
else:
|
182
|
+
atf_resistance_price = atf_struct.at[atf_struct.index[-1], self.STRUCT_HIGH_COL]
|
183
|
+
|
184
|
+
self.logger.info(f"{symbol} : {step}.1 ATF {atf}, Key Support={atf_support_price:.{precision}f} "
|
185
|
+
f"& Key Resistance={atf_resistance_price:.{precision}f} ")
|
186
|
+
# 计算支撑位和阻力位之间的利润空间百分比
|
187
|
+
atf_profit_percent = abs((atf_resistance_price - atf_support_price) / atf_support_price * 100)
|
188
|
+
|
189
|
+
if atf_profit_percent < min_profit_percent:
|
190
|
+
self.logger.info(f"{symbol} : {step}.2 ATF {atf} 支撑位={atf_support_price:.{precision}f} 与阻力位={atf_resistance_price:.{precision}f} "
|
191
|
+
f"之间利润空间{atf_profit_percent:.2f}% < {min_profit_percent}%,等待...")
|
192
|
+
return
|
193
|
+
else:
|
194
|
+
self.logger.info(f"{symbol} : {step}.2 ATF {atf} 支撑位={atf_support_price:.{precision}f} 与阻力位={atf_resistance_price:.{precision}f} "
|
195
|
+
f"之间利润空间{atf_profit_percent:.2f}% >= {min_profit_percent}%,允许下单...")
|
196
|
+
|
197
|
+
|
147
198
|
|
148
199
|
# 2.4. ATF 方向要和 HTF方向一致
|
149
200
|
step = "2.4"
|
150
201
|
|
151
|
-
if
|
152
|
-
self.logger.info(f"{symbol} : {step}. ATF {atf} is {
|
202
|
+
if htf_side != atf_side:
|
203
|
+
self.logger.info(f"{symbol} : {step}. ATF {atf} is {atf_side} 与 HTF {htf} is {htf_side} 不一致,等待...")
|
153
204
|
return
|
154
205
|
else:
|
155
|
-
self.logger.info(f"{symbol} : {step}. ATF {atf} is {
|
156
|
-
|
157
|
-
|
206
|
+
self.logger.info(f"{symbol} : {step}. ATF {atf} is {atf_side} 与 HTF {htf} is {htf_side} 一致。")
|
207
|
+
|
208
|
+
#2.5. 反转结构CHOCH, check Liquidity Areas ,检查当前结构是否是流动性摄取。
|
209
|
+
step = "2.5"
|
210
|
+
# if "CHOCH" in atf_struct[self.STRUCT_COL] or "BOS" in atf_struct[self.STRUCT_COL]:
|
211
|
+
# 2.5.1. Equal Lows & Equal Highs
|
212
|
+
end_idx = atf_latest_struct[self.STRUCT_HIGH_INDEX_COL] if atf_side == self.BUY_SIDE else atf_latest_struct[self.STRUCT_LOW_INDEX_COL]
|
213
|
+
last_EQ = self.find_EQH_EQL(symbol=symbol, data=atf_df, trend=atf_trend, end_idx=end_idx, pair_config=pair_config)
|
214
|
+
if last_EQ and last_EQ[self.HAS_EQ_KEY]:
|
215
|
+
price_eq = last_EQ[self.EQUAL_HIGH_COL] if atf_side == self.BUY_SIDE else last_EQ[self.EQUAL_LOW_COL]
|
216
|
+
self.logger.info(f"{symbol} : {step}.1 ATF {atf} {atf_side} find EQ {price_eq}")
|
217
|
+
# 检查是否Liquidity Sweeps
|
218
|
+
if (atf_side == self.BUY_SIDE and atf_latest_struct[self.STRUCT_HIGH_COL] > price_eq) \
|
219
|
+
or (atf_side == self.SELL_SIDE and atf_latest_struct[self.STRUCT_LOW_COL] < price_eq):
|
158
220
|
|
221
|
+
atf_side = self.SELL_SIDE if atf_side == self.BUY_SIDE else self.BUY_SIDE
|
222
|
+
self.logger.info(f"{symbol} : {step}.1 ATF {atf} Liquidity Sweeps , Reverse the ATF {atf} {atf_side} side。")
|
223
|
+
else:
|
224
|
+
self.logger.info(f"{symbol} : {step}.1 ATF {atf} is not found Liquidity Sweeps .")
|
225
|
+
else:
|
226
|
+
self.logger.info(f"{symbol} : {step}.1 ATF {atf} is not found EQ .")
|
227
|
+
|
228
|
+
# FIXME 2.5.2. Dynamic Trendlines and Channels
|
229
|
+
# atf_pre_struct = atf_struct[atf_struct[self.STRUCT_DIRECTION_COL].notna()].iloc[-2] # 看前一个结构是否为动态趋势
|
230
|
+
# atf_start_index = min(atf_pre_struct[self.STRUCT_LOW_INDEX_COL] ,atf_pre_struct[self.STRUCT_HIGH_INDEX_COL])
|
231
|
+
# atf_end_index = max(atf_latest_struct[self.STRUCT_LOW_INDEX_COL] ,atf_latest_struct[self.STRUCT_HIGH_INDEX_COL])
|
232
|
+
|
233
|
+
# is_dynamic_trendlines = self.identify_dynamic_trendlines(symbol=symbol, data=atf_struct, trend=atf_trend, start_idx=atf_start_index, end_idx=atf_end_index)
|
234
|
+
# if is_dynamic_trendlines :
|
235
|
+
# self.logger.info(f"{symbol} : {step}.2 ATF {atf} {atf_trend} find Dynamic Trendlines .")
|
236
|
+
# else:
|
237
|
+
# self.logger.info(f"{symbol} : {step}.2 ATF {atf} {atf_trend} not find Dynamic Trendlines .")
|
238
|
+
|
159
239
|
|
160
240
|
# 2.6. 在HTF供需区范围,找ATF的PDArray,FVG和OB,供需区,计算监测下单区域范围。
|
161
|
-
|
241
|
+
step = "2.6"
|
162
242
|
atf_pdArrays_df = self.find_PDArrays(symbol=symbol,struct=atf_struct,side=atf_side)
|
163
243
|
|
164
244
|
# 不同的结构,不同位置,如果是Choch则等待价格进入PDArray,如果是BOS则等待价格进入折价区
|
@@ -170,150 +250,150 @@ class BestTopDownStrategyMaker(StrategyMaker):
|
|
170
250
|
if "CHOCH" in atf_struct[self.STRUCT_COL]:
|
171
251
|
# 找PDArray,Bullish 则PDArray的mid要小于 atf_struct_mid,Bearish 则PDArray的mid要大于 atf_struct_mid
|
172
252
|
# atf_discount_mid = (atf_struct_mid + atf_struct_high) / 2 if atf_trend == self.BEARISH_TREND else (atf_struct_mid + atf_struct_low) / 2
|
173
|
-
mask = atf_pdArrays_df[self.PD_MID_COL] >= atf_struct_mid if
|
253
|
+
mask = atf_pdArrays_df[self.PD_MID_COL] >= atf_struct_mid if atf_side == self.BUY_SIDE else atf_pdArrays_df[self.PD_MID_COL] <= atf_struct_mid
|
174
254
|
atf_pdArrays_df = atf_pdArrays_df[mask]
|
175
255
|
if len(atf_pdArrays_df) == 0:
|
176
|
-
self.logger.info(f"{symbol} : {
|
256
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} 未找到PDArray,不下单")
|
177
257
|
return
|
178
258
|
else:
|
179
259
|
# 找到最新的PDArray
|
180
260
|
atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
|
181
|
-
self.logger.info(f"{symbol} : {
|
261
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
|
182
262
|
f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
|
183
263
|
|
184
264
|
|
185
265
|
#SMS
|
186
266
|
elif "SMS" in atf_struct[self.STRUCT_COL]:
|
187
|
-
mask = atf_pdArrays_df[self.PD_MID_COL] >= atf_struct_mid if
|
267
|
+
mask = atf_pdArrays_df[self.PD_MID_COL] >= atf_struct_mid if atf_side == self.BUY_SIDE else atf_pdArrays_df[self.PD_MID_COL] <= atf_struct_mid
|
188
268
|
atf_pdArrays_df = atf_pdArrays_df[mask]
|
189
269
|
if len(atf_pdArrays_df) == 0:
|
190
|
-
self.logger.info(f"{symbol} : {
|
270
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} 在{atf_struct_mid:.{precision}f}未找到PDArray,不下单")
|
191
271
|
return
|
192
272
|
else:
|
193
273
|
# 找到最新的PDArray
|
194
274
|
atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
|
195
|
-
self.logger.info(f"{symbol} : {
|
275
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
|
196
276
|
f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
|
197
277
|
|
198
278
|
|
199
279
|
#BMS
|
200
280
|
else:
|
201
|
-
atf_premium_mid = (atf_struct_mid + atf_struct_low) / 2 if
|
202
|
-
mask = atf_pdArrays_df[self.PD_HIGH_COL] >= atf_premium_mid if
|
281
|
+
atf_premium_mid = (atf_struct_mid + atf_struct_low) / 2 if atf_side == self.BUY_SIDE else (atf_struct_mid + atf_struct_high) / 2
|
282
|
+
mask = atf_pdArrays_df[self.PD_HIGH_COL] >= atf_premium_mid if atf_side == self.BUY_SIDE else atf_pdArrays_df[self.PD_LOW_COL] <= atf_premium_mid
|
203
283
|
atf_pdArrays_df = atf_pdArrays_df[mask]
|
204
284
|
if len(atf_pdArrays_df) == 0:
|
205
|
-
self.logger.info(f"{symbol} : {
|
285
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} ,在{atf_premium_mid:.{precision}f}未找到PDArray,不下单")
|
206
286
|
return
|
207
287
|
else:
|
208
288
|
# 找到最新的PDArray
|
209
289
|
atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
|
210
|
-
self.logger.info(f"{symbol} : {
|
290
|
+
self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
|
211
291
|
f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}")
|
212
292
|
|
213
293
|
|
214
294
|
|
215
|
-
|
295
|
+
step = "2.7"
|
216
296
|
|
217
297
|
# 2.7. 等待价格进入 PDArray
|
218
298
|
|
219
299
|
if not (market_price <= atf_vaild_pdArray[self.PD_HIGH_COL] and market_price >= atf_vaild_pdArray[self.PD_LOW_COL]):
|
220
|
-
self.logger.info(f"{symbol} : {
|
300
|
+
self.logger.info(f"{symbol} : {step}. ATF {atf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
|
221
301
|
f"PD_HIGH={atf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f} "
|
222
302
|
f"PD_LOW={atf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} ")
|
223
303
|
|
224
304
|
return
|
225
305
|
else:
|
226
|
-
self.logger.info(f"{symbol} : {
|
306
|
+
self.logger.info(f"{symbol} : {step}. ATF {atf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
|
227
307
|
f"PD_HIGH={atf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f} "
|
228
308
|
f"PD_LOW={atf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} ")
|
229
309
|
|
230
310
|
|
231
311
|
# 3. ETF Step
|
232
|
-
|
312
|
+
step = "3.1"
|
233
313
|
etf_side, etf_struct, etf_trend = None, None, None
|
234
314
|
etf_df = self.get_historical_klines_df(symbol=symbol, tf=etf)
|
235
315
|
etf_struct =self.build_struct(symbol=symbol, data=etf_df)
|
236
|
-
etf_latest_struct = self.
|
316
|
+
etf_latest_struct = self.get_latest_struct(symbol=symbol, data=etf_struct)
|
237
317
|
|
238
|
-
# 初始化
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
etf_trend =
|
243
|
-
|
318
|
+
# 初始化ETF趋势相关变量
|
319
|
+
if etf_latest_struct is None:
|
320
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf} 未形成结构,等待... ")
|
321
|
+
return
|
322
|
+
etf_trend = etf_latest_struct[self.STRUCT_DIRECTION_COL]
|
244
323
|
etf_side = self.BUY_SIDE if etf_trend == self.BULLISH_TREND else self.SELL_SIDE
|
245
324
|
|
246
325
|
# 3.1. Price's Current Trend 市场趋势(ETF )
|
247
|
-
|
248
|
-
self.logger.info(f"{symbol} : {
|
326
|
+
step = "3.1"
|
327
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf} Price's Current Trend is {etf_trend}。")
|
249
328
|
# 3.2. Who's In Control 供需控制,Bullish 或者 Bearish | Choch 或者 BOS
|
250
|
-
|
251
|
-
self.logger.info(f"{symbol} : {
|
329
|
+
step = "3.2"
|
330
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf} struct is {etf_latest_struct[self.STRUCT_COL]}。")
|
252
331
|
|
253
332
|
|
254
333
|
# 3.3 Reversal Signs 反转信号
|
255
|
-
|
334
|
+
step = "3.3"
|
256
335
|
|
257
|
-
if
|
336
|
+
if atf_side != etf_side:
|
258
337
|
|
259
|
-
self.logger.info(f"{symbol} : {
|
338
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}未反转,等待...")
|
260
339
|
return
|
261
340
|
else:
|
262
|
-
self.logger.info(f"{symbol} : {
|
341
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}已反转。")
|
263
342
|
|
264
343
|
# TODO "CHOCH"|"BOS" 的PDArray 入场位置不一样
|
265
344
|
|
266
345
|
# 3.4 找 PD Arrays 价格区间(ETF 看上下的供需区位置)
|
267
|
-
|
346
|
+
step = "3.4"
|
268
347
|
etf_pdArrays_df = self.find_PDArrays(symbol=symbol,struct=etf_struct,side=etf_side)
|
269
348
|
# 划分 折价(discount)区和溢价(premium)区
|
270
349
|
etf_struct_high = etf_latest_struct[self.STRUCT_HIGH_COL]
|
271
350
|
etf_struct_low = etf_latest_struct[self.STRUCT_LOW_COL]
|
272
351
|
etf_struct_mid = etf_latest_struct[self.STRUCT_MID_COL]
|
273
|
-
mask = etf_pdArrays_df[self.PD_MID_COL] >= etf_struct_mid if
|
352
|
+
mask = etf_pdArrays_df[self.PD_MID_COL] >= etf_struct_mid if etf_side == self.SELL_SIDE else etf_pdArrays_df[self.PD_MID_COL] <= etf_struct_mid
|
274
353
|
etf_pdArrays_df = etf_pdArrays_df[mask]
|
275
354
|
if len(etf_pdArrays_df) == 0:
|
276
|
-
self.logger.info(f"{symbol} : {
|
355
|
+
self.logger.info(f"{symbol} : {step}.1. ETF {etf} 未找到PDArray,不下单")
|
277
356
|
return
|
278
357
|
else:
|
279
358
|
# 找到最新的PDArray
|
280
359
|
etf_vaild_pdArray = etf_pdArrays_df.iloc[-1]
|
281
|
-
self.logger.info(f"{symbol} : {
|
360
|
+
self.logger.info(f"{symbol} : {step}.1. ETF {etf} 找到PDArray.\n"
|
282
361
|
f"{etf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
|
283
362
|
|
284
363
|
|
285
364
|
if not (market_price <= etf_vaild_pdArray[self.PD_HIGH_COL] and market_price >= etf_vaild_pdArray[self.PD_LOW_COL]):
|
286
|
-
self.logger.info(f"{symbol} : {
|
365
|
+
self.logger.info(f"{symbol} : {step}.2. ETF {etf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
|
287
366
|
f"PD_HIGH={etf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} "
|
288
367
|
f"PD_LOW={etf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f}")
|
289
368
|
|
290
369
|
return
|
291
370
|
else:
|
292
|
-
self.logger.info(f"{symbol} : {
|
371
|
+
self.logger.info(f"{symbol} : {step}.2. ETF {etf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
|
293
372
|
f"PD_HIGH={etf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} "
|
294
373
|
f"PD_LOW={etf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f}")
|
295
374
|
|
296
375
|
# 3.5 Place Order 下单
|
297
|
-
|
376
|
+
step = "3.5"
|
298
377
|
# order_price = self.toDecimal(etf_vaild_pdArray[self.PD_HIGH_COL] if etf_trend == self.BULLISH_TREND else etf_vaild_pdArray[self.PD_LOW_COL] )
|
299
378
|
order_price = self.toDecimal(etf_vaild_pdArray[self.PD_MID_COL])
|
300
379
|
|
301
380
|
latest_order_price = self.toDecimal(self.place_order_prices.get(symbol,0))
|
302
381
|
if order_price == latest_order_price:
|
303
|
-
self.logger.info(f"{symbol} : {
|
382
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf}, 下单价格 {order_price:.{precision}} 未变化,不进行下单。")
|
304
383
|
return
|
305
384
|
|
306
385
|
self.cancel_all_orders(symbol=symbol)
|
307
386
|
self.place_order(symbol=symbol, price=order_price, side=etf_side, pair_config=pair_config)
|
308
387
|
self.place_order_prices[symbol] = order_price # 记录下单价格,过滤重复下单
|
309
|
-
self.logger.info(f"{symbol} : {
|
388
|
+
self.logger.info(f"{symbol} : {step}. ETF {etf}, {etf_side} 价格={order_price:.{precision}}")
|
310
389
|
|
311
390
|
|
312
391
|
except KeyboardInterrupt:
|
313
392
|
self.logger.info("程序收到中断信号,开始退出...")
|
314
393
|
except Exception as e:
|
315
394
|
error_message = f"程序异常退出: {str(e)}"
|
316
|
-
|
395
|
+
# 记录错误信息和堆栈跟踪
|
396
|
+
self.logger.error(f"{error_message}\n{traceback.format_exc()}")
|
317
397
|
traceback.print_exc()
|
318
398
|
self.send_feishu_notification(symbol, error_message)
|
319
399
|
finally:
|
maker/StrategyMaker.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
from functools import lru_cache
|
3
|
-
import traceback
|
4
3
|
import pandas as pd
|
5
4
|
from datetime import datetime, timedelta
|
6
5
|
from decimal import Decimal
|
@@ -14,7 +13,8 @@ from core.smc import (
|
|
14
13
|
SMCPDArray,
|
15
14
|
SMCStruct,
|
16
15
|
SMCOrderBlock,
|
17
|
-
SMCFVG
|
16
|
+
SMCFVG,
|
17
|
+
SMCLiquidity
|
18
18
|
)
|
19
19
|
|
20
20
|
class StrategyMaker():
|
@@ -53,6 +53,14 @@ class StrategyMaker():
|
|
53
53
|
PD_LOW_COL = SMCPDArray.SMCPDArray.PD_LOW_COL
|
54
54
|
PD_MID_COL = SMCPDArray.SMCPDArray.PD_MID_COL
|
55
55
|
PD_TYPE_COL = SMCPDArray.SMCPDArray.PD_TYPE_COL
|
56
|
+
|
57
|
+
LIQU_HIGH_COL = SMCLiquidity.SMCLiquidity.LIQU_HIGH_COL
|
58
|
+
LIQU_LOW_COL = SMCLiquidity.SMCLiquidity.LIQU_LOW_COL
|
59
|
+
EQUAL_HIGH_COL = SMCLiquidity.SMCLiquidity.EQUAL_HIGH_COL
|
60
|
+
EQUAL_LOW_COL = SMCLiquidity.SMCLiquidity.EQUAL_LOW_COL
|
61
|
+
EQH_INDEX_KEY = SMCLiquidity.SMCLiquidity.EQUAL_HIGH_INDEX_KEY
|
62
|
+
EQL_INDEX_KEY = SMCLiquidity.SMCLiquidity.EQUAL_LOW_INDEX_KEY
|
63
|
+
HAS_EQ_KEY = SMCLiquidity.SMCLiquidity.HAS_EQ_KEY
|
56
64
|
|
57
65
|
def __init__(self, config, platform_config, common_config, feishu_webhook=None, logger=None ,exchangeKey='okx'):
|
58
66
|
"""_summary_
|
@@ -97,6 +105,7 @@ class StrategyMaker():
|
|
97
105
|
self.smcStruct = SMCStruct.SMCStruct()
|
98
106
|
self.smcOB = SMCOrderBlock.SMCOrderBlock()
|
99
107
|
self.smcFVG = SMCFVG.SMCFVG()
|
108
|
+
self.smcLiqu = SMCLiquidity.SMCLiquidity()
|
100
109
|
|
101
110
|
self.interval_map = {
|
102
111
|
'1d': 24 * 60 * 60 , # 1天
|
@@ -183,17 +192,22 @@ class StrategyMaker():
|
|
183
192
|
|
184
193
|
# 记录下单日志
|
185
194
|
direction = self.BULLISH_TREND if side == self.BUY_SIDE else self.BEARISH_TREND
|
186
|
-
self.logger.
|
195
|
+
self.logger.info(f"{symbol} : 触发{direction}下单条件. 下单价格: {price}")
|
187
196
|
|
188
197
|
# 执行下单
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
198
|
+
try :
|
199
|
+
self.exchange.place_order(
|
200
|
+
symbol=symbol,
|
201
|
+
price=price,
|
202
|
+
amount_usdt=order_amount_usdt,
|
203
|
+
side=side,
|
204
|
+
leverage=leverage,
|
205
|
+
order_type=order_type
|
206
|
+
)
|
207
|
+
except Exception as e:
|
208
|
+
error_message = f"{symbol} 下单失败: {e}"
|
209
|
+
self.logger.warning(error_message)
|
210
|
+
self.send_feishu_notification(symbol, error_message)
|
197
211
|
|
198
212
|
def cancel_all_orders(self, symbol):
|
199
213
|
"""_summary_
|
@@ -201,7 +215,13 @@ class StrategyMaker():
|
|
201
215
|
Args:
|
202
216
|
symbol (_type_): _description_
|
203
217
|
"""
|
204
|
-
|
218
|
+
try:
|
219
|
+
self.exchange.cancel_all_orders(symbol=symbol)
|
220
|
+
except Exception as e:
|
221
|
+
error_message = f"{symbol} 取消所有挂单失败: {e}"
|
222
|
+
self.logger.warning(error_message)
|
223
|
+
self.send_feishu_notification(symbol, error_message)
|
224
|
+
|
205
225
|
def get_historical_klines(self, symbol, tf='15m'):
|
206
226
|
"""_summary_
|
207
227
|
获取历史K线数据
|
@@ -327,7 +347,7 @@ class StrategyMaker():
|
|
327
347
|
|
328
348
|
return self.smcOB.find_OBs(struct=struct, side=side, start_index=start_index, is_valid=is_valid)
|
329
349
|
|
330
|
-
def
|
350
|
+
def get_latest_OB(self, symbol, data, trend, start_index=-1) -> dict:
|
331
351
|
"""_summary_
|
332
352
|
获取最新的Order Block
|
333
353
|
Args:
|
@@ -339,7 +359,7 @@ class StrategyMaker():
|
|
339
359
|
_type_: _description_
|
340
360
|
"""
|
341
361
|
|
342
|
-
return self.smcOB.
|
362
|
+
return self.smcOB.get_latest_OB(data=data, trend=trend, start_index=start_index)
|
343
363
|
|
344
364
|
|
345
365
|
def find_FVGs(self, symbol, data, side, check_balanced=True, start_index=-1, pair_config=None) -> pd.DataFrame:
|
@@ -359,6 +379,35 @@ class StrategyMaker():
|
|
359
379
|
|
360
380
|
return self.smcFVG.find_FVGs(data, side, check_balanced, start_index)
|
361
381
|
|
382
|
+
def find_EQH_EQL(self, symbol, data, trend, end_idx=-1, atr_offset=0.1, pair_config=None) -> dict:
|
383
|
+
"""_summary_
|
384
|
+
寻找等值高点和等值低点
|
385
|
+
Args:
|
386
|
+
symbol (_type_): _description_
|
387
|
+
data (_type_): _description_
|
388
|
+
trend (_type_): _description_
|
389
|
+
end_idx (int, optional): _description_. Defaults to -1.
|
390
|
+
atr_offset (float, optional): _description_. Defaults to 0.1.
|
391
|
+
Returns:
|
392
|
+
_type_: _description_
|
393
|
+
"""
|
394
|
+
return self.smcLiqu.find_EQH_EQL(data, trend, end_idx=end_idx, atr_offset=atr_offset)
|
395
|
+
|
396
|
+
def identify_dynamic_trendlines(self, symbol, data, trend, start_idx=-1, end_idx=-1, ratio=0.8) -> bool:
|
397
|
+
"""_summary_
|
398
|
+
识别动态趋势线
|
399
|
+
Args:
|
400
|
+
symbol (_type_): _description_
|
401
|
+
data (_type_): _description_
|
402
|
+
trend (_type_): _description_
|
403
|
+
start_idx (int, optional): _description_. Defaults to -1.
|
404
|
+
end_idx (int, optional): _description_. Defaults to -1.
|
405
|
+
ratio (float, optional): _description_. Defaults to 0.5.
|
406
|
+
Returns:
|
407
|
+
_type_: _description_
|
408
|
+
"""
|
409
|
+
return self.smcLiqu.identify_dynamic_trendlines(data, trend, start_idx, end_idx, ratio)
|
410
|
+
|
362
411
|
def build_struct(self, symbol, data) -> pd.DataFrame:
|
363
412
|
|
364
413
|
"""_summary_
|
@@ -373,7 +422,7 @@ class StrategyMaker():
|
|
373
422
|
|
374
423
|
return self.smcStruct.build_struct(data)
|
375
424
|
|
376
|
-
def
|
425
|
+
def get_latest_struct(self, symbol, data) -> dict:
|
377
426
|
"""_summary_
|
378
427
|
获取最后一个SMC结构
|
379
428
|
Args:
|
@@ -382,7 +431,7 @@ class StrategyMaker():
|
|
382
431
|
Returns:
|
383
432
|
_type_: _description_
|
384
433
|
"""
|
385
|
-
return self.smcStruct.
|
434
|
+
return self.smcStruct.get_latest_struct(data)
|
386
435
|
|
387
436
|
def reset_all_cache(self, symbol):
|
388
437
|
"""_summary_
|
@@ -392,7 +441,7 @@ class StrategyMaker():
|
|
392
441
|
self.place_order_prices.pop(symbol)
|
393
442
|
self.clear_cache_historical_klines_df(symbol)
|
394
443
|
|
395
|
-
def
|
444
|
+
def fetch_position(self, symbol) -> bool:
|
396
445
|
"""
|
397
446
|
检查指定交易对是否有持仓,失败时最多重试3次
|
398
447
|
|
@@ -409,7 +458,7 @@ class StrategyMaker():
|
|
409
458
|
error_message = f"{symbol} 检查持仓失败: {e}"
|
410
459
|
self.logger.error(error_message)
|
411
460
|
self.send_feishu_notification(symbol,error_message)
|
412
|
-
return
|
461
|
+
return True
|
413
462
|
|
414
463
|
@abstractmethod
|
415
464
|
def process_pair(self, symbol, pair_config):
|
@@ -1,8 +1,8 @@
|
|
1
1
|
maker/BestFVGStrategyMaker.py,sha256=a9UfClrfzkgX6jXL2FODzANtawrmGeZ_PVeO1-tweDc,12532
|
2
|
-
maker/BestTopDownStrategyMaker.py,sha256
|
2
|
+
maker/BestTopDownStrategyMaker.py,sha256=-cyoxGn6ZKFJ4neFKnHHJ84ZvODMR-a_agitHz6VNXI,23735
|
3
3
|
maker/MACDStrategyMaker.py,sha256=WX8wqpF9h5W4WclN1NjZ_Bur7KFi_aMTvacfLyHzEcI,12681
|
4
4
|
maker/SMCStrategyMaker.py,sha256=hkDqymWnuyYDo1gTYY_uyO4H4yOwCw8NBTM9RcfLRPc,28780
|
5
|
-
maker/StrategyMaker.py,sha256=
|
5
|
+
maker/StrategyMaker.py,sha256=iJa-9MxuUwPDOZot2YJmT-sdYHnPZbgPsdCaKvw3Sis,18821
|
6
6
|
maker/ThreeLineStrategyMaker.py,sha256=K4NZB1rH8IZMVrCFEnzwXctZQbyI9ZdyTMrYObpl-vM,32095
|
7
7
|
maker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
maker/history_code/WickReversalStrategyMaker.py,sha256=7DqPDVJot4EM0_lSAcFAHrR9rNvkIds9KLMoDOiAHEc,17486
|
@@ -11,7 +11,7 @@ maker/history_code/okxapi.py,sha256=_9G0U_o0ZC8NxaT6PqpiLgxBm9gPobC9PsFHZE1c5w0,
|
|
11
11
|
maker/history_code/zhen.py.bak,sha256=HNkrQbJts8G9umE9chEFsc0cLQApcM9KOVNMYPpkBXM,10918
|
12
12
|
maker/history_code/zhen_2.py,sha256=4IaHVtTCMSlrLGSTZrWpW2q-f7HZsUNRkW_-5QgWv24,10509
|
13
13
|
maker/main.py,sha256=PRCP2qCUiUFPQyi1YbvnmW9KqeCZcc0zGjy9OBvMWbM,3723
|
14
|
-
openfund_maker-2.3.
|
15
|
-
openfund_maker-2.3.
|
16
|
-
openfund_maker-2.3.
|
17
|
-
openfund_maker-2.3.
|
14
|
+
openfund_maker-2.3.2.dist-info/METADATA,sha256=V93SlcoCwKl4tYklrw4SMY_t2LqVZyFB9ZsF2OhOSD4,2001
|
15
|
+
openfund_maker-2.3.2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
16
|
+
openfund_maker-2.3.2.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
|
17
|
+
openfund_maker-2.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|