openfund-maker 2.3.1__py3-none-any.whl → 2.3.3__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:
@@ -104,7 +103,7 @@ class BestTopDownStrategyMaker(StrategyMaker):
104
103
  step = "1.4"
105
104
  # 计算支撑位和阻力位之间的利润空间百分比
106
105
  htf_profit_percent = abs((htf_resistance_price - htf_support_price) / htf_support_price * 100)
107
- min_profit_percent = pair_config.get('min_profit_percent', 4) # 默认最小利润空间为0.5%
106
+ min_profit_percent = top_down_strategy.get('min_profit_percent', 4) # 默认最小利润空间为0.5%
108
107
 
109
108
  if htf_profit_percent < min_profit_percent:
110
109
  self.logger.info(f"{symbol} : {step}. HTF {htf} 支撑位={htf_support_price:.{precision}f} 与阻力位={htf_resistance_price:.{precision}f} 之间利润空间{htf_profit_percent:.2f}% < {min_profit_percent}%,等待...")
@@ -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,81 @@ 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
-
158
-
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
+ if top_down_strategy.get('open_check_liquidity_areas', True):
213
+ 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]
214
+ last_EQ = self.find_EQH_EQL(symbol=symbol, data=atf_df, trend=atf_trend, end_idx=end_idx, pair_config=pair_config)
215
+ if last_EQ and last_EQ[self.HAS_EQ_KEY]:
216
+ price_eq = last_EQ[self.EQUAL_HIGH_COL] if atf_side == self.BUY_SIDE else last_EQ[self.EQUAL_LOW_COL]
217
+ self.logger.info(f"{symbol} : {step}.1 ATF {atf} {atf_side} find EQ {price_eq}")
218
+ # 检查是否Liquidity Sweeps
219
+ if (atf_side == self.BUY_SIDE and atf_latest_struct[self.STRUCT_HIGH_COL] > price_eq) \
220
+ or (atf_side == self.SELL_SIDE and atf_latest_struct[self.STRUCT_LOW_COL] < price_eq):
221
+
222
+ atf_side = self.SELL_SIDE if atf_side == self.BUY_SIDE else self.BUY_SIDE
223
+ self.logger.info(f"{symbol} : {step}.1 ATF {atf} Liquidity Sweeps , Reverse the ATF {atf} {atf_side} side。")
224
+ else:
225
+ self.logger.info(f"{symbol} : {step}.1 ATF {atf} is not found Liquidity Sweeps .")
226
+ else:
227
+ self.logger.info(f"{symbol} : {step}.1 ATF {atf} is not found EQ .")
228
+
229
+ # FIXME 2.5.2. Dynamic Trendlines and Channels
230
+ if top_down_strategy.get('open_check_dynamic_trendlines_and_channels', True):
231
+ atf_pre_struct = atf_struct[atf_struct[self.STRUCT_DIRECTION_COL].notna()].iloc[-2] # 看前一个结构是否为动态趋势
232
+ atf_start_index = min(atf_pre_struct[self.STRUCT_LOW_INDEX_COL] ,atf_pre_struct[self.STRUCT_HIGH_INDEX_COL])
233
+ atf_end_index = max(atf_latest_struct[self.STRUCT_LOW_INDEX_COL] ,atf_latest_struct[self.STRUCT_HIGH_INDEX_COL])
234
+
235
+ 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)
236
+ if is_dynamic_trendlines :
237
+ self.logger.info(f"{symbol} : {step}.2 ATF {atf} {atf_trend} find Dynamic Trendlines .")
238
+ else:
239
+ self.logger.info(f"{symbol} : {step}.2 ATF {atf} {atf_trend} not find Dynamic Trendlines .")
240
+
159
241
 
160
242
  # 2.6. 在HTF供需区范围,找ATF的PDArray,FVG和OB,供需区,计算监测下单区域范围。
161
- setp = "2.6"
243
+ step = "2.6"
162
244
  atf_pdArrays_df = self.find_PDArrays(symbol=symbol,struct=atf_struct,side=atf_side)
163
245
 
164
246
  # 不同的结构,不同位置,如果是Choch则等待价格进入PDArray,如果是BOS则等待价格进入折价区
@@ -170,150 +252,150 @@ class BestTopDownStrategyMaker(StrategyMaker):
170
252
  if "CHOCH" in atf_struct[self.STRUCT_COL]:
171
253
  # 找PDArray,Bullish 则PDArray的mid要小于 atf_struct_mid,Bearish 则PDArray的mid要大于 atf_struct_mid
172
254
  # 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
255
+ 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
256
  atf_pdArrays_df = atf_pdArrays_df[mask]
175
257
  if len(atf_pdArrays_df) == 0:
176
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} 未找到PDArray,不下单")
258
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} 未找到PDArray,不下单")
177
259
  return
178
260
  else:
179
261
  # 找到最新的PDArray
180
262
  atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
181
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} 找到PDArray\n"
263
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
182
264
  f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
183
265
 
184
266
 
185
267
  #SMS
186
268
  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
269
+ 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
270
  atf_pdArrays_df = atf_pdArrays_df[mask]
189
271
  if len(atf_pdArrays_df) == 0:
190
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} 在{atf_struct_mid:.{precision}f}未找到PDArray,不下单")
272
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} 在{atf_struct_mid:.{precision}f}未找到PDArray,不下单")
191
273
  return
192
274
  else:
193
275
  # 找到最新的PDArray
194
276
  atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
195
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} 找到PDArray\n"
277
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
196
278
  f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
197
279
 
198
280
 
199
281
  #BMS
200
282
  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
283
+ atf_premium_mid = (atf_struct_mid + atf_struct_low) / 2 if atf_side == self.BUY_SIDE else (atf_struct_mid + atf_struct_high) / 2
284
+ 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
285
  atf_pdArrays_df = atf_pdArrays_df[mask]
204
286
  if len(atf_pdArrays_df) == 0:
205
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} ,在{atf_premium_mid:.{precision}f}未找到PDArray,不下单")
287
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} ,在{atf_premium_mid:.{precision}f}未找到PDArray,不下单")
206
288
  return
207
289
  else:
208
290
  # 找到最新的PDArray
209
291
  atf_vaild_pdArray = atf_pdArrays_df.iloc[-1]
210
- self.logger.info(f"{symbol} : {setp}.1. ATF {atf} 找到PDArray\n"
292
+ self.logger.info(f"{symbol} : {step}.1. ATF {atf} 找到PDArray\n"
211
293
  f"{atf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}")
212
294
 
213
295
 
214
296
 
215
- setp = "2.7"
297
+ step = "2.7"
216
298
 
217
299
  # 2.7. 等待价格进入 PDArray
218
300
 
219
301
  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范围。"
302
+ self.logger.info(f"{symbol} : {step}. ATF {atf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
221
303
  f"PD_HIGH={atf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f} "
222
304
  f"PD_LOW={atf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} ")
223
305
 
224
306
  return
225
307
  else:
226
- self.logger.info(f"{symbol} : {setp}. ATF {atf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
308
+ self.logger.info(f"{symbol} : {step}. ATF {atf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
227
309
  f"PD_HIGH={atf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f} "
228
310
  f"PD_LOW={atf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} ")
229
311
 
230
312
 
231
313
  # 3. ETF Step
232
-
314
+ step = "3.1"
233
315
  etf_side, etf_struct, etf_trend = None, None, None
234
316
  etf_df = self.get_historical_klines_df(symbol=symbol, tf=etf)
235
317
  etf_struct =self.build_struct(symbol=symbol, data=etf_df)
236
- etf_latest_struct = self.get_last_struct(symbol=symbol, data=etf_struct)
318
+ etf_latest_struct = self.get_latest_struct(symbol=symbol, data=etf_struct)
237
319
 
238
- # 初始化HTF趋势相关变量
239
-
240
- etf_trend = etf_latest_struct[self.STRUCT_DIRECTION_COL]
241
- #FIXME etf_trend
242
- etf_trend = atf_trend
243
-
320
+ # 初始化ETF趋势相关变量
321
+ if etf_latest_struct is None:
322
+ self.logger.info(f"{symbol} : {step}. ETF {etf} 未形成结构,等待... ")
323
+ return
324
+ etf_trend = etf_latest_struct[self.STRUCT_DIRECTION_COL]
244
325
  etf_side = self.BUY_SIDE if etf_trend == self.BULLISH_TREND else self.SELL_SIDE
245
326
 
246
327
  # 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}。")
328
+ step = "3.1"
329
+ self.logger.info(f"{symbol} : {step}. ETF {etf} Price's Current Trend is {etf_trend}。")
249
330
  # 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]}。")
331
+ step = "3.2"
332
+ self.logger.info(f"{symbol} : {step}. ETF {etf} struct is {etf_latest_struct[self.STRUCT_COL]}。")
252
333
 
253
334
 
254
335
  # 3.3 Reversal Signs 反转信号
255
- setp = "3.3"
336
+ step = "3.3"
256
337
 
257
- if atf_trend != etf_trend:
338
+ if atf_side != etf_side:
258
339
 
259
- self.logger.info(f"{symbol} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}未反转,等待...")
340
+ self.logger.info(f"{symbol} : {step}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}未反转,等待...")
260
341
  return
261
342
  else:
262
- self.logger.info(f"{symbol} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}已反转。")
343
+ self.logger.info(f"{symbol} : {step}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}已反转。")
263
344
 
264
345
  # TODO "CHOCH"|"BOS" 的PDArray 入场位置不一样
265
346
 
266
347
  # 3.4 找 PD Arrays 价格区间(ETF 看上下的供需区位置)
267
- setp = "3.4"
348
+ step = "3.4"
268
349
  etf_pdArrays_df = self.find_PDArrays(symbol=symbol,struct=etf_struct,side=etf_side)
269
350
  # 划分 折价(discount)区和溢价(premium)区
270
351
  etf_struct_high = etf_latest_struct[self.STRUCT_HIGH_COL]
271
352
  etf_struct_low = etf_latest_struct[self.STRUCT_LOW_COL]
272
353
  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
354
+ 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
355
  etf_pdArrays_df = etf_pdArrays_df[mask]
275
356
  if len(etf_pdArrays_df) == 0:
276
- self.logger.info(f"{symbol} : {setp}.1. ETF {etf} 未找到PDArray,不下单")
357
+ self.logger.info(f"{symbol} : {step}.1. ETF {etf} 未找到PDArray,不下单")
277
358
  return
278
359
  else:
279
360
  # 找到最新的PDArray
280
361
  etf_vaild_pdArray = etf_pdArrays_df.iloc[-1]
281
- self.logger.info(f"{symbol} : {setp}.1. ETF {etf} 找到PDArray.\n"
362
+ self.logger.info(f"{symbol} : {step}.1. ETF {etf} 找到PDArray.\n"
282
363
  f"{etf_vaild_pdArray[[self.TIMESTAMP_COL,self.PD_TYPE_COL,self.PD_HIGH_COL,self.PD_LOW_COL,self.PD_MID_COL]]}。")
283
364
 
284
365
 
285
366
  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范围。"
367
+ self.logger.info(f"{symbol} : {step}.2. ETF {etf} market_price={market_price:.{precision}f} 未达到PDArray范围。"
287
368
  f"PD_HIGH={etf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} "
288
369
  f"PD_LOW={etf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f}")
289
370
 
290
371
  return
291
372
  else:
292
- self.logger.info(f"{symbol} : {setp}.2. ETF {etf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
373
+ self.logger.info(f"{symbol} : {step}.2. ETF {etf} market_price={market_price:.{precision}f} 已到达PDArray范围。"
293
374
  f"PD_HIGH={etf_vaild_pdArray[self.PD_HIGH_COL]:.{precision}f} "
294
375
  f"PD_LOW={etf_vaild_pdArray[self.PD_LOW_COL]:.{precision}f}")
295
376
 
296
377
  # 3.5 Place Order 下单
297
- setp = "3.5"
378
+ step = "3.5"
298
379
  # 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
380
  order_price = self.toDecimal(etf_vaild_pdArray[self.PD_MID_COL])
300
381
 
301
382
  latest_order_price = self.toDecimal(self.place_order_prices.get(symbol,0))
302
383
  if order_price == latest_order_price:
303
- self.logger.info(f"{symbol} : {setp}. ETF {etf}, 下单价格 {order_price:.{precision}} 未变化,不进行下单。")
384
+ self.logger.info(f"{symbol} : {step}. ETF {etf}, 下单价格 {order_price:.{precision}} 未变化,不进行下单。")
304
385
  return
305
386
 
306
387
  self.cancel_all_orders(symbol=symbol)
307
388
  self.place_order(symbol=symbol, price=order_price, side=etf_side, pair_config=pair_config)
308
389
  self.place_order_prices[symbol] = order_price # 记录下单价格,过滤重复下单
309
- self.logger.info(f"{symbol} : {setp}. ETF {etf}, {etf_side} 价格={order_price:.{precision}}")
390
+ self.logger.info(f"{symbol} : {step}. ETF {etf}, {etf_side} 价格={order_price:.{precision}}")
310
391
 
311
392
 
312
393
  except KeyboardInterrupt:
313
394
  self.logger.info("程序收到中断信号,开始退出...")
314
395
  except Exception as e:
315
396
  error_message = f"程序异常退出: {str(e)}"
316
- self.logger.error(error_message,exc_info=True)
397
+ # 记录错误信息和堆栈跟踪
398
+ self.logger.error(f"{error_message}\n{traceback.format_exc()}")
317
399
  traceback.print_exc()
318
400
  self.send_feishu_notification(symbol, error_message)
319
401
  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.3
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=HGG4883WTDBc3PPHNtWAAR4cjtg6r7a7QNv-JLBVOU0,23981
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.3.dist-info/METADATA,sha256=0ZvMTnyUA47auoEj2OmQ4gDGOWDk1PbQEQJ2Wp17XNs,2001
15
+ openfund_maker-2.3.3.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
16
+ openfund_maker-2.3.3.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
17
+ openfund_maker-2.3.3.dist-info/RECORD,,