openfund-taker 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.
- {openfund_taker-2.3.1.dist-info → openfund_taker-2.3.3.dist-info}/METADATA +1 -1
- {openfund_taker-2.3.1.dist-info → openfund_taker-2.3.3.dist-info}/RECORD +7 -7
- taker/BestTopDownStrategyTaker.py +161 -17
- taker/StrategyTaker.py +114 -79
- taker/main.py +2 -4
- {openfund_taker-2.3.1.dist-info → openfund_taker-2.3.3.dist-info}/WHEEL +0 -0
- {openfund_taker-2.3.1.dist-info → openfund_taker-2.3.3.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
taker/BestTopDownStrategyTaker.py,sha256=
|
1
|
+
taker/BestTopDownStrategyTaker.py,sha256=nWFgmLvQGCOzNVU7vlHnSM67BMTotUf-BMZZeZxYv34,11607
|
2
2
|
taker/SMCSLAndTPTaker.py,sha256=zJCtwyzIuDtbxMdLOwRbpUE60o9fKe-tNWuiuej4RGs,27621
|
3
|
-
taker/StrategyTaker.py,sha256=
|
3
|
+
taker/StrategyTaker.py,sha256=VCRc-QmhYAODfnc7yG6EpfilO2A-11L7dKQSkHykTuU,27686
|
4
4
|
taker/ThreeLineTradingTaker.py,sha256=NGEXd4baCfzF4ToGb9tQWXRMweFBpImyh2v4WcKLK3A,20580
|
5
5
|
taker/TrailingSLAndTPTaker.py,sha256=W-kj0BhPXqusO8z4zlDU_nb1L6L7TT8jEhCSgMkEF9U,2831
|
6
6
|
taker/TrailingSLTaker.py,sha256=diRfcIS71prbm1I4wYgwVUq3c2_ftntiC0tQ5byWeHQ,53465
|
@@ -11,8 +11,8 @@ taker/history_code/chua_ok.py,sha256=5pPAoEYbFuKxfZwqNvOO890s-2cy6n69QiI0ZA0GTCQ
|
|
11
11
|
taker/history_code/chua_ok_all.py,sha256=2XnZM6QdB3juSE1pqQIJyh2x1XuhlTlnBKNA3owlJ9E,15267
|
12
12
|
taker/history_code/chua_ok_bot.py,sha256=9SW0ujhi6PfN4yR1JZ9NaA37HtnXJ2QAWUfW52NG68w,13109
|
13
13
|
taker/history_code/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
|
14
|
-
taker/main.py,sha256=
|
15
|
-
openfund_taker-2.3.
|
16
|
-
openfund_taker-2.3.
|
17
|
-
openfund_taker-2.3.
|
18
|
-
openfund_taker-2.3.
|
14
|
+
taker/main.py,sha256=3ebFQDeKR4qF2pmOszK1UXx2XZFUHaaq86MUfJwVfvw,2103
|
15
|
+
openfund_taker-2.3.3.dist-info/METADATA,sha256=aDGmIW7nHEAFMOtrovr_B1SboYcPf-MMw0FpF_pb4Kg,7575
|
16
|
+
openfund_taker-2.3.3.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
17
|
+
openfund_taker-2.3.3.dist-info/entry_points.txt,sha256=a7mG8F7aOA5-Gk2vPWuAR4537faxaHUgM_jwIDBZoEc,50
|
18
|
+
openfund_taker-2.3.3.dist-info/RECORD,,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from re import A
|
1
2
|
from typing import override
|
2
3
|
|
3
4
|
from taker.StrategyTaker import StrategyTaker
|
@@ -6,18 +7,21 @@ class BestTopDownStrategyTaker(StrategyTaker):
|
|
6
7
|
"""
|
7
8
|
最佳顶底策略
|
8
9
|
"""
|
9
|
-
def __init__(self, g_config, platform_config, common_config,
|
10
|
-
super().__init__(g_config=g_config, platform_config=platform_config, common_config=common_config,
|
10
|
+
def __init__(self, g_config, platform_config, common_config, logger=None, exchangeKey='okx') -> None:
|
11
|
+
super().__init__(g_config=g_config, platform_config=platform_config, common_config=common_config, logger=logger, exchangeKey=exchangeKey)
|
11
12
|
self.has_init_SL_TPs = {}
|
13
|
+
self.entering_channel_tps = {} # 进入止盈监控
|
12
14
|
|
13
15
|
@override
|
14
|
-
def
|
15
|
-
super().
|
16
|
+
def reset_SL_TP(self, symbol=None, attachType='BOTH'):
|
17
|
+
super().reset_SL_TP(symbol, attachType)
|
16
18
|
if not symbol :
|
17
19
|
self.has_init_SL_TPs.clear()
|
20
|
+
self.entering_channel_tps.clear()
|
18
21
|
|
19
|
-
elif symbol in self.has_init_SL_TPs:
|
22
|
+
elif attachType==self.BOTH_KEY and symbol in self.has_init_SL_TPs:
|
20
23
|
del self.has_init_SL_TPs[symbol]
|
24
|
+
del self.entering_channel_tps[symbol]
|
21
25
|
|
22
26
|
def init_SL_TP(self, symbol: str, position, tfs: dict, strategy: dict) -> bool:
|
23
27
|
"""
|
@@ -26,48 +30,170 @@ class BestTopDownStrategyTaker(StrategyTaker):
|
|
26
30
|
|
27
31
|
precision = self.get_precision_length(symbol)
|
28
32
|
# htf = tfs[self.HTF_KEY]
|
29
|
-
|
33
|
+
|
30
34
|
atf = tfs[self.ATF_KEY]
|
31
35
|
|
32
36
|
# 1.1 ATF Key Support & Resistance Levels 支撑或阻力关键位置(ATF 看上下的供需区位置)
|
33
|
-
atf_df = self.
|
37
|
+
atf_df = self.get_historical_klines_df(symbol=symbol, tf=atf)
|
34
38
|
atf_struct =self.build_struct(symbol=symbol, data=atf_df)
|
35
39
|
atf_OBs_df = self.find_OBs(symbol=symbol,struct=atf_struct)
|
40
|
+
pos_side = position[self.SIDE_KEY]
|
41
|
+
side = self.SELL_SIDE if pos_side == self.SHORT_KEY else self.BUY_SIDE
|
42
|
+
|
43
|
+
entering_trigger_price = 0.0 # 进入止盈监控的触发价格
|
36
44
|
|
37
|
-
atf_support_OB = self.
|
45
|
+
atf_support_OB = self.get_latest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BULLISH_TREND)
|
38
46
|
if atf_support_OB :
|
39
47
|
atf_support_price = atf_support_OB.get(self.OB_MID_COL)
|
48
|
+
if pos_side == self.SHORT_KEY:
|
49
|
+
entering_trigger_price = atf_support_OB.get(self.OB_LOW_COL)
|
40
50
|
else:
|
41
51
|
atf_support_price = atf_struct.at[atf_struct.index[-1], self.STRUCT_LOW_COL]
|
42
52
|
|
43
|
-
atf_resistance_OB = self.
|
53
|
+
atf_resistance_OB = self.get_latest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BEARISH_TREND)
|
54
|
+
|
44
55
|
if atf_resistance_OB :
|
45
56
|
atf_resistance_price = atf_resistance_OB.get(self.OB_MID_COL)
|
57
|
+
if pos_side == self.LONG_KEY:
|
58
|
+
entering_trigger_price = atf_resistance_OB.get(self.OB_HIGH_COL)
|
46
59
|
else:
|
47
60
|
atf_resistance_price = atf_struct.at[atf_struct.index[-1], self.STRUCT_HIGH_COL]
|
61
|
+
|
48
62
|
self.logger.info(f"{symbol} : ATF {atf}, Key Support={atf_support_price:.{precision}f} "
|
49
63
|
f"& Key Resistance={atf_resistance_price:.{precision}f} ")
|
50
64
|
|
51
|
-
|
65
|
+
etf = tfs[self.ETF_KEY]
|
66
|
+
etf_df = self.get_historical_klines_df_by_cache(symbol=symbol, tf=etf)
|
67
|
+
# etf_struct =self.build_struct(symbol=symbol, data=etf_df)
|
68
|
+
etf_latest_struct = self.get_latest_struct(symbol=symbol, data=etf_df)
|
69
|
+
|
70
|
+
|
52
71
|
tick_size = self.get_tick_size(symbol)
|
53
72
|
offset = strategy.get('offset',1) # 价格偏移量, 1 代表 1 tick , 0.000001 代表 1 p
|
54
73
|
price_offset = offset * tick_size
|
55
74
|
|
56
75
|
if side == self.BUY_SIDE:
|
57
|
-
sl_price = self.toDecimal(atf_support_price) - self.toDecimal(price_offset)
|
76
|
+
# sl_price = self.toDecimal(atf_support_price) - self.toDecimal(price_offset)
|
77
|
+
# 20250610 止损价格设置ETF的结构的最低价格
|
78
|
+
sl_price = self.toDecimal(etf_latest_struct[self.STRUCT_LOW_COL]) - self.toDecimal(price_offset)
|
58
79
|
tp_price = self.toDecimal(atf_resistance_price) + self.toDecimal(price_offset)
|
59
80
|
else:
|
60
|
-
sl_price = self.toDecimal(atf_resistance_price) + self.toDecimal(price_offset)
|
81
|
+
# sl_price = self.toDecimal(atf_resistance_price) + self.toDecimal(price_offset)
|
82
|
+
# 20250610 止损价格设置ETF的结构的最高价格
|
83
|
+
sl_price = self.toDecimal(etf_latest_struct[self.STRUCT_HIGH_COL]) + self.toDecimal(price_offset)
|
61
84
|
tp_price = self.toDecimal(atf_support_price) - self.toDecimal(price_offset)
|
62
85
|
|
63
|
-
self.cancel_all_algo_orders(symbol=symbol, attachType=
|
86
|
+
self.cancel_all_algo_orders(symbol=symbol, attachType=self.SL_KEY)
|
64
87
|
self.set_stop_loss(symbol=symbol, position=position, sl_price=sl_price)
|
65
|
-
self.cancel_all_algo_orders(symbol=symbol, attachType=
|
88
|
+
self.cancel_all_algo_orders(symbol=symbol, attachType=self.TP_KEY)
|
66
89
|
self.set_take_profit(symbol=symbol, position=position, tp_price=tp_price)
|
67
|
-
|
68
90
|
|
91
|
+
if entering_trigger_price > 0:
|
92
|
+
self.entering_channel_tps[symbol] = entering_trigger_price
|
69
93
|
return True
|
70
94
|
|
95
|
+
def check_TP(self, symbol: str, position, tfs, strategy: dict) -> bool:
|
96
|
+
"""
|
97
|
+
监控TP,在ETF下Bullish进入BOT或Bearish进入TOP开始监测反转结构止盈
|
98
|
+
"""
|
99
|
+
if_pass = False
|
100
|
+
|
101
|
+
if symbol not in self.entering_channel_tps:
|
102
|
+
return if_pass
|
103
|
+
|
104
|
+
entering_trigger_price = self.entering_channel_tps[symbol]
|
105
|
+
marker_price = position[self.MARK_PRICE_KEY]
|
106
|
+
mask = marker_price >= entering_trigger_price if position[self.SIDE_KEY] == self.LONG_KEY else marker_price <= entering_trigger_price
|
107
|
+
|
108
|
+
if not mask:
|
109
|
+
return if_pass
|
110
|
+
|
111
|
+
precision = self.get_precision_length(symbol)
|
112
|
+
self.logger.info(f"{symbol} : 进入止盈监控,当前价格{marker_price:.{precision}f},触发价格{entering_trigger_price:.{precision}f}")
|
113
|
+
|
114
|
+
# 1.2 检查反转结构
|
115
|
+
etf = tfs[self.ETF_KEY]
|
116
|
+
etf_side, etf_struct, etf_trend = None, None, None
|
117
|
+
etf_df = self.get_historical_klines_df(symbol=symbol, tf=etf)
|
118
|
+
etf_struct =self.build_struct(symbol=symbol, data=etf_df)
|
119
|
+
etf_latest_struct = self.get_latest_struct(symbol=symbol, data=etf_struct)
|
120
|
+
|
121
|
+
etf_trend = etf_latest_struct[self.STRUCT_DIRECTION_COL]
|
122
|
+
|
123
|
+
setp = "1.2.1"
|
124
|
+
self.logger.info(f"{symbol} : {setp}. ETF {etf} Price's Current Trend is {etf_trend}。")
|
125
|
+
# 3.2. Who's In Control 供需控制,Bullish 或者 Bearish | Choch 或者 BOS
|
126
|
+
setp = "1.2.2"
|
127
|
+
self.logger.info(f"{symbol} : {setp}. ETF {etf} struct is {etf_latest_struct[self.STRUCT_COL]}。")
|
128
|
+
setp = "1.2.3"
|
129
|
+
pos_trend = self.BULLISH_TREND if position[self.SIDE_KEY] == self.LONG_KEY else self.BEARISH_TREND
|
130
|
+
|
131
|
+
if pos_trend != etf_trend:
|
132
|
+
|
133
|
+
self.logger.info(f"{symbol} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}未反转,等待...")
|
134
|
+
return if_pass
|
135
|
+
else:
|
136
|
+
self.logger.info(f"{symbol} : {setp}. ETF {etf} 市场结构{etf_latest_struct[self.STRUCT_COL]}已反转。")
|
137
|
+
if_pass = True
|
138
|
+
|
139
|
+
|
140
|
+
return if_pass
|
141
|
+
|
142
|
+
def trailing_SL(self, symbol: str, position, tfs, strategy: dict) :
|
143
|
+
"""
|
144
|
+
移动止损
|
145
|
+
:param symbol: 交易对
|
146
|
+
:param position: 仓位
|
147
|
+
:param tfs: 时间周期
|
148
|
+
:param strategy: 策略配置
|
149
|
+
:return:
|
150
|
+
"""
|
151
|
+
|
152
|
+
precision = self.get_precision_length(symbol)
|
153
|
+
atf = tfs[self.ATF_KEY]
|
154
|
+
atf_side = position[self.SIDE_KEY]
|
155
|
+
market_price = position[self.MARK_PRICE_KEY]
|
156
|
+
|
157
|
+
# 获取ATF K线数据
|
158
|
+
atf_df = self.get_historical_klines_df_by_cache(symbol=symbol, tf=atf)
|
159
|
+
|
160
|
+
# 获取最新的PDArray
|
161
|
+
atf_struct = self.build_struct(symbol=symbol, data=atf_df)
|
162
|
+
pdArray_side = self.BUY_SIDE if atf_side == self.SELL_SIDE else self.SELL_SIDE
|
163
|
+
atf_PDArrays_df = self.find_PDArrays(symbol=symbol, struct=atf_struct, balanced=True)
|
164
|
+
mask = atf_PDArrays_df[self.PD_HIGH_COL] < market_price if atf_side == self.BUY_SIDE else atf_PDArrays_df[self.PD_LOW_COL] > market_price
|
165
|
+
atf_PDArrays_df = atf_PDArrays_df[mask]
|
166
|
+
atf_latest_crossed_PD = self.get_latest_PDArray(symbol=symbol, data=atf_PDArrays_df, side=pdArray_side)
|
167
|
+
if atf_latest_crossed_PD is None:
|
168
|
+
self.logger.info(f"{symbol} : ATF {atf} 未找到最新被平衡过的PDArray。")
|
169
|
+
return
|
170
|
+
|
171
|
+
|
172
|
+
# 根据方向找到对应的ce价格
|
173
|
+
pos_side = position[self.SIDE_KEY]
|
174
|
+
tick_size = self.get_tick_size(symbol)
|
175
|
+
offset = strategy.get('offset', 1)
|
176
|
+
price_offset = offset * tick_size
|
177
|
+
|
178
|
+
latest_sl_price = self.get_stop_loss_price(symbol)
|
179
|
+
if pos_side == self.LONG_KEY:
|
180
|
+
# 多头找下方PDArray的ce
|
181
|
+
sl_price = atf_latest_crossed_PD[self.PD_MID_COL] - self.toDecimal(price_offset)
|
182
|
+
mask = sl_price > 0 and sl_price < position[self.MARK_PRICE_KEY] and (latest_sl_price is None or sl_price > latest_sl_price)
|
183
|
+
else:
|
184
|
+
# 空头找上方PDArray的ce
|
185
|
+
sl_price = atf_latest_crossed_PD[self.PD_MID_COL] + self.toDecimal(price_offset)
|
186
|
+
mask = sl_price > 0 and sl_price > position[self.MARK_PRICE_KEY] and (latest_sl_price is None or sl_price < latest_sl_price)
|
187
|
+
|
188
|
+
|
189
|
+
if mask:
|
190
|
+
|
191
|
+
self.logger.info(f"{symbol} : ATF {atf} {pos_side} 重置SL价格 = {sl_price:.{precision}f}")
|
192
|
+
self.cancel_all_algo_orders(symbol=symbol, attachType=self.SL_KEY)
|
193
|
+
self.set_stop_loss(symbol=symbol, position=position, sl_price=sl_price)
|
194
|
+
else:
|
195
|
+
self.logger.debug(f"{symbol} : ATF {atf} {pos_side} 未重置SL。SL价格 = {sl_price:.{precision}f} "
|
196
|
+
f"最新价格 = {position[self.MARK_PRICE_KEY]:.{precision}f} last_sl={latest_sl_price:.{precision}f}")
|
71
197
|
@override
|
72
198
|
def process_pair(self, symbol: str, position, pair_config: dict) -> None:
|
73
199
|
"""
|
@@ -97,8 +223,26 @@ class BestTopDownStrategyTaker(StrategyTaker):
|
|
97
223
|
# 1.1 初始化止盈止损
|
98
224
|
if symbol not in self.has_init_SL_TPs:
|
99
225
|
has_pass = self.init_SL_TP(symbol, position, tfs, top_down_strategy)
|
226
|
+
|
100
227
|
if has_pass:
|
101
228
|
self.has_init_SL_TPs[symbol] = True
|
229
|
+
self.logger.debug(f"{symbol} : 初始化止盈止损成功。 {has_pass}。")
|
230
|
+
else:
|
231
|
+
self.logger.info(f"{symbol} : 初始化止盈止损失败。 {has_pass}。")
|
102
232
|
|
103
|
-
|
104
|
-
|
233
|
+
# 1.2 移动止损
|
234
|
+
open_trailing_SL = top_down_strategy.get('open_trailing_SL', False)
|
235
|
+
if open_trailing_SL:
|
236
|
+
self.logger.debug(f"{symbol} : 开启移动止损。")
|
237
|
+
self.trailing_SL(symbol, position, tfs, top_down_strategy)
|
238
|
+
|
239
|
+
# 1.3 监测止盈
|
240
|
+
open_check_TP = top_down_strategy.get('open_check_TP', True)
|
241
|
+
if open_check_TP:
|
242
|
+
if not self.check_TP(symbol, position, tfs, top_down_strategy):
|
243
|
+
self.logger.debug(f"{symbol} : 未触发止盈监控,等待...")
|
244
|
+
else:
|
245
|
+
order = self.close_position(symbol, position)
|
246
|
+
if order:
|
247
|
+
self.logger.info(f"{symbol} : 已触发止盈监控,市价{position[self.MARK_PRICE_KEY]:.{precision}f}平仓。")
|
248
|
+
|
taker/StrategyTaker.py
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
import traceback
|
3
2
|
import pandas as pd
|
4
3
|
import time
|
5
4
|
from functools import lru_cache
|
6
|
-
from datetime import datetime
|
5
|
+
from datetime import datetime
|
7
6
|
from decimal import Decimal
|
8
7
|
from abc import abstractmethod
|
9
|
-
|
8
|
+
|
10
9
|
from core.utils.OPTools import OPTools
|
11
10
|
from core.Exchange import Exchange
|
12
11
|
# 导入SMC相关模块
|
@@ -25,6 +24,10 @@ class StrategyTaker():
|
|
25
24
|
SHORT_KEY = 'short'
|
26
25
|
SIDE_KEY = 'side'
|
27
26
|
SYMBOL_KEY = 'symbol'
|
27
|
+
SL_KEY = "SL"
|
28
|
+
TP_KEY = "TP"
|
29
|
+
BOTH_KEY = "BOTH"
|
30
|
+
|
28
31
|
ENTRY_PRICE_KEY = 'entryPrice'
|
29
32
|
MARK_PRICE_KEY = 'markPrice'
|
30
33
|
CONTRACTS_KEY = 'contracts'
|
@@ -51,6 +54,12 @@ class StrategyTaker():
|
|
51
54
|
HIGH_START_COL = SMCStruct.SMCStruct.HIGH_START_COL
|
52
55
|
LOW_START_COL = SMCStruct.SMCStruct.LOW_START_COL
|
53
56
|
|
57
|
+
FVG_TOP_COL = SMCFVG.SMCFVG.FVG_TOP
|
58
|
+
FVG_BOT_COL = SMCFVG.SMCFVG.FVG_BOT
|
59
|
+
FVG_MID_COL = SMCFVG.SMCFVG.FVG_MID
|
60
|
+
FVG_SIDE_COL = SMCFVG.SMCFVG.FVG_SIDE
|
61
|
+
FVG_WAS_BALANCED = SMCFVG.SMCFVG.FVG_WAS_BALANCED
|
62
|
+
|
54
63
|
OB_HIGH_COL = SMCOrderBlock.SMCOrderBlock.OB_HIGH_COL
|
55
64
|
OB_LOW_COL = SMCOrderBlock.SMCOrderBlock.OB_LOW_COL
|
56
65
|
OB_MID_COL = SMCOrderBlock.SMCOrderBlock.OB_MID_COL
|
@@ -65,7 +74,7 @@ class StrategyTaker():
|
|
65
74
|
PD_MID_COL = SMCPDArray.SMCPDArray.PD_MID_COL
|
66
75
|
PD_TYPE_COL = SMCPDArray.SMCPDArray.PD_TYPE_COL
|
67
76
|
|
68
|
-
def __init__(self, g_config, platform_config, common_config,
|
77
|
+
def __init__(self, g_config, platform_config, common_config, logger=None ,exchangeKey='okx'):
|
69
78
|
"""_summary_
|
70
79
|
初始化
|
71
80
|
Args:
|
@@ -79,7 +88,7 @@ class StrategyTaker():
|
|
79
88
|
self.g_config = g_config
|
80
89
|
|
81
90
|
self.common_config = common_config
|
82
|
-
self.monitor_interval = monitor_interval
|
91
|
+
self.monitor_interval = common_config.get('monitor_interval', 10)
|
83
92
|
self.feishu_webhook = self.common_config.get('feishu_webhook',"")
|
84
93
|
|
85
94
|
self.strategy_config = self.g_config.get('strategy', {})
|
@@ -135,6 +144,7 @@ class StrategyTaker():
|
|
135
144
|
"""
|
136
145
|
tick_size = self.exchange.get_tick_size(symbol)
|
137
146
|
return self.smcStruct.get_precision_length(tick_size)
|
147
|
+
|
138
148
|
def toDecimal(self, price):
|
139
149
|
"""_summary_
|
140
150
|
将价格转换为Decimal类型
|
@@ -144,6 +154,7 @@ class StrategyTaker():
|
|
144
154
|
_type_: _description_
|
145
155
|
"""
|
146
156
|
return OPTools.toDecimal(price)
|
157
|
+
|
147
158
|
def format_price(self, symbol, price:Decimal) -> str:
|
148
159
|
precision = self.get_precision_length(symbol)
|
149
160
|
return f"{price:.{precision}f}"
|
@@ -157,8 +168,7 @@ class StrategyTaker():
|
|
157
168
|
**self.strategy_config, # 基础配置
|
158
169
|
**pair_config # 交易对特定配置会覆盖基础配置
|
159
170
|
}
|
160
|
-
return pair_config
|
161
|
-
|
171
|
+
return pair_config
|
162
172
|
|
163
173
|
def send_feishu_notification(self, symbol, message):
|
164
174
|
if self.feishu_webhook:
|
@@ -187,47 +197,61 @@ class StrategyTaker():
|
|
187
197
|
"""
|
188
198
|
return self.exchange.get_market_price(symbol)
|
189
199
|
|
190
|
-
def
|
200
|
+
def close_position(self, symbol, position, params={}) -> dict:
|
191
201
|
"""_summary_
|
192
|
-
|
202
|
+
平仓
|
193
203
|
Args:
|
194
204
|
symbol (_type_): _description_
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
205
|
+
position (_type_): _description_
|
206
|
+
params (_type_, optional): _description_. Defaults to {}.
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
_type_: _description_
|
199
210
|
"""
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# 记录下单日志
|
211
|
-
direction = self.BULLISH_TREND if side == self.BUY_SIDE else self.BEARISH_TREND
|
212
|
-
self.logger.debug(f"{symbol} : 触发{direction}下单条件。")
|
213
|
-
|
214
|
-
# 执行下单
|
215
|
-
self.exchange.place_order(
|
216
|
-
symbol=symbol,
|
217
|
-
price=price,
|
218
|
-
amount_usdt=order_amount_usdt,
|
219
|
-
side=side,
|
220
|
-
leverage=leverage,
|
221
|
-
order_type=order_type
|
222
|
-
)
|
223
|
-
|
211
|
+
try:
|
212
|
+
order = self.exchange.close_position(symbol=symbol, position=position, params=params)
|
213
|
+
return order
|
214
|
+
except Exception as e:
|
215
|
+
error_message = f"{symbol} 平仓失败: {e}"
|
216
|
+
self.logger.error(error_message)
|
217
|
+
self.send_feishu_notification(symbol, error_message)
|
218
|
+
return None
|
219
|
+
|
224
220
|
def cancel_all_orders(self, symbol):
|
225
221
|
"""_summary_
|
226
222
|
取消所有挂单
|
227
223
|
Args:
|
228
224
|
symbol (_type_): _description_
|
229
225
|
"""
|
230
|
-
|
226
|
+
|
227
|
+
try:
|
228
|
+
self.exchange.cancel_all_orders(symbol=symbol)
|
229
|
+
except Exception as e:
|
230
|
+
error_message = f"{symbol} 取消所有挂单失败: {e}"
|
231
|
+
self.logger.warning(error_message)
|
232
|
+
self.send_feishu_notification(symbol, error_message)
|
233
|
+
|
234
|
+
def cancel_all_algo_orders(self, symbol, attachType: str = None):
|
235
|
+
"""取消止盈止损单
|
236
|
+
Args:
|
237
|
+
symbol: 交易对
|
238
|
+
attachType: 订单类型,'SL'表示止损单,'TP'表示止盈单,None表示不不区分
|
239
|
+
"""
|
240
|
+
try:
|
241
|
+
self.exchange.cancel_all_algo_orders(symbol=symbol, attachType=attachType)
|
242
|
+
except Exception as e:
|
243
|
+
error_message = f"{symbol} 取消止盈止损单失败: {e}"
|
244
|
+
self.logger.warning(error_message)
|
245
|
+
self.send_feishu_notification(symbol, error_message)
|
246
|
+
return
|
247
|
+
|
248
|
+
if attachType == self.SL_KEY and symbol in self.stop_loss_prices:
|
249
|
+
del self.stop_loss_prices[symbol]
|
250
|
+
elif attachType == self.TP_KEY and symbol in self.take_profit_prices:
|
251
|
+
del self.take_profit_prices[symbol]
|
252
|
+
else:
|
253
|
+
self.reset_SL_TP(symbol, attachType)
|
254
|
+
|
231
255
|
def get_historical_klines(self, symbol, tf='15m'):
|
232
256
|
"""_summary_
|
233
257
|
获取历史K线数据
|
@@ -243,6 +267,7 @@ class StrategyTaker():
|
|
243
267
|
def _get_cache_historical_klines_df(self, symbol, tf):
|
244
268
|
"""被缓存的获取K线数据的方法"""
|
245
269
|
return self.get_historical_klines_df(symbol, tf)
|
270
|
+
|
246
271
|
def clear_cache_historical_klines_df(self, symbol=None):
|
247
272
|
"""
|
248
273
|
清除指定交易对和时间周期的缓存
|
@@ -264,8 +289,7 @@ class StrategyTaker():
|
|
264
289
|
del self.cache_time[k]
|
265
290
|
# 由于lru_cache无法单独清除特定键,这里只能清除所有缓存
|
266
291
|
self._get_cache_historical_klines_df.cache_clear()
|
267
|
-
|
268
|
-
|
292
|
+
|
269
293
|
def get_historical_klines_df_by_cache(self, symbol, tf='15m'):
|
270
294
|
"""_summary_
|
271
295
|
获取历史K线数据
|
@@ -297,8 +321,7 @@ class StrategyTaker():
|
|
297
321
|
self.logger.debug(f"{symbol} : 重新获取新数据: {symbol} {tf}")
|
298
322
|
self.cache_time[cache_key] = current_time
|
299
323
|
return self._get_cache_historical_klines_df(symbol, tf)
|
300
|
-
|
301
|
-
|
324
|
+
|
302
325
|
def get_historical_klines_df(self, symbol, tf='15m'):
|
303
326
|
"""_summary_
|
304
327
|
获取历史K线数据
|
@@ -309,6 +332,7 @@ class StrategyTaker():
|
|
309
332
|
_type_: _description_
|
310
333
|
"""
|
311
334
|
return self.exchange.get_historical_klines_df(symbol=symbol, bar=tf)
|
335
|
+
|
312
336
|
def format_klines(self, klines) -> pd.DataFrame:
|
313
337
|
|
314
338
|
"""_summary_
|
@@ -321,7 +345,7 @@ class StrategyTaker():
|
|
321
345
|
|
322
346
|
return self.exchange.format_klines(klines)
|
323
347
|
|
324
|
-
def find_PDArrays(self, symbol, struct, side=None, start_index=-1, pair_config=None) -> pd.DataFrame:
|
348
|
+
def find_PDArrays(self, symbol, struct, side=None, start_index=-1,balanced=False, pair_config=None) -> pd.DataFrame:
|
325
349
|
"""_summary_
|
326
350
|
寻找PDArray
|
327
351
|
Args:
|
@@ -329,12 +353,27 @@ class StrategyTaker():
|
|
329
353
|
data (_type_): _description_
|
330
354
|
side (_type_): _description_
|
331
355
|
start_index (_type_): _description_
|
332
|
-
|
356
|
+
balanced (bool, optional): _description_. Defaults to False.
|
333
357
|
pair_config (_type_): _description_
|
334
358
|
Returns:
|
335
359
|
_type_: _description_
|
336
360
|
"""
|
337
|
-
return self.smcPDArray.find_PDArrays(struct=struct, side=side, start_index=start_index)
|
361
|
+
return self.smcPDArray.find_PDArrays(struct=struct, side=side, start_index=start_index, balanced=balanced)
|
362
|
+
|
363
|
+
def get_latest_PDArray(self, symbol, data, side, start_index=-1, check_balanced=True, mask=None) -> dict:
|
364
|
+
"""_summary_
|
365
|
+
获取最新的PDArray
|
366
|
+
Args:
|
367
|
+
symbol (_type_): _description_
|
368
|
+
data (_type_): _description_
|
369
|
+
side (_type_): _description_
|
370
|
+
start_index (_type_): _description_
|
371
|
+
check_balanced (bool, optional): _description_. Defaults to True.
|
372
|
+
mask (_type_, optional): _description_. Defaults to None.
|
373
|
+
Returns:
|
374
|
+
_type_: _description_
|
375
|
+
"""
|
376
|
+
return self.smcPDArray.get_latest_PDArray(data, side, start_index, check_balanced, mask)
|
338
377
|
|
339
378
|
def find_OBs(self, symbol, struct, side=None, start_index=-1, is_valid=True, pair_config=None) -> pd.DataFrame:
|
340
379
|
"""_summary_
|
@@ -353,7 +392,7 @@ class StrategyTaker():
|
|
353
392
|
|
354
393
|
return self.smcOB.find_OBs(struct=struct, side=side, start_index=start_index, is_valid=is_valid)
|
355
394
|
|
356
|
-
def
|
395
|
+
def get_latest_OB(self, symbol, data, trend, start_index=-1) -> dict:
|
357
396
|
"""_summary_
|
358
397
|
获取最新的Order Block
|
359
398
|
Args:
|
@@ -365,9 +404,8 @@ class StrategyTaker():
|
|
365
404
|
_type_: _description_
|
366
405
|
"""
|
367
406
|
|
368
|
-
return self.smcOB.
|
369
|
-
|
370
|
-
|
407
|
+
return self.smcOB.get_latest_OB(data=data, trend=trend, start_index=start_index)
|
408
|
+
|
371
409
|
def find_FVGs(self, symbol, data, side, check_balanced=True, start_index=-1, pair_config=None) -> pd.DataFrame:
|
372
410
|
"""_summary_
|
373
411
|
寻找公允价值缺口
|
@@ -399,7 +437,7 @@ class StrategyTaker():
|
|
399
437
|
|
400
438
|
return self.smcStruct.build_struct(data)
|
401
439
|
|
402
|
-
def
|
440
|
+
def get_latest_struct(self, symbol, data) -> dict:
|
403
441
|
"""_summary_
|
404
442
|
获取最后一个SMC结构
|
405
443
|
Args:
|
@@ -408,7 +446,8 @@ class StrategyTaker():
|
|
408
446
|
Returns:
|
409
447
|
_type_: _description_
|
410
448
|
"""
|
411
|
-
return self.smcStruct.
|
449
|
+
return self.smcStruct.get_latest_struct(data)
|
450
|
+
|
412
451
|
def reset_highest_profit_and_tier(self,symbol=None):
|
413
452
|
"""重置最高总盈利和当前档位状态"""
|
414
453
|
if not symbol:
|
@@ -417,7 +456,7 @@ class StrategyTaker():
|
|
417
456
|
if symbol in self.highest_total_profit:
|
418
457
|
self.highest_total_profit[symbol] = 0.0
|
419
458
|
|
420
|
-
def
|
459
|
+
def reset_SL_TP(self, symbol=None, attachType='BOTH'):
|
421
460
|
"""_summary_
|
422
461
|
重置止盈止损
|
423
462
|
"""
|
@@ -425,20 +464,20 @@ class StrategyTaker():
|
|
425
464
|
self.stop_loss_prices.clear()
|
426
465
|
self.take_profit_prices.clear()
|
427
466
|
else :
|
428
|
-
if
|
429
|
-
|
430
|
-
|
431
|
-
|
467
|
+
if attachType == self.BOTH_KEY or attachType == self.SL_KEY:
|
468
|
+
if symbol in self.stop_loss_prices:
|
469
|
+
del self.stop_loss_prices[symbol]
|
470
|
+
if attachType == self.BOTH_KEY or attachType == self.TP_KEY:
|
471
|
+
if symbol in self.take_profit_prices:
|
472
|
+
del self.take_profit_prices[symbol]
|
432
473
|
|
433
474
|
def close_all_cache(self):
|
434
475
|
self.clear_cache_historical_klines_df()
|
435
476
|
self.positions_entry_price ={}
|
436
477
|
|
437
478
|
self.reset_highest_profit_and_tier()
|
438
|
-
self.
|
439
|
-
|
440
|
-
|
441
|
-
|
479
|
+
self.reset_SL_TP()
|
480
|
+
|
442
481
|
def reset_all_cache(self, symbol):
|
443
482
|
"""_summary_
|
444
483
|
重置所有缓存数据
|
@@ -448,7 +487,7 @@ class StrategyTaker():
|
|
448
487
|
|
449
488
|
self.reset_highest_profit_and_tier(symbol)
|
450
489
|
self.clear_cache_historical_klines_df(symbol)
|
451
|
-
self.
|
490
|
+
self.reset_SL_TP(symbol)
|
452
491
|
|
453
492
|
def fetch_positions(self):
|
454
493
|
"""_summary_
|
@@ -456,27 +495,21 @@ class StrategyTaker():
|
|
456
495
|
Returns:
|
457
496
|
_type_: _description_
|
458
497
|
"""
|
459
|
-
|
498
|
+
|
499
|
+
try:
|
500
|
+
positions = self.exchange.fetch_positions()
|
501
|
+
return positions
|
502
|
+
except Exception as e:
|
503
|
+
error_message = f"获取持仓列表失败: {e}"
|
504
|
+
self.logger.error(error_message)
|
505
|
+
self.send_feishu_notification("",error_message)
|
506
|
+
return []
|
507
|
+
|
460
508
|
def check_position(self, symbol, position):
|
461
509
|
# TODO 检查持仓是否有异常,比如未设置止损
|
462
510
|
self.logger.debug(f"{symbol} : 检查持仓是否有异常。")
|
463
511
|
pass
|
464
512
|
|
465
|
-
def cancel_all_algo_orders(self, symbol, attachType: str = None):
|
466
|
-
"""取消止盈止损订单
|
467
|
-
Args:
|
468
|
-
symbol: 交易对
|
469
|
-
attachType: 订单类型,'SL'表示止损单,'TP'表示止盈单,None表示不不区分
|
470
|
-
"""
|
471
|
-
|
472
|
-
self.exchange.cancel_all_algo_orders(symbol=symbol, attachType=attachType)
|
473
|
-
if attachType == "SL" and symbol in self.stop_loss_prices:
|
474
|
-
del self.stop_loss_prices[symbol]
|
475
|
-
elif attachType == "TP" and symbol in self.take_profit_prices:
|
476
|
-
del self.take_profit_prices[symbol]
|
477
|
-
else:
|
478
|
-
self.rest_SL_TP(symbol)
|
479
|
-
|
480
513
|
def set_take_profit(self, symbol, position, tp_price:Decimal=None, order_type='optimal_limit_ioc') -> bool:
|
481
514
|
|
482
515
|
"""
|
@@ -550,7 +583,9 @@ class StrategyTaker():
|
|
550
583
|
|
551
584
|
return has_pass
|
552
585
|
|
553
|
-
|
586
|
+
def get_stop_loss_price(self, symbol):
|
587
|
+
return self.stop_loss_prices.get(symbol, None)
|
588
|
+
|
554
589
|
def calculate_average_profit(self,symbol,position):
|
555
590
|
|
556
591
|
total_profit_pct = self.toDecimal(0.0)
|
@@ -627,7 +662,7 @@ class StrategyTaker():
|
|
627
662
|
except Exception as e:
|
628
663
|
error_message = f"!!获取持仓失败: {str(e)}"
|
629
664
|
self.logger.error(error_message)
|
630
|
-
self.send_feishu_notification("
|
665
|
+
self.send_feishu_notification("ALL",error_message)
|
631
666
|
continue
|
632
667
|
|
633
668
|
try:
|
taker/main.py
CHANGED
@@ -22,7 +22,7 @@ def initialize_logger(config: Dict[str, Any]) -> logging.Logger:
|
|
22
22
|
logging.config.dictConfig(config["Logger"])
|
23
23
|
return logging.getLogger("openfund-taker")
|
24
24
|
|
25
|
-
def create_strategy_instance(taker_name: str, configs: Dict[str, Any], logger: logging.Logger,
|
25
|
+
def create_strategy_instance(taker_name: str, configs: Dict[str, Any], logger: logging.Logger, exchangeKey:str):
|
26
26
|
"""创建策略实例"""
|
27
27
|
module = importlib.import_module(f"taker.{taker_name}")
|
28
28
|
strategy_class = getattr(module, taker_name)
|
@@ -30,7 +30,6 @@ def create_strategy_instance(taker_name: str, configs: Dict[str, Any], logger: l
|
|
30
30
|
configs,
|
31
31
|
configs['platform'][exchangeKey],
|
32
32
|
configs['common'],
|
33
|
-
monitor_interval=monitor_interval,
|
34
33
|
logger=logger
|
35
34
|
)
|
36
35
|
|
@@ -46,7 +45,6 @@ def main():
|
|
46
45
|
# 获取配置参数
|
47
46
|
common_config = config_data['common']
|
48
47
|
taker_name = common_config.get('actived_taker', 'StrategyTaker')
|
49
|
-
monitor_interval = common_config.get("monitor_interval", 60)
|
50
48
|
|
51
49
|
# 初始化日志
|
52
50
|
logger = initialize_logger(config_data)
|
@@ -57,7 +55,7 @@ def main():
|
|
57
55
|
logger.info(f" ++ {package_name}.{taker_name}:{version} is doing...")
|
58
56
|
exchangeKey = common_config.get("exchange_key", "okx")
|
59
57
|
# 创建并运行策略实例
|
60
|
-
bot = create_strategy_instance(taker_name, config_data, logger,
|
58
|
+
bot = create_strategy_instance(taker_name, config_data, logger, exchangeKey)
|
61
59
|
bot.monitor_total_profit()
|
62
60
|
|
63
61
|
if __name__ == "__main__":
|
File without changes
|
File without changes
|