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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openfund-taker
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Summary: Openfund-taker
5
5
  Requires-Python: >=3.9,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,6 +1,6 @@
1
- taker/BestTopDownStrategyTaker.py,sha256=2GCHFr15N-RBVYklraFnAunXi2aKS--asYAIvRgvbwo,4376
1
+ taker/BestTopDownStrategyTaker.py,sha256=nWFgmLvQGCOzNVU7vlHnSM67BMTotUf-BMZZeZxYv34,11607
2
2
  taker/SMCSLAndTPTaker.py,sha256=zJCtwyzIuDtbxMdLOwRbpUE60o9fKe-tNWuiuej4RGs,27621
3
- taker/StrategyTaker.py,sha256=SHm-YmT4eVATbfiYzS-zYMOn8oj1fi1QqJi6aCY3G44,26281
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=w1FaaDlqbWZkXffCA47mQTJ5n4uaE0kclc_teez6_Ek,2252
15
- openfund_taker-2.3.1.dist-info/METADATA,sha256=CkKJcYe-zRX-yI_gNesX19Yk5cYasvBDzu9jEoT3c_8,7575
16
- openfund_taker-2.3.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
17
- openfund_taker-2.3.1.dist-info/entry_points.txt,sha256=a7mG8F7aOA5-Gk2vPWuAR4537faxaHUgM_jwIDBZoEc,50
18
- openfund_taker-2.3.1.dist-info/RECORD,,
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, monitor_interval=4, logger=None, exchangeKey='okx') -> None:
10
- super().__init__(g_config=g_config, platform_config=platform_config, common_config=common_config, monitor_interval=monitor_interval, logger=logger, exchangeKey=exchangeKey)
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 rest_SL_TP(self, symbol):
15
- super().rest_SL_TP(symbol)
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
- # etf = tfs[self.ETF_KEY]
33
+
30
34
  atf = tfs[self.ATF_KEY]
31
35
 
32
36
  # 1.1 ATF Key Support & Resistance Levels 支撑或阻力关键位置(ATF 看上下的供需区位置)
33
- atf_df = self.get_historical_klines_df_by_cache(symbol=symbol, tf=atf)
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.get_lastest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BULLISH_TREND)
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.get_lastest_OB(symbol=symbol,data=atf_OBs_df,trend=self.BEARISH_TREND)
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
- side = self.SELL_SIDE if position[self.SIDE_KEY] == self.SHORT_KEY else self.BUY_SIDE # 和持仓反向相反下单
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='SL')
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='TP')
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
- pass
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, timedelta
5
+ from datetime import datetime
7
6
  from decimal import Decimal
8
7
  from abc import abstractmethod
9
- from concurrent.futures import ThreadPoolExecutor, as_completed
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, monitor_interval=4, logger=None ,exchangeKey='okx'):
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 place_order(self, symbol, price:Decimal, side, pair_config, leverage:int=0, order_type='limit', params={}):
200
+ def close_position(self, symbol, position, params={}) -> dict:
191
201
  """_summary_
192
- 下单
202
+ 平仓
193
203
  Args:
194
204
  symbol (_type_): _description_
195
- price (_type_): _description_
196
- amount_usdt (_type_): _description_
197
- side (_type_): _description_
198
- order_type (_type_): _description_
205
+ position (_type_): _description_
206
+ params (_type_, optional): _description_. Defaults to {}.
207
+
208
+ Returns:
209
+ _type_: _description_
199
210
  """
200
- # 获取做多和做空的下单金额配置
201
- long_amount_usdt = pair_config.get('long_amount_usdt', 5)
202
- short_amount_usdt = pair_config.get('short_amount_usdt', 5)
203
-
204
- # 设置杠杆倍数
205
- leverage = leverage or self.leverage_value
206
-
207
- # 根据交易方向设置下单金额
208
- order_amount_usdt = short_amount_usdt if side == self.SELL_SIDE else long_amount_usdt
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
- self.exchange.cancel_all_orders(symbol=symbol)
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
- is_valid (bool, optional): _description_. Defaults to True.
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 get_lastest_OB(self, symbol, data, trend, start_index=-1) -> dict:
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.get_lastest_OB(data=data, trend=trend, start_index=start_index)
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 get_last_struct(self, symbol, data) -> dict:
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.get_last_struct(data)
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 rest_SL_TP(self, symbol):
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 symbol in self.stop_loss_prices:
429
- del self.stop_loss_prices[symbol]
430
- if symbol in self.take_profit_prices:
431
- del self.take_profit_prices[symbol]
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.rest_SL_TP()
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.rest_SL_TP(symbol)
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
- return self.exchange.fetch_positions()
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(" ",error_message)
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, monitor_interval: int, exchangeKey:str):
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, monitor_interval, exchangeKey)
58
+ bot = create_strategy_instance(taker_name, config_data, logger, exchangeKey)
61
59
  bot.monitor_total_profit()
62
60
 
63
61
  if __name__ == "__main__":