openfund-maker 2.0.3__py3-none-any.whl → 2.0.5__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.
@@ -15,9 +15,8 @@ class BestFVGStrategyMaker(SMCStrategyMaker):
15
15
  """
16
16
  检查最大或最小价格是否在FVG范围内
17
17
  Args:
18
- htf_last_side: str, 方向 'buy' or 'sell'
19
- fvg_top: float, FVG上边界
20
- fvg_bot: float, FVG下边界
18
+ side: str, 方向 'buy' or 'sell'
19
+ fvg: Dic, FVG
21
20
  Returns:
22
21
  bool: 是否在FVG范围内
23
22
  """
@@ -62,91 +61,99 @@ class BestFVGStrategyMaker(SMCStrategyMaker):
62
61
 
63
62
  smc_strategy = pair_config.get('smc_strategy',{})
64
63
 
65
- # 获取历史K线,HTF和CTF
64
+ """
65
+ 获取策略配置
66
+ """
66
67
  htf = str(smc_strategy.get('HTF','15m'))
67
68
  ltf = str(smc_strategy.get('LTF', '1m'))
69
+ htf_prd = int(smc_strategy.get('HTF_swing_points_length',15))
70
+ ltf_prd = int(smc_strategy.get('LTF_swing_points_length',3))
71
+ enable_FVG = bool(smc_strategy.get('enable_FVG',True)) # 是否启用FVG
72
+ enable_OB = bool(smc_strategy.get('enable_OB',True))# 是否启用OB
73
+ self.logger.info(f"{symbol} : BestFVGSMC策略 {ltf}|{htf} \nenable_FVG={enable_FVG} enable_OB={enable_OB} \nHTF_swing_points_length={htf_prd} LTF_swing_points_length={ltf_prd}")
74
+
68
75
  htf_Klines = self.get_historical_klines(symbol=symbol, bar=htf)
69
- htf_df = self.format_klines(htf_Klines)
70
-
71
- enable_FVG = smc_strategy.get('enable_FVG',True) # 是否启用FVG
72
- enable_OB = smc_strategy.get('enable_OB',True) # 是否启用OB
73
- self.logger.debug(f"{symbol} : BestFVGSMC策略 {ltf}|{htf} enable_FVG={enable_FVG} enable_OB={enable_OB} ...")
76
+ htf_df = self.format_klines(htf_Klines)
77
+
74
78
 
75
79
  # 初始化HTF趋势相关变量
76
- htf_last_side, htf_last_CHoCH_label = None, None
77
-
80
+ htf_side, htf_last_CHoCH_label = None, None
81
+ htf_struct = {"struct": "None"}
78
82
  # 检查是否有上一个CHoCH结构
79
83
  htf_last_CHoCH = self.htf_last_CHoCH.get(symbol,None)
80
-
84
+ htf_struct = self.detect_struct(htf_df, prd=htf_prd, struct_key="CHoCH") # HTF结构要严谨,prd周期要长一些
85
+
81
86
  # 如果存在上一个CHoCH结构,更新趋势标签和方向
82
87
  if htf_last_CHoCH:
83
88
  htf_last_CHoCH_label = htf_last_CHoCH["struct"]
84
- htf_last_side = htf_last_CHoCH["side"]
85
-
89
+ # htf_side = htf_last_CHoCH["side"]
86
90
 
87
- # 1. HTF 判断struct趋势(CHoCH\SMS\BMS) ,HTF struct 看趋势,CTF 看FVG和OB的位置
88
- swing_points_length = smc_strategy.get('swing_points_length',10)
89
- htf_struct = self.detect_struct(htf_df,prd=swing_points_length,struct_key="CHoCH")
90
- htf_struct_label = htf_struct["struct"]
91
- htf_last_pivot_high = htf_struct["pivot_high"]
92
- htf_last_pivot_low = htf_struct["pivot_low"]
93
- htf_last_mid_line = self.calculate_ce(symbol,htf_last_pivot_high,htf_last_pivot_low)
94
-
91
+ # 优化: 只在没有新CHoCH结构时使用上一个结构
92
+ if htf_struct["struct"] == "None":
93
+ htf_struct = htf_last_CHoCH
94
+ self.logger.debug(f"{symbol} : {htf} 使用之前 CHoCH struct。{htf_struct['struct']} prd={htf_prd}。")
95
+ else:
96
+ self.logger.debug(f"{symbol} : {htf} 形成新的 CHoCH struct。{htf_struct['struct']} prd={htf_prd}。")
95
97
 
96
- # 检查是否已形成CHoCH结构
97
- if not (htf_last_CHoCH or 'CHoCH' in htf_struct_label):
98
- self.logger.debug(f"{symbol} : {htf} 未形成 CHoCH struct,不下单。{htf_struct}。")
99
- return
100
-
101
- # 更新最新的CHoCH结构信息
98
+ # 检查是否已形成有效的CHoCH结构
99
+ htf_struct_label = htf_struct["struct"]
100
+ htf_side = htf_struct["side"]
101
+ if htf_struct_label == "None" and not htf_last_CHoCH:
102
+ self.logger.debug(f"{symbol} : {htf} 未形成有效的 CHoCH struct,不下单。{htf_struct}。")
103
+ return
104
+
105
+ # 更新最新的CHoCH结构信息
102
106
  if 'CHoCH' in htf_struct_label and htf_struct_label != htf_last_CHoCH_label:
103
107
  self.htf_last_CHoCH[symbol] = htf_struct
104
108
  htf_last_CHoCH = htf_struct
105
109
  htf_last_CHoCH_label = htf_struct_label
106
- htf_last_side = htf_struct["side"]
107
-
108
-
109
- # 2. HTF 获取最新的两个极值点,设置折价(discount)区和溢价(premium)区
110
+
110
111
 
111
-
112
+ # 1. HTF 判断struct趋势(CHoCH\SMS\BMS) ,HTF struct 看趋势,CTF 看FVG和OB的位置
113
+ htf_pivot_high = htf_struct["pivot_high"]
114
+ htf_pivot_low = htf_struct["pivot_low"]
115
+ htf_mid_line = self.calculate_ce(symbol,htf_pivot_high,htf_pivot_low)
116
+
117
+ # 2. HTF 获取最新的两个极值点,设置折价(discount)区和溢价(premium)区
112
118
  # 计算溢价和折价区
113
119
  premium_box = {
114
- 'top': htf_last_pivot_high,
115
- 'bot': htf_last_mid_line,
116
- 'ce': self.calculate_ce(symbol,htf_last_pivot_high,htf_last_mid_line)
120
+ 'top': htf_pivot_high,
121
+ 'bot': htf_mid_line,
122
+ 'ce': self.calculate_ce(symbol,htf_pivot_high,htf_mid_line)
117
123
  }
118
124
  discount_box = {
119
- 'top': htf_last_mid_line,
120
- 'bot': htf_last_pivot_low,
121
- 'ce': self.calculate_ce(symbol,htf_last_mid_line,htf_last_pivot_low)
125
+ 'top': htf_mid_line,
126
+ 'bot': htf_pivot_low,
127
+ 'ce': self.calculate_ce(symbol,htf_mid_line,htf_pivot_low)
122
128
  }
123
129
 
124
130
  self.logger.info(f"{symbol} : {htf} 趋势={htf_last_CHoCH_label}")
125
- self.logger.debug(f"{symbol} : \npivot_high={htf_last_pivot_high} pivot_low={htf_last_pivot_low} mid_line={htf_last_mid_line}\n溢价区={premium_box}\n折价区={discount_box}")
131
+ self.logger.debug(f"{symbol} : \npivot_high={htf_pivot_high} pivot_low={htf_pivot_low} mid_line={htf_mid_line}\n溢价区={premium_box}\n折价区={discount_box}")
126
132
 
127
133
  # 3. find HTF FVG
128
- pivot_index = htf_struct["pivot_low_index"] if htf_last_side == "buy" else htf_struct["pivot_high_index"]
129
- htf_fvg_boxes = self.find_fvg_boxes(htf_df,side=htf_last_side,threshold=htf_last_mid_line,check_balanced=False,pivot_index=pivot_index)
134
+ pivot_index = htf_struct["pivot_low_index"] if htf_side == "buy" else htf_struct["pivot_high_index"]
135
+ # TODO 优化: 缓存FVG,不用每次都计算,且被平衡
136
+ htf_fvg_boxes = self.find_fvg_boxes(htf_df,side=htf_side,threshold=htf_mid_line,check_balanced=False,pivot_index=pivot_index)
130
137
  if len(htf_fvg_boxes) == 0:
131
- self.logger.debug(f"{symbol} : HTF={htf} 方向={htf_last_side}, 未找到 FVG")
138
+ self.logger.debug(f"{symbol} : HTF={htf} 方向={htf_side}, 未找到 FVG")
132
139
  return
133
140
  self.logger.debug(f"{symbol} : HTF_fvg_box={htf_fvg_boxes[-1]}")
134
141
 
135
142
  # 判断是否进入最近的FVG
136
- if_tap_into_fvg = self.check_price_in_fvg(htf_df,htf_last_side,htf_fvg_boxes[-1])
143
+ if_tap_into_fvg = self.check_price_in_fvg(htf_df,htf_side,htf_fvg_boxes[-1])
137
144
  if not if_tap_into_fvg:
138
- self.logger.debug(f"{symbol} : 价格[未进入]HTF_FVG区域,不进行下单")
145
+ self.logger.debug(f"{symbol} : 价格[未进入] HTF_FVG区域,不进行下单")
139
146
  return
140
147
  else:
141
- self.logger.debug(f"{symbol} : 价格[进入]HTF_FVG区域,开始下单。fvgbox={htf_fvg_boxes[-1]}")
148
+ self.logger.debug(f"{symbol} : 价格[进入] HTF_FVG区域,开始下单。fvgbox={htf_fvg_boxes[-1]}")
142
149
 
143
150
  # 4. LTF 判断struct趋势是否有CHoCH
144
151
 
145
152
 
146
153
  ltf_kLines = self.get_historical_klines(symbol=symbol, bar=ltf)
147
154
  ltf_df = self.format_klines(ltf_kLines)
148
-
149
- ltf_struct = self.detect_struct(ltf_df,prd=swing_points_length)
155
+
156
+ ltf_struct = self.detect_struct(ltf_df,prd=ltf_prd)
150
157
  ltf_struct_label = ltf_struct["struct"]
151
158
  ltf_struct_side = ltf_struct["side"]
152
159
  ltf_last_pivot_high = ltf_struct["pivot_high"]
@@ -171,8 +178,8 @@ class BestFVGStrategyMaker(SMCStrategyMaker):
171
178
 
172
179
  # 5. LTF 寻找FVG,下单
173
180
  # if htf_last_CHoCH_label != ltf_struct_label :
174
- if htf_last_side != ltf_struct_side :
175
- self.logger.debug(f"{symbol} : {htf} {htf_last_CHoCH_label} VS {ltf} {ltf_struct_label} 趋势不一致,不进行下单")
181
+ if ltf_struct_label == "None" or htf_side != ltf_struct_side :
182
+ self.logger.debug(f"{symbol} : {htf} {htf_last_CHoCH_label} VS {ltf} {ltf_struct_label} 趋势不一致{htf_side}|{ltf_struct_side},不进行下单")
176
183
  return
177
184
 
178
185
  threshold = 0.0
maker/SMCStrategyMaker.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import traceback
3
3
  import pandas as pd
4
- import talib as ta
5
4
 
6
5
  from maker.ThreeLineStrategyMaker import ThreeLineStrategyMaker
7
6
 
@@ -179,32 +178,158 @@ class SMCStrategyMaker(ThreeLineStrategyMaker):
179
178
  return fvg_boxes
180
179
 
181
180
 
182
- def detect_struct(self, data, prd=10, struct_key=None, check_bounds=True, global_extremum=False, s1=True, resp=7) -> dict:
183
- """_summary_
184
- 识别SMC结构,参考 Tradingview Smart Money Concepts Probability (Expo)@Openfund
181
+ # def detect_struct(self, data, prd=10, struct_key=None, check_valid_range=True, check_bounds=True, global_extremum=False, s1=True, resp=7) -> dict:
182
+ # """_summary_
183
+ # 识别SMC结构,参考 Tradingview Smart Money Concepts Probability (Expo)@Openfund
185
184
 
186
- Args:
187
- data (df): df格式的K线数据
188
- prd (int): 计算Swing Points的bar数量
189
- struct_key (str): 结构类型,如 'CHoCH'|'SMS'|'BMS'
190
- check_bounds (bool): 计算Swing Points是否检查边界,默认为True
191
- global_extremum (bool): 是否使用全局极值点,默认为False
192
- s1 (bool): 结构响应布尔值
193
- resp (int): 响应周期
194
- Returns:
195
- dict: 包含结构识别结果的字典,包含以下字段:
196
- "struct": 结构类型,如 'Bullish_CHoCH'|'Bullish_SMS'|'Bullish_BMS'|'Bearish_CHoCH'|'Bearish_SMS'|'Bearish_BMS'
197
- "index": 结构出现的位置索引
198
- "pivot_high": 枢轴高点价格
199
- "pivot_high_index": 枢轴高点索引
200
- "pivot_low": 枢轴低点价格
201
- "pivot_low_index": 枢轴低点索引
202
- "side": 交易方向,'buy'或'sell'
185
+ # Args:
186
+ # data (df): df格式的K线数据
187
+ # prd (int): 计算Swing Points的bar数量
188
+ # struct_key (str): 结构类型,如 'CHoCH'|'SMS'|'BMS'
189
+ # check_valid_range (bool): 结构类型在 pivot_high_index 和 pivot_low_index 之间为有效范围内,默认为False
190
+ # check_bounds (bool): 计算Swing Points是否检查边界,默认为True
191
+ # global_extremum (bool): 是否使用全局极值点,默认为False
192
+ # s1 (bool): 结构响应布尔值
193
+ # resp (int): 响应周期
194
+ # Returns:
195
+ # dict: 包含结构识别结果的字典,包含以下字段:
196
+ # "struct": 结构类型,如 'Bullish_CHoCH'|'Bullish_SMS'|'Bullish_BMS'|'Bearish_CHoCH'|'Bearish_SMS'|'Bearish_BMS'
197
+ # "index": 结构出现的位置索引
198
+ # "pivot_high": 枢轴高点价格
199
+ # "pivot_high_index": 枢轴高点索引
200
+ # "pivot_low": 枢轴低点价格
201
+ # "pivot_low_index": 枢轴低点索引
202
+ # "side": 交易方向,'buy'或'sell'
203
203
 
204
204
 
205
- """
205
+ # """
206
+
207
+ # # data = data.copy()
208
+ # data['Up'] = None
209
+ # data['Dn'] = None
210
+ # data['iUp'] = None
211
+ # data['iDn'] = None
212
+ # data['pos'] = 0
213
+ # data['pattern'] = None
214
+
215
+ # # 初始化 Up 和 Dn 的第一个值
216
+ # data.at[0, 'Up'] = data.at[0, 'high']
217
+ # data.at[0, 'Dn'] = data.at[0, 'low']
218
+
219
+
220
+ # for index in range(1, len(data)):
221
+
222
+ # data.at[index, 'Up'] = max(data.at[index - 1, 'Up'], data.at[index, 'high'])
223
+ # data.at[index, 'Dn'] = min(data.at[index - 1, 'Dn'], data.at[index, 'low'])
224
+ # data.at[index, 'pos'] = data.at[index - 1, 'pos']
225
+ # data.at[index, 'iUp'] = data.at[max(0,index - 1), 'iUp'] if data.at[max(0,index - 1), 'iUp'] is not None else index
226
+ # data.at[index, 'iDn'] = data.at[max(0,index - 1), 'iDn'] if data.at[max(0,index - 1), 'iDn'] is not None else index
227
+
228
+ # # 寻找枢轴高点和低点
229
+ # pvtHi = self.is_pivot_high(data, index, prd, check_bounds)
230
+ # pvtLo = self.is_pivot_low(data, index, prd, check_bounds)
231
+
232
+ # if pvtHi:
233
+ # data.at[index, 'Up'] = data.at[index, 'high']
234
+ # data.at[index, 'iUp'] = index
235
+ # if pvtLo:
236
+ # data.at[index, 'Dn'] = data.at[index, 'low']
237
+ # data.at[index, 'iDn'] = index
238
+ # # 寻找Bullish结构
239
+ # if data.at[index, 'Up'] > data.at[index - 1, 'Up']:
240
+
241
+ # data.at[index, 'iUp'] = index
242
+ # if data.at[index - 1, 'pos'] <= 0:
243
+ # # data.at[index, 'pattern'] = 'CHoCH (Bullish)'
244
+ # data.at[index, 'pattern'] = 'Bullish_CHoCH'
245
+ # data.at[index, 'pos'] = 1
246
+ # elif data.at[index - 1, 'pos'] == 1 \
247
+ # and data.at[index - 1, 'Up'] == data.at[max(0,index - (resp if s1 else prd)), 'Up']:
248
+ # data.at[index, 'pattern'] = 'Bullish_SMS'
249
+ # data.at[index, 'pos'] = 2
250
+
251
+ # elif data.at[index - 1, 'pos'] > 1 \
252
+ # and data.at[index - 1, 'Up'] == data.at[max(0,index - (resp if s1 else prd)), 'Up']:
253
+ # data.at[index, 'pattern'] = 'Bullish_BMS'
254
+ # data.at[index, 'pos'] = data.at[index - 1, 'pos'] + 1
255
+
256
+ # elif global_extremum and data.at[index, 'Up'] < data.at[index - 1, 'Up']:
257
+ # data.at[index, 'iUp'] = data.at[index - 1, 'iUp']
258
+
259
+ # # # 寻找Bearish结构
260
+ # if data.at[index, 'Dn'] < data.at[index - 1, 'Dn']:
261
+ # data.at[index, 'iDn'] = index
262
+ # if data.at[index - 1, 'pos'] >= 0:
263
+
264
+ # data.at[index, 'pattern'] = 'Bearish_CHoCH'
265
+ # data.at[index, 'pos'] = -1
266
+ # elif data.at[index - 1, 'pos'] == -1 \
267
+ # and data.at[index - 1, 'Dn'] == data.at[max(0,index - (resp if s1 else prd)), 'Dn']:
268
+ # data.at[index, 'pattern'] = 'Bearish_SMS'
269
+ # data.at[index, 'pos'] = -2
270
+ # elif data.at[index - 1, 'pos'] < -1 \
271
+ # and data.at[index - 1, 'Dn'] == data.at[max(0,index - (resp if s1 else prd)), 'Dn']:
272
+ # data.at[index, 'pattern'] = 'Bearish_BMS'
273
+ # data.at[index, 'pos'] = data.at[index - 1, 'pos'] - 1
274
+
275
+ # elif global_extremum and data.at[index, 'Dn'] > data.at[index - 1, 'Dn']:
276
+ # data.at[index, 'iDn'] = data.at[index - 1, 'iDn']
277
+
278
+ # # 获取最后一个结构和位置
279
+ # last_struct = {
280
+ # "struct": None,
281
+ # "index": -1,
282
+ # "pivot_high": None,
283
+ # "pivot_high_index": -1,
284
+ # "pivot_low": None,
285
+ # "pivot_low_index": -1,
286
+ # "side": None
287
+
288
+ # }
289
+
290
+
291
+ # for i in range(len(data)-1, -1, -1):
292
+ # if check_valid_range:
293
+ # # 检查是否在pivot_high_index和pivot_low_index之间的有效范围内
294
+ # if data.at[i, 'iUp'] != -1 and data.at[i, 'iDn'] != -1:
295
+ # pivot_high_index = data.at[i, 'iUp']
296
+ # pivot_low_index = data.at[i, 'iDn']
297
+ # if i < min(pivot_high_index, pivot_low_index) or i > max(pivot_high_index, pivot_low_index):
298
+ # continue
299
+
300
+ # if data.at[i, 'pattern'] is not None:
301
+ # if struct_key is not None and struct_key not in data.at[i, 'pattern']:
302
+ # continue
303
+ # last_struct["struct"] = data.at[i, 'pattern']
304
+ # last_struct["index"] = i
206
305
 
207
- # data = data.copy()
306
+ # break
307
+
308
+ # if last_struct['struct'] is not None :
309
+ # # 找到最后一个结构的枢轴高点和低点,如果当前是孤立点,则取前一个孤立点
310
+ # # 判断交易方向
311
+ # if 'Bearish' in last_struct["struct"]:
312
+ # last_struct["side"] = 'sell'
313
+ # else :
314
+ # last_struct["side"] = 'buy'
315
+
316
+ # last_struct["pivot_high_index"] = int(data["iUp"].iloc[-1])
317
+ # last_struct["pivot_low_index"] = int(data["iDn"].iloc[-1])
318
+
319
+ # last_struct["pivot_high"] = float(data.loc[last_struct["pivot_high_index"], 'high'])
320
+ # last_struct["pivot_low"] = float(data.loc[last_struct["pivot_low_index"], 'low'])
321
+ # else:
322
+ # last_struct['struct'] = "None"
323
+ # last_struct["index"] = -1
324
+
325
+
326
+ # return last_struct
327
+ def build_struct(self, df, prd=20, check_bounds=True, global_extremum=False) :
328
+
329
+ """_summary_
330
+ 构建SMC结构,参考 Tradingview Smart Money Concepts Probability (Expo)@Openfund
331
+ """
332
+ data = df.copy()
208
333
  data['Up'] = None
209
334
  data['Dn'] = None
210
335
  data['iUp'] = None
@@ -237,19 +362,18 @@ class SMCStrategyMaker(ThreeLineStrategyMaker):
237
362
  data.at[index, 'iDn'] = index
238
363
  # 寻找Bullish结构
239
364
  if data.at[index, 'Up'] > data.at[index - 1, 'Up']:
240
-
241
- data.at[index, 'iUp'] = index
365
+ data.at[index, 'iUp'] = index # TODO
242
366
  if data.at[index - 1, 'pos'] <= 0:
243
367
  # data.at[index, 'pattern'] = 'CHoCH (Bullish)'
244
368
  data.at[index, 'pattern'] = 'Bullish_CHoCH'
245
369
  data.at[index, 'pos'] = 1
246
370
  elif data.at[index - 1, 'pos'] == 1 \
247
- and data.at[index - 1, 'Up'] == data.at[max(0,index - (resp if s1 else prd)), 'Up']:
371
+ and data.at[index - 1, 'Up'] == data.at[max(0,index - prd), 'Up']:
248
372
  data.at[index, 'pattern'] = 'Bullish_SMS'
249
373
  data.at[index, 'pos'] = 2
250
374
 
251
375
  elif data.at[index - 1, 'pos'] > 1 \
252
- and data.at[index - 1, 'Up'] == data.at[max(0,index - (resp if s1 else prd)), 'Up']:
376
+ and data.at[index - 1, 'Up'] == data.at[max(0,index - prd), 'Up']:
253
377
  data.at[index, 'pattern'] = 'Bullish_BMS'
254
378
  data.at[index, 'pos'] = data.at[index - 1, 'pos'] + 1
255
379
 
@@ -258,23 +382,51 @@ class SMCStrategyMaker(ThreeLineStrategyMaker):
258
382
 
259
383
  # # 寻找Bearish结构
260
384
  if data.at[index, 'Dn'] < data.at[index - 1, 'Dn']:
261
- data.at[index, 'iDn'] = index
385
+ data.at[index, 'iDn'] = index # TODO
262
386
  if data.at[index - 1, 'pos'] >= 0:
263
387
 
264
388
  data.at[index, 'pattern'] = 'Bearish_CHoCH'
265
389
  data.at[index, 'pos'] = -1
266
390
  elif data.at[index - 1, 'pos'] == -1 \
267
- and data.at[index - 1, 'Dn'] == data.at[max(0,index - (resp if s1 else prd)), 'Dn']:
391
+ and data.at[index - 1, 'Dn'] == data.at[max(0,index - prd), 'Dn']:
268
392
  data.at[index, 'pattern'] = 'Bearish_SMS'
269
393
  data.at[index, 'pos'] = -2
270
394
  elif data.at[index - 1, 'pos'] < -1 \
271
- and data.at[index - 1, 'Dn'] == data.at[max(0,index - (resp if s1 else prd)), 'Dn']:
395
+ and data.at[index - 1, 'Dn'] == data.at[max(0,index - prd), 'Dn']:
272
396
  data.at[index, 'pattern'] = 'Bearish_BMS'
273
397
  data.at[index, 'pos'] = data.at[index - 1, 'pos'] - 1
274
398
 
275
399
  elif global_extremum and data.at[index, 'Dn'] > data.at[index - 1, 'Dn']:
276
400
  data.at[index, 'iDn'] = data.at[index - 1, 'iDn']
277
401
 
402
+ return data
403
+
404
+ def detect_struct(self, data, prd=20, check_valid_range=True, struct_key=None, check_bounds=True, global_extremum=False) -> dict:
405
+ """_summary_
406
+ 识别SMC结构,参考 Tradingview Smart Money Concepts Probability (Expo)@Openfund
407
+
408
+ Args:
409
+ data (df): df格式的K线数据
410
+ prd (int): 计算Swing Points的bar数量
411
+ struct_key (str): 结构类型,如 'CHoCH'|'SMS'|'BMS'
412
+ check_valid_range (bool): 结构类型在 pivot_high_index 和 pivot_low_index 之间为有效范围内,默认为False
413
+ check_bounds (bool): 计算Swing Points是否检查边界,默认为True
414
+ global_extremum (bool): 是否使用全局极值点,默认为False
415
+ s1 (bool): 结构响应布尔值
416
+ resp (int): 响应周期
417
+ Returns:
418
+ dict: 包含结构识别结果的字典,包含以下字段:
419
+ "struct": 结构类型,如 'Bullish_CHoCH'|'Bullish_SMS'|'Bullish_BMS'|'Bearish_CHoCH'|'Bearish_SMS'|'Bearish_BMS'
420
+ "index": 结构出现的位置索引
421
+ "pivot_high": 枢轴高点价格
422
+ "pivot_high_index": 枢轴高点索引
423
+ "pivot_low": 枢轴低点价格
424
+ "pivot_low_index": 枢轴低点索引
425
+ "side": 交易方向,'buy'或'sell'
426
+ """
427
+ data = self.build_struct(data, prd, check_bounds, global_extremum)
428
+
429
+
278
430
  # 获取最后一个结构和位置
279
431
  last_struct = {
280
432
  "struct": None,
@@ -287,8 +439,21 @@ class SMCStrategyMaker(ThreeLineStrategyMaker):
287
439
 
288
440
  }
289
441
 
442
+ pivot_high_index = last_struct["pivot_high_index"] = int(data["iUp"].iloc[-1])
443
+ pivot_low_index = last_struct["pivot_low_index"] = int(data["iDn"].iloc[-1])
444
+
445
+ last_struct["pivot_high"] = float(data.loc[last_struct["pivot_high_index"], 'high'])
446
+ last_struct["pivot_low"] = float(data.loc[last_struct["pivot_low_index"], 'low'])
290
447
 
291
448
  for i in range(len(data)-1, -1, -1):
449
+ if check_valid_range:
450
+ # 检查是否在pivot_high_index和pivot_low_index之间的有效范围内
451
+ if data.at[i, 'iUp'] != -1 and data.at[i, 'iDn'] != -1:
452
+ # pivot_high_index = data.at[i, 'iUp']
453
+ # pivot_low_index = data.at[i, 'iDn']
454
+ if i < min(pivot_high_index, pivot_low_index) or i > max(pivot_high_index, pivot_low_index):
455
+ continue
456
+
292
457
  if data.at[i, 'pattern'] is not None:
293
458
  if struct_key is not None and struct_key not in data.at[i, 'pattern']:
294
459
  continue
@@ -304,24 +469,13 @@ class SMCStrategyMaker(ThreeLineStrategyMaker):
304
469
  last_struct["side"] = 'sell'
305
470
  else :
306
471
  last_struct["side"] = 'buy'
307
-
308
- last_struct["pivot_high_index"] = int(data["iUp"].iloc[-1])
309
- last_struct["pivot_low_index"] = int(data["iDn"].iloc[-1])
310
-
311
- last_struct["pivot_high"] = float(data.loc[last_struct["pivot_high_index"], 'high'])
312
- last_struct["pivot_low"] = float(data.loc[last_struct["pivot_low_index"], 'low'])
313
-
314
-
315
-
316
- # last_struct["pivot_high_index"] = int(data.loc[data.index > last_struct["index"], 'high'].idxmax())
317
- # last_struct["pivot_high"] = float(data.loc[data.index > last_struct["index"], 'high'].max())
318
- # last_struct["pivot_low_index"] = int(data.loc[data.index > last_struct["index"], 'low'].idxmin())
319
- # last_struct["pivot_low"] = float(data.loc[data.index > last_struct["index"], 'low'].min())
472
+ else:
473
+ last_struct['struct'] = 'None'
474
+ last_struct["index"] = -1
475
+
320
476
 
321
477
  return last_struct
322
-
323
-
324
-
478
+
325
479
 
326
480
  def is_pivot_high(self, data, index, period, check_bounds=False):
327
481
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openfund-maker
3
- Version: 2.0.3
3
+ Version: 2.0.5
4
4
  Summary: Openfund-maker.
5
5
  Requires-Python: >=3.9,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,6 +1,6 @@
1
- maker/BestFVGStrategyMaker.py,sha256=FQOk2IK8JXsFTsXMZU5Qage5YqDyVBPDwTdWAuABBx8,10489
1
+ maker/BestFVGStrategyMaker.py,sha256=7SK3ysHfG46KLxIaG7YGB-3rxrtQ6tCkCgyJqEMwv_Q,11164
2
2
  maker/MACDStrategyMaker.py,sha256=iS5HO04piKHFJxUI2e5QmicxzGeK-V1aphJSr2n_4Ac,12651
3
- maker/SMCStrategyMaker.py,sha256=LUm9HFX5S_OKesynPyf4SBsfGnK0awPeChpHWOfD8VM,26957
3
+ maker/SMCStrategyMaker.py,sha256=gXhYs-QvKBZ-mdR4UN2EF7OwFGI5JyW2-0RJLXq7SSc,34695
4
4
  maker/ThreeLineStrategyMaker.py,sha256=ArjnHlGECiD3cCFXxO0Ex5scR2agwoxZY-4mKukyKc4,30402
5
5
  maker/WickReversalStrategyMaker.py,sha256=7DqPDVJot4EM0_lSAcFAHrR9rNvkIds9KLMoDOiAHEc,17486
6
6
  maker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10,7 +10,7 @@ maker/main_m.py,sha256=0PzDTnuBrxfpy5WDfsIHKAzZ_7pkuvuqqeWik0vpWio,15522
10
10
  maker/okxapi.py,sha256=_9G0U_o0ZC8NxaT6PqpiLgxBm9gPobC9PsFHZE1c5w0,553
11
11
  maker/zhen.py.bak,sha256=HNkrQbJts8G9umE9chEFsc0cLQApcM9KOVNMYPpkBXM,10918
12
12
  maker/zhen_2.py,sha256=4IaHVtTCMSlrLGSTZrWpW2q-f7HZsUNRkW_-5QgWv24,10509
13
- openfund_maker-2.0.3.dist-info/METADATA,sha256=sdZ7cSb8_uzPwvHBUmJ-BvJvFIdvaufpr1igCBdEYJ0,1953
14
- openfund_maker-2.0.3.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
15
- openfund_maker-2.0.3.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
16
- openfund_maker-2.0.3.dist-info/RECORD,,
13
+ openfund_maker-2.0.5.dist-info/METADATA,sha256=VWMVbIbwCfmZKpeb6E5Fx1ECLMbjkpa_hbS2t6DChGg,1953
14
+ openfund_maker-2.0.5.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
15
+ openfund_maker-2.0.5.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
16
+ openfund_maker-2.0.5.dist-info/RECORD,,