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.
@@ -38,8 +38,8 @@ class BestTopDownStrategyMaker(StrategyMaker):
38
38
 
39
39
  """
40
40
  try:
41
- # 检查是否有持仓,有持仓不进行下单
42
- if self.check_position(symbol=symbol) :
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.get_last_struct(symbol=symbol, data=htf_struct)
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.get_lastest_OB(symbol=symbol,data=htf_OBs_df,trend=self.BULLISH_TREND)
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.get_lastest_OB(symbol=symbol,data=htf_OBs_df,trend=self.BEARISH_TREND)
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
- htf_ce_price = (htf_support_price + htf_resistance_price) / 2
118
- # 折价区
119
- target_trend = self.BULLISH_TREND if (market_price >= htf_support_price and market_price < htf_ce_price) else self.BEARISH_TREND
120
- # FIXME target_trend 更新 htf_trend
121
- htf_trend = target_trend
122
- if target_trend != htf_trend:
123
- self.logger.info(f"{symbol} : {step}. HTF {htf} 目标趋势 {target_trend} 与当前趋势 {htf_trend} 不符,折价区 ce={htf_ce_price:.{precision}f} ,等待...")
124
- return
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} 目标趋势 {target_trend} 与当前趋势 {htf_trend} 一致, ce={htf_ce_price:.{precision}f}")
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
- atf_latest_struct = self.get_last_struct(symbol=symbol, data=atf_struct)
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
- # TODO 2.3. 检查关键支撑位和阻力位之间是否有利润空间。
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 htf_trend != atf_trend:
152
- self.logger.info(f"{symbol} : {step}. ATF {atf} is {atf_trend} 与 HTF {htf} is {htf_trend} 不一致,等待...")
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 {atf_trend} 与 HTF {htf} is {htf_trend} 一致。")
156
- #TODO 2.5. check Liquidity Areas ,检查当前结构是否是流动性摄取。
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
- setp = "2.6"
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 atf_trend == self.BEARISH_TREND else atf_pdArrays_df[self.PD_MID_COL] <= atf_struct_mid
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} : {setp}.1. ATF {atf} 未找到PDArray,不下单")
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} : {setp}.1. ATF {atf} 找到PDArray\n"
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 atf_trend == self.BEARISH_TREND else atf_pdArrays_df[self.PD_MID_COL] <= atf_struct_mid
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} : {setp}.1. ATF {atf} 在{atf_struct_mid:.{precision}f}未找到PDArray,不下单")
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} : {setp}.1. ATF {atf} 找到PDArray\n"
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 atf_trend == self.BEARISH_TREND else (atf_struct_mid + atf_struct_high) / 2
202
- mask = atf_pdArrays_df[self.PD_HIGH_COL] >= atf_premium_mid if atf_trend == self.BEARISH_TREND else atf_pdArrays_df[self.PD_LOW_COL] <= atf_premium_mid
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} : {setp}.1. ATF {atf} ,在{atf_premium_mid:.{precision}f}未找到PDArray,不下单")
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} : {setp}.1. ATF {atf} 找到PDArray\n"
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
- setp = "2.7"
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} : {setp}. ATF {atf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
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} : {setp}. ATF {atf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
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.get_last_struct(symbol=symbol, data=etf_struct)
316
+ etf_latest_struct = self.get_latest_struct(symbol=symbol, data=etf_struct)
237
317
 
238
- # 初始化HTF趋势相关变量
239
-
240
- etf_trend = etf_latest_struct[self.STRUCT_DIRECTION_COL]
241
- #FIXME etf_trend
242
- etf_trend = atf_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
- setp = "3.1"
248
- self.logger.info(f"{symbol} : {setp}. ETF {etf} Price's Current Trend is {etf_trend}。")
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
- setp = "3.2"
251
- self.logger.info(f"{symbol} : {setp}. ETF {etf} struct is {etf_latest_struct[self.STRUCT_COL]}。")
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
- setp = "3.3"
334
+ step = "3.3"
256
335
 
257
- if atf_trend != etf_trend:
336
+ if atf_side != etf_side:
258
337
 
259
- self.logger.info(f"{symbol} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}未反转,等待...")
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} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}已反转。")
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
- setp = "3.4"
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 etf_trend == self.BEARISH_TREND else etf_pdArrays_df[self.PD_MID_COL] <= etf_struct_mid
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} : {setp}.1. ETF {etf} 未找到PDArray,不下单")
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} : {setp}.1. ETF {etf} 找到PDArray.\n"
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} : {setp}.2. ETF {etf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
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} : {setp}.2. ETF {etf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
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
- setp = "3.5"
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} : {setp}. ETF {etf}, 下单价格 {order_price:.{precision}} 未变化,不进行下单。")
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} : {setp}. ETF {etf}, {etf_side} 价格={order_price:.{precision}}")
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
- self.logger.error(error_message,exc_info=True)
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.debug(f"{symbol} : 触发{direction}下单条件。")
195
+ self.logger.info(f"{symbol} : 触发{direction}下单条件. 下单价格: {price}")
187
196
 
188
197
  # 执行下单
189
- self.exchange.place_order(
190
- symbol=symbol,
191
- price=price,
192
- amount_usdt=order_amount_usdt,
193
- side=side,
194
- leverage=leverage,
195
- order_type=order_type
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
- self.exchange.cancel_all_orders(symbol=symbol)
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 get_lastest_OB(self, symbol, data, trend, start_index=-1) -> dict:
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.get_lastest_OB(data=data, trend=trend, start_index=start_index)
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 get_last_struct(self, symbol, data) -> dict:
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.get_last_struct(data)
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 check_position(self, symbol) -> bool:
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 False
461
+ return True
413
462
 
414
463
  @abstractmethod
415
464
  def process_pair(self, symbol, pair_config):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openfund-maker
3
- Version: 2.3.1
3
+ Version: 2.3.2
4
4
  Summary: Openfund-maker.
5
5
  Requires-Python: >=3.9,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,8 +1,8 @@
1
1
  maker/BestFVGStrategyMaker.py,sha256=a9UfClrfzkgX6jXL2FODzANtawrmGeZ_PVeO1-tweDc,12532
2
- maker/BestTopDownStrategyMaker.py,sha256=w3g9U6sQO5ybY8dx7F6ojsWjXbQ06WANa9M_z1045jA,18009
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=2nF5TPqHUIAVo3qK2AyZvfsWCrhDNtwULPJ2bIrOb-g,16550
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.1.dist-info/METADATA,sha256=hIo1FIfLMrxBxds9aEpfrW1yI3iuNLZZAkBVXll3kvc,2001
15
- openfund_maker-2.3.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
16
- openfund_maker-2.3.1.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
17
- openfund_maker-2.3.1.dist-info/RECORD,,
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,,