openfund-maker 2.0.3__tar.gz → 2.0.4__tar.gz
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.
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/PKG-INFO +1 -1
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/pyproject.toml +2 -2
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/BestFVGStrategyMaker.py +57 -50
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/SMCStrategyMaker.py +200 -46
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/README.md +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/MACDStrategyMaker.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/ThreeLineStrategyMaker.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/WickReversalStrategyMaker.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/__init__.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/config.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/main.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/main_m.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/okxapi.py +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/zhen.py.bak +0 -0
- {openfund_maker-2.0.3 → openfund_maker-2.0.4}/src/maker/zhen_2.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "openfund-maker"
|
3
|
-
version = "2.0.
|
3
|
+
version = "2.0.4"
|
4
4
|
description = "Openfund-maker."
|
5
5
|
authors = []
|
6
6
|
readme = "README.md"
|
@@ -21,7 +21,7 @@ apscheduler = "^3.11.0"
|
|
21
21
|
pytest = "^7.4.4"
|
22
22
|
pytest-mock = "^3.14.0"
|
23
23
|
pytest-asyncio = "^0.23.3"
|
24
|
-
|
24
|
+
pytest-cov = "^6.1.1"
|
25
25
|
|
26
26
|
[[tool.poetry.source]]
|
27
27
|
name = "mirrors"
|
@@ -15,9 +15,8 @@ class BestFVGStrategyMaker(SMCStrategyMaker):
|
|
15
15
|
"""
|
16
16
|
检查最大或最小价格是否在FVG范围内
|
17
17
|
Args:
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
85
|
-
|
89
|
+
# htf_side = htf_last_CHoCH["side"]
|
86
90
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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':
|
115
|
-
'bot':
|
116
|
-
'ce': self.calculate_ce(symbol,
|
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':
|
120
|
-
'bot':
|
121
|
-
'ce': self.calculate_ce(symbol,
|
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={
|
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
|
129
|
-
|
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} 方向={
|
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,
|
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=
|
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
|
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
|
@@ -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
|
-
|
184
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
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 -
|
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 -
|
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 -
|
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 -
|
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[
|
309
|
-
last_struct["
|
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
|
"""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|