openfund-taker 1.2.13__py3-none-any.whl → 1.3.2__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: 1.2.13
3
+ Version: 1.3.2
4
4
  Summary: Openfund-taker
5
5
  Requires-Python: >=3.9,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3.10
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
- Requires-Dist: TA-Lib (>=0.6.0,<0.7.0)
12
+ Requires-Dist: TA-Lib (>=0.5.1,<0.6.0)
13
13
  Requires-Dist: ccxt (>=4.4.26,<5.0.0)
14
14
  Requires-Dist: pandas (>=2.2.3,<3.0.0)
15
15
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
@@ -1,6 +1,7 @@
1
- taker/MultiAssetNewTradingBot.py,sha256=bpgI6eewTwd7-Q5u2Au0UXd-mvFT1UM9S3H4CXPzf4g,42412
2
1
  taker/MultiAssetOldTradingBot.py,sha256=uBh_BxglvcbaHIsWHM7GI9Qa_QjzsxXaXJAAWEOMO5c,15315
3
2
  taker/ThreeLineTradingBot.py,sha256=oXIoQ8z9AzKzk0z13d0ufj2KGBOk5iHJTJNZQRDKA5U,20625
3
+ taker/TrailingSLAndTPTaker.py,sha256=gikp-Yahn4nWYk6PmUB02t8Sun--Fo4I7vDEU4U2uso,1263
4
+ taker/TrailingSLTaker.py,sha256=LL6BxJQo6yhkruXRDA0jQMt9K-GxhMZiTmKrWwIuZzo,42277
4
5
  taker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
6
  taker/chua_bitget.py,sha256=YY6XK5Bd-wlArsN5BnAfiqE0h1DPkti6i4TEB4F5zDA,12918
6
7
  taker/chua_bn.py,sha256=GnTePWlgDwdHgroBbEp1Ajcsf5_m_Vn_RV63SYzu2jI,10668
@@ -8,8 +9,8 @@ taker/chua_ok.py,sha256=5pPAoEYbFuKxfZwqNvOO890s-2cy6n69QiI0ZA0GTCQ,12474
8
9
  taker/chua_ok_all.py,sha256=2XnZM6QdB3juSE1pqQIJyh2x1XuhlTlnBKNA3owlJ9E,15267
9
10
  taker/chua_ok_bot.py,sha256=9SW0ujhi6PfN4yR1JZ9NaA37HtnXJ2QAWUfW52NG68w,13109
10
11
  taker/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
11
- taker/main.py,sha256=dTi-Innb_DSKp-esWPc6dVQekrGnv7dNHlv49xmVDfo,2516
12
- openfund_taker-1.2.13.dist-info/METADATA,sha256=z2DHJxiMaYaB001bK4otdTyKukF50r1RuicNbb4_jqc,7528
13
- openfund_taker-1.2.13.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
14
- openfund_taker-1.2.13.dist-info/entry_points.txt,sha256=a7mG8F7aOA5-Gk2vPWuAR4537faxaHUgM_jwIDBZoEc,50
15
- openfund_taker-1.2.13.dist-info/RECORD,,
12
+ taker/main.py,sha256=CYibDo9PEOWbhpSXZ5JTmE-NGJXqDdgvhpadXJNRX60,2557
13
+ openfund_taker-1.3.2.dist-info/METADATA,sha256=JG97fvJiR96kRpwabOg1MlIRSTNGoBOX9A2bsymTm94,7527
14
+ openfund_taker-1.3.2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
15
+ openfund_taker-1.3.2.dist-info/entry_points.txt,sha256=a7mG8F7aOA5-Gk2vPWuAR4537faxaHUgM_jwIDBZoEc,50
16
+ openfund_taker-1.3.2.dist-info/RECORD,,
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ import ccxt
3
+ import time
4
+ import requests
5
+ import traceback
6
+ import pandas as pd
7
+ import talib as ta
8
+ from taker.TrailingSLTaker import TrailingSLTaker
9
+ '''
10
+ 自动设置移动止损单
11
+ '''
12
+ class TrailingSLAndTPTaker(TrailingSLTaker):
13
+ def __init__(self,g_config, platform_config, feishu_webhook=None, monitor_interval=4,logger=None):
14
+ super().__init__(g_config, platform_config, feishu_webhook, monitor_interval,logger)
15
+ self.all_TP_LS_ratio = float(platform_config.get("all_TP_LS_ratio",1.5)) #The profit-loss ratio 盈亏比
16
+ self.all_take_profit_pct = self.stop_loss_pct * self.all_TP_LS_ratio
17
+
18
+ def set_stop_loss_take_profit(self, symbol, position, stop_loss_price=None, take_profit_price=None) -> bool:
19
+ is_successful = super().set_stop_loss_take_profit(symbol, position, stop_loss_price, take_profit_price)
20
+ # take_profile_pct = self.stop_loss_pct * 2
21
+ order_take_profit_price = take_profit_price
22
+ if take_profit_price is None:
23
+ order_take_profit_price = self.calculate_take_profile_price(symbol, position, self.all_take_profit_pct)
24
+ is_successful = self.set_take_profit(symbol, position, order_take_profit_price)
25
+
26
+
27
+ return is_successful
28
+
@@ -4,13 +4,17 @@ import time
4
4
  import requests
5
5
  import traceback
6
6
  import pandas as pd
7
-
8
- class MultiAssetNewTradingBot:
7
+ import talib as ta
8
+ '''
9
+ 自动设置移动止损单
10
+ '''
11
+ class TrailingSLTaker:
9
12
  def __init__(self,g_config, platform_config, feishu_webhook=None, monitor_interval=4,logger=None):
10
13
  self.trading_pairs_config = g_config.get('tradingPairs', {})
11
14
 
12
- self.stop_loss_pct = platform_config["all_stop_loss_pct"] # 全局止损百分比
13
- # 止盈比例
15
+
16
+ self.stop_loss_pct = float(platform_config.get("all_stop_loss_pct",2)) # 全局止损百分比
17
+ # 回撤比例
14
18
  self.low_trail_stop_loss_pct = platform_config["all_low_trail_stop_loss_pct"] # 第一档
15
19
  self.trail_stop_loss_pct = platform_config["all_trail_stop_loss_pct"]# 第二档
16
20
  self.higher_trail_stop_loss_pct = platform_config["all_higher_trail_stop_loss_pct"]# 第三档
@@ -22,14 +26,17 @@ class MultiAssetNewTradingBot:
22
26
  self.feishu_webhook = feishu_webhook
23
27
  self.monitor_interval = monitor_interval # 监控循环时间是分仓监控的3倍
24
28
 
25
- self.highest_total_profit = 0 # 记录最高总盈利
26
- self.current_tier = "无" # 记录当前的仓位模式
29
+ self.highest_total_profit = {} # 记录最高总盈利
30
+
31
+ # self.current_tier = {} # 记录当前的仓位模式
32
+
33
+ self.positions_entry_price = {} # 记录每个symbol的开仓价格
27
34
 
28
35
  self.global_symbol_stop_loss_flag = {} # 记录每个symbol是否设置全局止损
29
36
  self.global_symbol_stop_loss_price = {} # 记录每个symbol的止损价格
30
37
  # 保留在止盈挂单中最高最低两个价格,计算止盈价格。
31
- self.max_market_price = 0.0
32
- self.min_market_price = float('inf') # 初始化为浮点数最大值
38
+ self.max_market_price = {}
39
+ self.min_market_price = {}
33
40
 
34
41
  self.cross_directions = {} # 持仓期间,存储每个交易对的交叉方向
35
42
 
@@ -104,7 +111,8 @@ class MultiAssetNewTradingBot:
104
111
  self.logger.error(f"Error fetching open orders: {e}")
105
112
  return []
106
113
 
107
- def get_historical_klines(self,symbol, bar='1m', limit=241):
114
+ # 获取历史K线,不包含最新的一条
115
+ def get_historical_klines_except_last(self,symbol, bar='1m', limit=241):
108
116
  # response = market_api.get_candlesticks(instId, bar=bar, limit=limit)
109
117
  params = {
110
118
  # 'instId': instId,
@@ -113,180 +121,174 @@ class MultiAssetNewTradingBot:
113
121
  # if 'data' in response and len(response['data']) > 0:
114
122
  if klines :
115
123
  # return response['data']
116
- return klines
124
+ return klines[:-1]
117
125
  else:
118
126
  raise ValueError("Unexpected response structure or missing candlestick data")
119
127
 
120
- def judge_cross_direction(self,symbol, fastklines ,slowklines) :
121
- # 创建DataFrame
122
- df = pd.DataFrame({
123
- 'fast': fastklines,
124
- 'slow': slowklines
125
- })
126
-
127
- # 判断金叉和死叉
128
- df['golden_cross'] = (df['fast'] > df['slow']) & (df['fast'].shift(1) < df['slow'].shift(1))
129
- df['death_cross'] = (df['fast'] < df['slow']) & (df['fast'].shift(1) > df['slow'].shift(1))
130
-
131
- # 从后往前找最近的交叉点
132
- last_golden = df['golden_cross'].iloc[::-1].idxmax() if df['golden_cross'].any() else None
133
- last_death = df['death_cross'].iloc[::-1].idxmax() if df['death_cross'].any() else None
134
- # self.logger.debug(f"golden_cross = {last_golden}, death_cross = {last_death}")
128
+ def get_last_solated_point(self,symbol,position,kLines):
129
+ # 将K线数据转换为DataFrame格式,最后一个数据可能未完成。
130
+ df = pd.DataFrame(kLines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
131
+ df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
135
132
 
136
- # self.logger.debug(f"df= \n{df[['fast','slow','golden_cross','death_cross']].tail()}")
137
- # 判断最近的交叉类型
138
- if last_golden is None and last_death is None:
139
- return {
140
- 'cross': -1, # 无交叉
141
- 'index': None
142
- }
133
+ # 根据position方向寻找孤立点
134
+ side = position['side']
135
+ window = 1 # 设置窗口大小,用于判断局部最值
143
136
 
144
- # 如果金叉更近或只有金叉
145
- if last_golden is not None and (last_death is None or last_golden > last_death):
146
- return {
147
- 'cross': 1, # 金叉
148
- 'index': last_golden
149
- }
150
- # 如果死叉更近或只有死叉
137
+ if side == 'short':
138
+ # 找最近的孤立高点
139
+ # 判断局部最高点:当前点比前后window个点都高
140
+ df['is_high'] = df['high'].rolling(window=window*2+1, center=True).apply(
141
+ lambda x: x[window] > max(x[:window]) and x[window] > max(x[window+1:]), raw=True
142
+ )
143
+ # 获取最近的孤立高点
144
+ isolated_points = df[df['is_high']==True]['high'].iloc[-3:]
145
+
151
146
  else:
152
- return {
153
- 'cross': 0, # 死叉
154
- 'index': last_death
155
- }
156
-
157
- def judge_ma_apex(self,symbol,pair_config, fastklines,slowklines) -> bool:
158
- period = int(pair_config.get('ema_range_period', 3))
159
- precision= self.get_precision_length(symbol)
147
+ # 找最近的孤立低点
148
+ # 判断局部最低点:当前点比前后window个点都低
149
+ df['is_low'] = df['low'].rolling(window=window*2+1, center=True).apply(
150
+ lambda x: x[window] < min(x[:window]) and x[window] < min(x[window+1:]), raw=True
151
+ )
152
+ # 获取最近的孤立低点
153
+ isolated_points = df[df['is_low']==True]['low'].iloc[-3:]
154
+
155
+ return isolated_points
156
+
157
+
158
+ def judge_correct_postion_side(self, symbol, pair_config, klines=None) -> str:
159
+
160
+ '''
161
+ 零轴之上的macd与signal形成金叉
162
+ 零轴之下的死叉
163
+ 零轴之上的死叉-金叉-死叉
164
+ 零轴之下的金叉-死叉-金叉
165
+ '''
166
+
167
+ order_side = 'none'
168
+ if 'macd_strategy' not in pair_config :
169
+ return order_side
160
170
 
161
- df = pd.DataFrame({
162
- 'ema': fastklines,
163
- 'sma': slowklines
164
- })
165
- # 快线和慢线的差值
166
- # 将ema和sma转换为tick_size精度
167
- # df['diff'] = df['ema'].apply(lambda x: float(self.round_price_to_tick(x, tick_size))) - df['sma'].apply(lambda x: float(self.round_price_to_tick(x, tick_size)))
168
- df['diff'] = df['ema'].round(precision)-df['sma'].round(precision)
169
- df['ema_diff'] = df['ema'] - df['ema'].shift(1)
170
- df['sma_diff'] = df['sma'] - df['sma'].shift(1)
171
- # 计算斜率,【正】表示两线距离扩张,【负】表示两线距离收缩
172
- df['slope'] = df['diff'].abs().diff().round(4)
171
+ macd = pd.DataFrame(klines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
172
+ # 将时间戳转换为日期时间格式
173
+ macd['timestamp'] = pd.to_datetime(macd['timestamp'], unit='ms').dt.strftime('%m-%d %H:%M')
174
+
175
+ # 使用 TA-Lib 计算 MACD
176
+ macd[['macd', 'signal', 'hist']] = pd.DataFrame(ta.MACD(macd['close'], fastperiod=12, slowperiod=26, signalperiod=9)).T
177
+ # self.logger.debug(f"{symbol} : MACD Values = \n {macd.tail(5)}")
173
178
 
174
- self.logger.debug(f"{symbol}: slopes = \n{df[['ema','ema_diff','sma','sma_diff','diff','slope']].iloc[-6:-1]} ")
175
179
 
176
- # 两条线的距离是扩张状态还是收缩状态 true 是收缩 flase 是扩张
177
- is_expanding_or_contracting = all(df['slope'].tail(period) <= 0 ) and any(df['slope'].tail(period) < 0)
178
-
179
- return is_expanding_or_contracting
180
-
181
- # 定义根据均线斜率判断 K 线方向的函数: 0 空 1 多 -1 平
182
-
183
- def judge_range_diff(self,symbol,pair_config,prices:pd.Series) -> bool:
184
- """
185
- 计算价格列表中最后一个价格与第一个价格的差值。
186
- Args:
187
- prices: 价格列表。
188
- Returns:
189
- diff: 计算最高价列的最大值与最小值的差值
190
-
191
- """
192
- limit = int(pair_config.get('ema_range_limit', 1))
193
- period = int(pair_config.get('ema_range_period', 3))
194
- tick_size = self.get_tick_size(symbol)
195
- if prices.empty:
196
- return None
197
-
198
- diff = prices.tail(period).max() - prices.tail(period).min()
199
- self.logger.debug(f"{symbol}: 最高价列的最大值与最小值的差值 = {diff:.9f}")
200
- return abs(diff) <= tick_size * limit
201
-
202
- # 定义根据均线斜率判断 K 线方向的函数: 0 空 1 多 -1 平
203
- def judge_k_line_direction(self, symbol, pair_config, ema: pd.Series, klines) -> int:
204
- """
205
- 判断K线方向
206
- Args:
207
- symbol: 交易对
208
- pair_config: 配置参数
209
- ema: EMA数据
210
- Returns:
211
- int: -1:平, 0:空, 1:多
212
- """
213
- # 获取配置参数
214
- period = int(pair_config.get('ema_range_period', 3))
180
+ # 计算最近三个交叉点
181
+ last_up_crosses = []
182
+ last_down_crosses = []
183
+ other_crosses = []
184
+ all_cross = []
215
185
 
216
- ema_diff = ema.diff().tail(period)
217
-
218
- direction = None
219
- if ema_diff.iloc[-1] < 0:
220
- # 下降趋势
221
- direction = 0
222
- elif ema_diff.iloc[-1] > 0:
223
- # 上升趋势
224
- direction = 1
186
+ # 从最新K开始往前遍历
187
+ for i in range(len(macd)-1, 2, -1):
188
+ # 检查是否发生死叉(MACD从上方穿过Signal)
189
+ if (macd.iloc[i-1]['macd'] <= macd.iloc[i-1]['signal'] and
190
+ macd.iloc[i]['macd'] > macd.iloc[i]['signal']
191
+ ):
192
+ all_cross.append(('golden', i))
193
+
194
+ # 判断如果都在零轴之上加入last_up_crosses , 判断如果都在零轴之下加入last_down_crosses
195
+ if macd.iloc[i]['macd'] > 0 and macd.iloc[i]['signal'] > 0 :
196
+ last_up_crosses.append(('golden', i))
197
+ elif macd.iloc[i]['macd'] < 0 and macd.iloc[i]['signal'] < 0 :
198
+ last_down_crosses.append(('golden', i))
199
+ else:
200
+ other_crosses.append(('golden', i))
201
+
202
+ # 检查是否发生死叉(MACD从上方穿过Signal)
203
+ elif macd.iloc[i-1]['macd'] >= macd.iloc[i-1]['signal'] and macd.iloc[i]['macd'] < macd.iloc[i]['signal']:
204
+ all_cross.append(('death', i))
205
+ # 判断如果都在零轴之上加入last_up_crosses , 判断如果都在零轴之下加入last_down_crosses
206
+ if macd.iloc[i]['macd'] > 0 and macd.iloc[i]['signal'] > 0 :
207
+ last_up_crosses.append(('death', i))
208
+ elif macd.iloc[i]['macd'] < 0 and macd.iloc[i]['signal'] < 0 :
209
+ last_down_crosses.append(('death', i))
210
+ else:
211
+ other_crosses.append(('golden', i))
212
+ # 只保留最后三个交叉点
213
+ if len(last_up_crosses) == 3 or len(last_down_crosses) == 3:
214
+ break
215
+
216
+ self.logger.debug(f"{symbol} : \n- 所有cross {all_cross} \n- 零轴之上cross {last_up_crosses} \n- 零轴之下cross {last_down_crosses} \n- 其他corss {other_crosses}。")
217
+
218
+ # valid_klines = pair_config['macd_strategy'].get('valid_klines', 5)
219
+ # 如果最新的交叉是金叉,且又是零轴上方的金叉
220
+ if len(last_up_crosses) > 0 and all_cross[0][0] == 'golden' and all_cross[0][1] == last_up_crosses[0][1] :
221
+ order_side = 'long'
222
+ self.logger.debug(f"{symbol} : 零轴之上的macd与signal形成金叉{all_cross[0]} 。")
223
+
224
+ # 如果最新的交叉是死叉,且又是零轴下方的死叉
225
+ elif len(last_down_crosses) > 0 and all_cross[0][0] == 'death' and all_cross[0][1] == last_down_crosses[0][1] :
226
+ order_side ='short'
227
+ self.logger.debug(f"{symbol} : 零轴之下的macd与signal形成死叉{all_cross[0]} 。")
228
+ # 分析交叉点模式,要满足连续的三个交叉都是零上
229
+ elif len(last_up_crosses) == 3 and len(all_cross) == 3:
230
+
231
+ # 零轴之上的死叉-金叉-死叉模式
232
+ if (last_up_crosses[0][0] == 'death' and
233
+ last_up_crosses[1][0] == 'golden' and
234
+ last_up_crosses[2][0] == 'death'
235
+ ):
236
+ order_side = 'short'
237
+ self.logger.debug(f"{symbol} : 零轴之上的死叉-金叉-死叉模式 {order_side}。")
238
+
239
+ elif len(last_down_crosses) == 3 and len(all_cross) == 3:
240
+ # 零轴之下的金叉-死叉-金叉模式
241
+ if (last_down_crosses[0][0] == 'golden' and
242
+ last_down_crosses[1][0] == 'death' and
243
+ last_down_crosses[2][0] == 'golden'
244
+ ):
245
+ order_side = 'long'
246
+ self.logger.debug(f"{symbol} : 零轴之下的金叉-死叉-金叉模式 {order_side}。")
247
+
248
+ return order_side
249
+
250
+ def is_profitable(self, symbol ,position) -> bool:
251
+ # 判断是否进入浮赢阶段
252
+ if position['side'] == 'long':
253
+ # 多单判断持仓价格是否小于止损价格
254
+ return position['entryPrice'] < self.global_symbol_stop_loss_price.get(symbol,0.0)
225
255
  else:
226
- # 震荡趋势
227
- direction = -1
228
- self.logger.debug(f"{symbol}: K线极差={ema_diff.map('{:.9f}'.format).values} ,K线方向={direction}")
229
- return direction
230
-
256
+ # 空单判断持仓价格是否大于止损价格
257
+ return position['entryPrice'] > self.global_symbol_stop_loss_price.get(symbol,0.0)
258
+
231
259
  def check_reverse_position(self,symbol,position,pair_config):
260
+ if 'entryPrice' not in position or self.is_profitable(symbol,position):
261
+ self.logger.debug(f"{symbol} : 方向={position['side']} 经进入浮赢阶段,不校验全局止损位置,止损价={self.global_symbol_stop_loss_price} ,开仓价={position['entryPrice']}。")
262
+ return
263
+
232
264
  side = position['side']
233
265
  try:
234
- klines_period = str(pair_config.get('klines_period', '1m'))
235
- klines = self.get_historical_klines(symbol=symbol,bar=klines_period)
236
266
 
237
- # 计算 快线EMA & 慢线SMA
238
- ema_length = pair_config.get('ema', 15)
239
- sma_length = pair_config.get('sma', 50)
240
-
241
- # 增加 金叉死叉 方向确认的 20250209
242
- fastk = self.calculate_ema_pandas(symbol, klines, period=ema_length)
243
- slowk = self.calculate_sma_pandas(symbol, klines, period=sma_length)
244
-
245
- cross_direction = self.judge_cross_direction(symbol=symbol,fastklines=fastk,slowklines=slowk)
267
+ klines_period = str(pair_config.get('klines_period', '1m'))
268
+ klines = self.get_historical_klines_except_last(symbol=symbol,bar=klines_period)
246
269
 
247
- # 更新交叉状态
248
- if cross_direction['cross'] != -1 : #本次不一定有交叉
249
- self.cross_directions[symbol] = cross_direction
270
+ self.logger.debug(f"开始监控 {symbol} : klines {klines_period} - {len(klines)}")
250
271
 
251
- # 最新交叉方向
252
- last_cross_direction = self.exchange.safe_dict(self.cross_directions,symbol,None)
253
- # 计算 快线EMA & 慢线SMA
254
- # 结合金叉死叉判断是否是周期顶部和底部
255
- is_apex = self.judge_ma_apex(symbol=symbol,pair_config=pair_config, fastklines=fastk,slowklines=slowk)
272
+ correct_side = self.judge_correct_postion_side(symbol=symbol, pair_config=pair_config, klines=klines)
256
273
 
257
- kline_direction = self.judge_k_line_direction(symbol=symbol, pair_config=pair_config, ema=fastk, klines=klines)
258
- # if_inner_range = self.judge_range_diff(symbol=symbol, pair_config=pair_config, prices=fastk)
259
-
260
- self.logger.debug(f"{symbol} cross={last_cross_direction},两线收缩={is_apex},持仓方向={side} ,K线方向={kline_direction}")
261
274
  order_stop_loss_pct = None
262
- # 20250213 增加趋势顶部/底部判断
263
- # 金叉逻辑 ,如果是金叉,且是周期顶部,且K线方向是空头,
264
- if last_cross_direction and last_cross_direction['cross'] == 1 and is_apex and side == 'long' and kline_direction == 0:
265
- self.logger.debug(f"{symbol} 金叉:{last_cross_direction['cross']},两线收缩={is_apex},持仓方向={side} ,K线方向={kline_direction} ,开始清理多单!!")
266
- # self.close_all_positions(symbol=symbol, position=position)
275
+ # 方向不一致 尽快平仓
276
+ if correct_side != 'none' and correct_side != side :
277
+ self.logger.info(f"{symbol}: 持仓方向={side} 正确方向={correct_side} 相反 , 减少止损。")
267
278
  order_stop_loss_pct = self.stop_loss_pct / 2
268
- self.logger.debug(f"{symbol} 全局止损阈值-修正后= {self.stop_loss_pct:.2f} -> {order_stop_loss_pct:.2f}%")
269
-
270
-
271
- # 死叉逻辑 ,如果是死叉,且是周期底部,且K线方向是多头,就清仓空单
272
- if last_cross_direction and last_cross_direction['cross'] == 0 and is_apex and side == 'short' and kline_direction == 1:
273
- self.logger.debug(f"{symbol} 死叉:{last_cross_direction['cross']},两线收缩={is_apex},持仓方向={side} ,K线方向={kline_direction} ,开始清理空单!!")
274
- # self.close_all_positions(symbol=symbol, position=position)
275
- order_stop_loss_pct = self.stop_loss_pct / 2
276
- self.logger.debug(f"{symbol} 全局止损阈值-修正后= {self.stop_loss_pct:.2f} -> {order_stop_loss_pct:.2f}%")
277
-
278
- # 根据情况 重新修正 止损
279
- if order_stop_loss_pct is not None :
280
- self.global_symbol_stop_loss_flag[symbol] = False
281
- self.set_global_stop_loss(symbol=symbol,position=position,stop_loss_pct=order_stop_loss_pct)
279
+ self.logger.info(f"{symbol} 全局止损阈值-修正后= {self.stop_loss_pct:.2f} -> {order_stop_loss_pct:.2f}%")
282
280
  else :
283
- self.global_symbol_stop_loss_flag[symbol] = False
281
+ order_stop_loss_pct = self.stop_loss_pct
282
+ self.logger.info(f"{symbol}: 持仓方向={side} 与 正确方向={correct_side} 相同 , 恢复正常。")
284
283
 
285
- except KeyboardInterrupt:
286
- self.logger.info("程序收到中断信号,开始退出...")
284
+ # 根据情况 重新修正止损
285
+ self.global_symbol_stop_loss_flag[symbol] = False
286
+ self.set_global_stop_loss(symbol=symbol,position=position,stop_loss_pct=order_stop_loss_pct)
287
+
288
+
287
289
  except Exception as e:
288
290
  error_message = f"程序异常退出: {str(e)}"
289
- self.logger.error(error_message,exc_info=True)
291
+ self.logger.warning(error_message,exc_info=True)
290
292
  traceback.print_exc()
291
293
  self.send_feishu_notification(error_message)
292
294
 
@@ -345,20 +347,31 @@ class MultiAssetNewTradingBot:
345
347
  average_profit_pct = total_profit_pct / num_positions if num_positions > 0 else 0
346
348
  return average_profit_pct
347
349
 
348
- def reset_highest_profit_and_tier(self):
350
+ def reset_highest_profit_and_tier(self,symbol=None):
349
351
  """重置最高总盈利和当前档位状态"""
350
- self.highest_total_profit = 0
351
- self.current_tier = "无"
352
- self.global_symbol_stop_loss_flag.clear()
353
- # self.logger.debug("已重置最高总盈利和档位状态")
352
+ if not symbol:
353
+ self.highest_total_profit.clear()
354
+ else :
355
+ self.highest_total_profit[symbol] = 0.0
356
+
357
+ # self.logger.debug("已重置最高总盈利")
354
358
  # FIXME 目前只支持 单symbol
355
- def reset_take_profie(self):
356
- self.global_symbol_stop_loss_price.clear()
357
- self.global_symbol_stop_loss_flag.clear()
358
- # 保留在止盈挂单中最高最低两个价格,计算止盈价格。
359
- self.max_market_price = 0.0
360
- self.min_market_price = float('inf') # 初始化为浮点数最大值
361
- self.cross_directions = {}
359
+ def reset_take_profie(self,symbol=None):
360
+ if not symbol:
361
+ self.global_symbol_stop_loss_price.clear()
362
+ self.global_symbol_stop_loss_flag.clear()
363
+ # 保留在止盈挂单中最高最低两个价格,计算止盈价格。
364
+ self.max_market_price.clear()
365
+ self.min_market_price.clear()
366
+ self.cross_directions.clear()
367
+
368
+ else :
369
+ self.global_symbol_stop_loss_price[symbol] = 0.0
370
+ self.global_symbol_stop_loss_flag[symbol] = False
371
+ # 保留在止盈挂单中最高最低两个价格,计算止盈价格。
372
+ self.max_market_price[symbol] = 0.0
373
+ self.min_market_price[symbol] = float('inf') # 初始化为浮点数最大值
374
+ self.cross_directions[symbol] = None
362
375
 
363
376
  def round_price_to_tick(self,symbol, price):
364
377
  tick_size = float(self.exchange.market(symbol)['info']['tickSz'])
@@ -378,7 +391,7 @@ class MultiAssetNewTradingBot:
378
391
  orders = self.fetch_open_orders(symbol=symbol,params=params)
379
392
  # 如果没有委托订单则直接返回
380
393
  if not orders:
381
- self.global_symbol_stop_loss_flag.clear()
394
+ self.global_symbol_stop_loss_flag[symbol] = False
382
395
  self.logger.debug(f"{symbol} 未设置策略订单列表。")
383
396
  return
384
397
 
@@ -389,7 +402,7 @@ class MultiAssetNewTradingBot:
389
402
  "trigger": 'trigger'
390
403
  }
391
404
  rs = self.exchange.cancel_orders(ids=algo_ids, symbol=symbol, params=params)
392
- self.global_symbol_stop_loss_flag.clear()
405
+ self.global_symbol_stop_loss_flag[symbol] = False
393
406
  # self.logger.debug(f"Order {algo_ids} cancelled:{rs}")
394
407
  except Exception as e:
395
408
  self.logger.error(f"{symbol} Error cancelling order {algo_ids}: {e}")
@@ -454,7 +467,7 @@ class MultiAssetNewTradingBot:
454
467
  while retry_count < max_retries:
455
468
  try:
456
469
 
457
- self.logger.debug(f"{symbol} - {orderSide}:Pre Take profit order set for {symbol} at {take_profit_price} Starting.... ")
470
+ self.logger.debug(f"{symbol}: {orderSide} - TP at {adjusted_price} Starting.... ")
458
471
 
459
472
  self.exchange.create_order(
460
473
  symbol=symbol,
@@ -465,7 +478,8 @@ class MultiAssetNewTradingBot:
465
478
  amount=amount,
466
479
  params=tp_params
467
480
  )
468
- self.logger.debug(f"{symbol} - {orderSide}: Take profit order set for {symbol} at {take_profit_price} Done.")
481
+
482
+ self.logger.info(f"{symbol}: TP at {adjusted_price} Done.")
469
483
  break
470
484
 
471
485
 
@@ -497,8 +511,7 @@ class MultiAssetNewTradingBot:
497
511
 
498
512
 
499
513
  def set_stop_loss(self, symbol, position, stop_loss_price=None) -> bool:
500
-
501
-
514
+
502
515
  # 计算下单数量
503
516
  amount = abs(float(position['contracts']))
504
517
 
@@ -537,7 +550,7 @@ class MultiAssetNewTradingBot:
537
550
  while retry_count < max_retries:
538
551
  try:
539
552
 
540
- self.logger.debug(f"{symbol} - {orderSide}:Pre Stop loss order set for {symbol} at {stop_loss_price} Starting.... ")
553
+ self.logger.debug(f"{symbol}: {orderSide} - Pre SL at {adjusted_price} Starting.... ")
541
554
 
542
555
  self.exchange.create_order(
543
556
  symbol=symbol,
@@ -549,7 +562,7 @@ class MultiAssetNewTradingBot:
549
562
  amount=amount,
550
563
  params=sl_params
551
564
  )
552
- self.logger.debug(f"{symbol} - {orderSide}: Stop loss order set for {symbol} at {stop_loss_price} Done.")
565
+ self.logger.info(f"{symbol}: SL at {adjusted_price} Done.")
553
566
  break
554
567
 
555
568
 
@@ -590,11 +603,11 @@ class MultiAssetNewTradingBot:
590
603
  stop_loss_algo: 止损算法信息
591
604
  """
592
605
  # 如果已经触发过全局止损并且有止损单,则跳过
606
+ self.logger.debug(f"{symbol} - 是否设置过全局止损 {self.global_symbol_stop_loss_flag.get(symbol, False)} ")
593
607
  if self.global_symbol_stop_loss_flag.get(symbol, False):
594
-
595
608
  return
596
- else :
597
- self.logger.debug(f"{symbol} - 是否设置过全局止损 {self.global_symbol_stop_loss_flag.get(symbol, False)} ")
609
+
610
+
598
611
  if stop_loss_pct is None :
599
612
  stop_loss_pct = self.stop_loss_pct
600
613
 
@@ -609,11 +622,29 @@ class MultiAssetNewTradingBot:
609
622
 
610
623
  sl_order_price = float(self.round_price_to_tick(symbol, sl_price))
611
624
  tp_order_price = float(self.round_price_to_tick(symbol, tp_price))
625
+
626
+ # 250228 没有指定止损回撤阈值stop_loss_pct,则参考最新的孤立点设置止损价格
627
+ if stop_loss_pct is None :
628
+ pair_config = self.trading_pairs_config.get(symbol, {})
629
+ klines_period = str(pair_config.get('klines_period', '1m'))
630
+ kLines = self.get_historical_klines_except_last(symbol=symbol,bar=klines_period)
631
+
632
+ if kLines:
633
+ isolated_point = self.get_last_solated_point(symbol ,position , kLines)
634
+
635
+ if len(isolated_point) > 0 :
636
+ last_isolated_point = float(isolated_point.iloc[-1])
637
+ self.logger.debug(f"{symbol}: - {side} ,孤立点={last_isolated_point} ,止损价={sl_order_price}")
638
+ if side == 'long':
639
+ sl_order_price = max(last_isolated_point, sl_order_price)
640
+ elif side =='short':
641
+ sl_order_price = min(last_isolated_point, sl_order_price)
642
+
612
643
 
613
644
  last_sl_price= self.global_symbol_stop_loss_price.get(symbol,None)
614
645
  if last_sl_price is not None and last_sl_price == sl_order_price:
615
646
  self.global_symbol_stop_loss_flag[symbol] = True
616
- self.logger.debug(f"{symbol} - {side} 全局止损价没变化: {last_sl_price} = {sl_order_price}")
647
+ self.logger.debug(f"{symbol}: - {side} ,全局止损价没变化: {last_sl_price} = {sl_order_price}")
617
648
  return
618
649
 
619
650
  try:
@@ -644,18 +675,17 @@ class MultiAssetNewTradingBot:
644
675
  # 计算止盈价格。
645
676
 
646
677
  if side == 'long':
647
-
648
- base_price = entry_price * (1-take_profile_pct)
649
- take_profile_price = entry_price + base_price - offset * tick_size
678
+ take_profile_price = entry_price * (1+take_profile_pct/100) - offset * tick_size
650
679
 
651
680
 
652
681
  elif side == 'short':
653
682
 
654
- base_price = entry_price * (1-take_profile_pct)
655
- take_profile_price = entry_price - base_price + offset * tick_size
683
+ # base_price = entry_price * (1-take_profile_pct)
684
+ take_profile_price = entry_price * (1-take_profile_pct/100) + offset * tick_size
656
685
 
657
- return take_profile_price
686
+ return float(self.round_price_to_tick(symbol,take_profile_price))
658
687
 
688
+ # 计算回撤止盈价格
659
689
  def calculate_stop_loss_price(self, symbol, position, stop_loss_pct, offset=1) -> float:
660
690
  tick_size = float(self.exchange.market(symbol)['precision']['price'])
661
691
  market_price = position['markPrice']
@@ -665,19 +695,21 @@ class MultiAssetNewTradingBot:
665
695
  # 计算止盈价格,用市场价格(取持仓期间历史最高)减去开仓价格的利润,再乘以不同阶段的止盈百分比。
666
696
  latest_stop_loss_price = self.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,None)
667
697
  if side == 'long':
668
- self.max_market_price = max(market_price,self.max_market_price)
669
- base_price = abs(self.max_market_price - entry_price) * (1-stop_loss_pct)
698
+ last_max_market_price = self.max_market_price.get(symbol,0.0)
699
+ self.max_market_price[symbol] = max(market_price,last_max_market_price)
700
+ base_price = abs(self.max_market_price[symbol] - entry_price) * (1-stop_loss_pct)
670
701
  stop_loss_price = entry_price + base_price - offset * tick_size
671
702
  if latest_stop_loss_price :
672
703
  stop_loss_price = max(stop_loss_price,latest_stop_loss_price)
673
704
 
674
705
  elif side == 'short':
675
- self.min_market_price = min(market_price,self.min_market_price)
676
- base_price = abs(self.min_market_price - entry_price) * (1-stop_loss_pct)
706
+ last_min_market_price = self.min_market_price.get(symbol,float('inf'))
707
+ self.min_market_price[symbol] = min(market_price,last_min_market_price)
708
+ base_price = abs(self.min_market_price[symbol] - entry_price) * (1-stop_loss_pct)
677
709
  stop_loss_price = entry_price - base_price + offset * tick_size
678
710
  if latest_stop_loss_price :
679
711
  stop_loss_price = min(stop_loss_price,latest_stop_loss_price)
680
- return stop_loss_price
712
+ return float(self.round_price_to_tick(symbol,stop_loss_price))
681
713
 
682
714
  # 市价仓位平仓
683
715
  def close_all_positions(self,symbol,position):
@@ -714,7 +746,7 @@ class MultiAssetNewTradingBot:
714
746
  params=params
715
747
  )
716
748
  time.sleep(0.1) # 短暂延迟后再试
717
- self.reset_take_profie()
749
+ self.reset_take_profie(symbol)
718
750
  self.logger.info(f"{symbol} Close position response for {symbol}: {order}")
719
751
  self.send_feishu_notification(f"{symbol} 平仓订单完全成交 -{symbol} side: {side}")
720
752
 
@@ -733,25 +765,27 @@ class MultiAssetNewTradingBot:
733
765
  """
734
766
  latest_stop_loss_price = self.exchange.safe_float(self.global_symbol_stop_loss_price, symbol, 0.0)
735
767
  if latest_stop_loss_price == 0.0:
736
- self.logger.warning(f"{symbol} 未设置止损价格,执行平仓")
768
+ self.logger.warning(f"{symbol}: 未设置止损价格,执行平仓")
737
769
  return True
738
770
 
739
771
  mark_price = position['markPrice']
740
772
  side = position['side']
741
773
 
742
- if side == 'long' and mark_price < latest_stop_loss_price:
743
- self.logger.warning(f"!![非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 低于止盈 {latest_stop_loss_price},触发全局止盈")
774
+ sl_price = float(self.round_price_to_tick(symbol=symbol,price=latest_stop_loss_price))
775
+
776
+ if side == 'long' and mark_price < sl_price:
777
+ self.logger.warning(f"{'*'*60}\n[非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 低于止盈 {latest_stop_loss_price},触发全局止盈\n{'*'*60}")
744
778
  return True
745
- elif side == 'short' and mark_price > latest_stop_loss_price:
746
- self.logger.warning(f"!![非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 高于止盈价 {latest_stop_loss_price},触发全局止盈")
779
+ elif side == 'short' and mark_price > sl_price:
780
+ self.logger.warning(f"{'*'*60}\n[非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 高于止盈价 {latest_stop_loss_price},触发全局止盈\n{'*'*60}")
747
781
  return True
748
782
 
749
783
  return False
750
784
 
751
785
  def check_position(self, symbol, position):
752
786
  # 清理趋势相反的仓位
753
- # pair_config = self.trading_pairs_config.get(symbol, {})
754
- # self.check_reverse_position(symbol=symbol, position=position, pair_config=pair_config)
787
+ pair_config = self.trading_pairs_config.get(symbol, {})
788
+ self.check_reverse_position(symbol=symbol, position=position, pair_config=pair_config)
755
789
 
756
790
  # 检查止损是否触发止盈
757
791
  if self.check_stop_loss_trigger(symbol, position):
@@ -760,105 +794,105 @@ class MultiAssetNewTradingBot:
760
794
 
761
795
 
762
796
  def check_total_profit(self, symbol, position):
763
-
797
+ """
798
+ 检查当前总盈利
799
+ """
800
+
764
801
  total_profit = self.calculate_average_profit(symbol, position)
765
802
  if total_profit > 0.0 :
766
803
  self.logger.info(f"{symbol} 当前总盈利: {total_profit:.2f}%")
767
804
  self.send_feishu_notification(f"{symbol} 当前总盈利: {total_profit:.2f}%")
768
- if total_profit > self.highest_total_profit:
769
- self.highest_total_profit = total_profit
805
+
806
+ cur_highest_total_profit = self.highest_total_profit.get(symbol, 0.0)
807
+
808
+ if total_profit > cur_highest_total_profit:
809
+ cur_highest_total_profit = total_profit
810
+ self.highest_total_profit[symbol] = total_profit
811
+
812
+ current_tier = '无'
770
813
  # 确定当前盈利档位
771
- if self.highest_total_profit >= self.second_trail_profit_threshold:
772
- self.current_tier = "第二档移动止盈"
814
+ if cur_highest_total_profit >= self.second_trail_profit_threshold:
815
+ current_tier = "高档"
773
816
 
774
- elif self.highest_total_profit >= self.first_trail_profit_threshold:
775
- self.current_tier = "第一档移动止盈"
817
+ elif cur_highest_total_profit>= self.first_trail_profit_threshold:
818
+ current_tier = "中档"
776
819
 
777
- elif self.highest_total_profit >= self.low_trail_profit_threshold:
778
- self.current_tier = "低档保护止盈"
820
+ elif cur_highest_total_profit >= self.low_trail_profit_threshold:
821
+ current_tier = "低档"
779
822
 
780
823
 
781
824
  if total_profit > 0.0 :
782
825
  self.logger.info(
783
- f"{symbol} 档位[{self.current_tier} ]: 当前总盈利: {total_profit:.2f}%,最高总盈利: {self.highest_total_profit:.2f}%")
784
- self.send_feishu_notification(
785
- f"{symbol} 档位[{self.current_tier} ]: 当前总盈利: {total_profit:.2f}%,最高总盈利: {self.highest_total_profit:.2f}%")
826
+ f"{symbol} 档位[{current_tier}]: 当前总盈利: {total_profit:.2f}%,最高总盈利: {cur_highest_total_profit:.2f}%")
786
827
 
787
828
  '''
788
- 第一档 低档保护止盈:当盈利达到0.3%触发,要么到第二档,要么回到0.2%止盈
829
+ 第一档:低档保护止盈:当盈利达到0.3%触发,要么到第二档,要么回到0.2%止盈
789
830
  第二档:盈利达到1%触发,记录最高价,最高价的80%是止盈位
790
831
  第三档:盈利达到3%触发,记录最高价,最高价的75%是止盈位
832
+
791
833
  '''
792
834
  # 各档止盈逻辑
793
835
 
794
- if self.current_tier == "低档保护止盈":
795
- self.logger.info(f"{symbol} 低档回撤止盈阈值: {self.low_trail_stop_loss_pct:.2f}%")
796
- if total_profit >= self.low_trail_stop_loss_pct:
797
-
798
- sl_price = self.calculate_stop_loss_price(symbol=symbol, position=position,stop_loss_pct=self.low_trail_stop_loss_pct )
799
- # tp_price = self.calculate_take_profile_price(symbol=symbol, position=position,stop_loss_pct=self.low_trail_stop_loss_pct)
800
- # 判断止盈价格是否变化,无变化不需要设置
801
- latest_sl_price = self.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,0.0)
802
- if sl_price == latest_sl_price:
803
- self.logger.debug(f"{symbol} 止盈价格未变化,不设置")
804
- return
805
- if_success = self.set_stop_loss_take_profit(symbol, position, stop_loss_price=sl_price )
806
- if if_success:
807
- self.logger.info(f"{symbol} 总盈利触发低档保护止盈,当前回撤到: {total_profit:.2f}%,市场价格:{position['markPrice']},设置止盈位: {sl_price:.9f}")
808
- self.global_symbol_stop_loss_price[symbol] = sl_price
809
- self.reset_highest_profit_and_tier()
810
- self.send_feishu_notification(f"{symbol} 总盈利触发低档保护止盈,当前回撤到: {total_profit:.2f}%,市场价格:{position['markPrice']},设置止盈位: {sl_price:.9f}")
836
+ # 根据不同档位设置止损价格和通知
837
+ tier_config = {
838
+ "低档": {
839
+ "stop_loss_pct": float(self.low_trail_stop_loss_pct)
840
+ },
841
+ "中档": {
842
+ "stop_loss_pct": float(self.trail_stop_loss_pct)
843
+ },
844
+ "高档": {
845
+ "stop_loss_pct": float(self.higher_trail_stop_loss_pct)
846
+ }
847
+ }
848
+
849
+ if current_tier in tier_config:
850
+ config = tier_config[current_tier]
851
+
852
+ # 记录日志
853
+ self.logger.debug(f"{symbol} 回撤止盈阈值: {config['stop_loss_pct']*100}%")
854
+
855
+ # 计算回撤止损价格
856
+ sl_price = self.calculate_stop_loss_price(
857
+ symbol=symbol,
858
+ position=position,
859
+ stop_loss_pct=config['stop_loss_pct']
860
+ )
861
+
862
+ # 检查价格是否变化
863
+ latest_sl_price = self.exchange.safe_float(self.global_symbol_stop_loss_price, symbol, 0.0)
864
+ if sl_price == latest_sl_price:
865
+ self.logger.debug(f"{symbol} 回撤止损价格{latest_sl_price}未变化,不设置")
811
866
  return
812
- elif self.current_tier == "第一档移动止盈":
813
- trail_stop_loss = self.highest_total_profit * (1 - self.trail_stop_loss_pct)
814
- self.logger.info(f"{symbol} 第一档回撤止盈阈值: {trail_stop_loss:.2f}%")
815
- if total_profit >= trail_stop_loss:
816
- sl_price = self.calculate_stop_loss_price(symbol=symbol, position=position,stop_loss_pct=self.trail_stop_loss_pct )
817
- # 判断止盈价格是否变化,无变化不需要设置
818
- latest_sl_price = self.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,0.0)
819
- if sl_price == latest_sl_price :
820
- self.logger.debug(f"{symbol} 止盈价格未变化,不设置")
821
- return
822
- if_success = self.set_stop_loss_take_profit(symbol, position, stop_loss_price=sl_price)
823
- if if_success:
824
- self.logger.info(
825
- f"{symbol} 总盈利达到第一档回撤阈值,最高总盈利: {self.highest_total_profit:.2f}%,当前回撤到: {total_profit:.2f}%,市场价格: {position['markPrice']},设置止盈位: {sl_price:.9f}")
826
- # 记录一下止盈价格
827
- self.global_symbol_stop_loss_price[symbol] = float(sl_price)
828
- self.reset_highest_profit_and_tier()
829
- self.send_feishu_notification(
830
- f"{symbol} 总盈利达到第一档回撤阈值,最高总盈利: {self.highest_total_profit:.2f}%,当前回撤到: {total_profit:.2f}%,市场价格: {position['markPrice']}, 设置止盈位: {sl_price:.9f}")
831
- return
832
-
833
- elif self.current_tier == "第二档移动止盈":
834
- trail_stop_loss = self.highest_total_profit * (1 - self.higher_trail_stop_loss_pct)
835
- self.logger.info(f"{symbol} 第二档回撤止盈阈值: {trail_stop_loss:.2f}%")
836
- if total_profit >= trail_stop_loss:
837
- sl_price = self.calculate_stop_loss_price(symbol=symbol, position=position,stop_loss_pct=self.higher_trail_stop_loss_pct)
838
- # 判断止盈价格是否变化,无变化不需要设置
839
- latest_sl_price = self.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,0.0)
840
- if sl_price == latest_sl_price:
841
- self.logger.debug(f"{symbol} 止盈价格未变化,不设置")
842
- return
843
- if_success = self.set_stop_loss_take_profit(symbol, position, stop_loss_price=sl_price)
844
- if if_success:
845
- self.logger.info(f"{symbol} 总盈利达到第二档回撤阈值,最高总盈利: {self.highest_total_profit:.2f}%,当前回撤到: {total_profit:.2f}%,市场价格: {position['markPrice']},设置止盈位: {sl_price:.9f}")
846
- # 记录一下止盈价格
847
- self.global_symbol_stop_loss_price[symbol] = sl_price
848
- self.reset_highest_profit_and_tier()
849
- self.send_feishu_notification(f"{symbol} 总盈利达到第二档回撤阈值,最高总盈利: {self.highest_total_profit:.2f}%,当前回撤到: {total_profit:.2f}%,市场价格: {position['markPrice']},设置止盈位: {sl_price:.9f}")
850
- return
851
- else :
852
- self.logger.info(f"{symbol} 全局止损阈值: {self.stop_loss_pct:.2f}%")
867
+
868
+ # 设置止损
869
+ if_success = self.set_stop_loss_take_profit(symbol, position, stop_loss_price=sl_price)
853
870
 
854
- self.set_global_stop_loss(symbol, position)
871
+ if if_success:
872
+ # 更新回撤止损价格
873
+
874
+ self.global_symbol_stop_loss_price[symbol] = sl_price
875
+ self.global_symbol_stop_loss_flag[symbol] = True
876
+
877
+ # 发送通知
878
+ msg = (f"{symbol}: 盈利达到【{current_tier}】阈值,最高总盈利: {cur_highest_total_profit:.2f}%,"
879
+ f"当前盈利回撤到: {total_profit:.2f}%,市场价格:{position['markPrice']},"
880
+ f"设置回撤止损位: {sl_price:.9f}")
881
+ self.logger.info(msg)
882
+ self.send_feishu_notification(msg)
883
+
884
+ else:
855
885
 
856
- return
886
+ # 默认全局止损
887
+ self.set_global_stop_loss(symbol, position)
888
+ self.logger.info(f"{symbol} 全局止损阈值: {self.stop_loss_pct:.2f}%")
889
+
890
+ return
857
891
 
858
892
  def monitor_total_profit(self):
859
893
  self.logger.info("启动主循环,开始监控总盈利...")
860
- previous_position_size = sum(
861
- abs(float(position['contracts'])) for position in self.fetch_positions()) # 初始总仓位大小
894
+ # previous_position_size = sum(
895
+ # abs(float(position['contracts'])) for position in self.fetch_positions()) # 初始总仓位大小
862
896
  while True:
863
897
  try:
864
898
 
@@ -868,21 +902,31 @@ class MultiAssetNewTradingBot:
868
902
  # self.logger.debug("没有持仓,等待下一次检查...")
869
903
  self.reset_highest_profit_and_tier()
870
904
  self.reset_take_profie()
905
+ self.positions_entry_price= {}
871
906
  time.sleep(1)
872
907
  continue
908
+
873
909
  self.logger.info("+" * 60)
874
910
  # 检查仓位总规模变化
875
- current_position_size = sum(abs(float(position['contracts'])) for position in self.fetch_positions())
876
- if current_position_size > previous_position_size:
877
- self.send_feishu_notification(f"检测到仓位变化操作,重置最高盈利和档位状态")
878
- self.logger.info("检测到新增仓位操作,重置最高盈利和档位状态")
879
- self.reset_highest_profit_and_tier()
880
- previous_position_size = current_position_size
881
- time.sleep(0.1)
882
- continue # 跳过本次循环
911
+ # current_position_size = sum(abs(float(position['contracts'])) for position in self.fetch_positions())
912
+ # if current_position_size > previous_position_size:
913
+ # self.send_feishu_notification(f"检测到仓位变化操作,重置最高盈利和档位状态")
914
+ # self.logger.info("检测到新增仓位操作,重置最高盈利和档位状态")
915
+ # self.reset_highest_profit_and_tier()
916
+ # previous_position_size = current_position_size
917
+ # time.sleep(0.1)
918
+ # continue # 跳过本次循环
883
919
 
884
920
  for position in positions:
885
921
  symbol = position['symbol']
922
+ cur_entry_price = position['entryPrice']
923
+ # 检查仓位是否有变化
924
+ if symbol in self.positions_entry_price and cur_entry_price != self.positions_entry_price[symbol]:
925
+ # 新开仓
926
+ self.reset_highest_profit_and_tier(symbol)
927
+ self.reset_take_profie(symbol)
928
+ self.positions_entry_price[symbol] = cur_entry_price
929
+
886
930
  self.check_total_profit(symbol, position)
887
931
  time.sleep(0.1)
888
932
  # 检查仓位和挂单是否有问题
@@ -892,8 +936,9 @@ class MultiAssetNewTradingBot:
892
936
  time.sleep(self.monitor_interval)
893
937
 
894
938
  except Exception as e:
895
- print(e)
939
+ # print(e)
896
940
  error_message = f"程序异常退出: {str(e)}"
941
+ traceback.print_exc()
897
942
  self.logger.error(error_message)
898
943
  self.send_feishu_notification(error_message)
899
944
  continue
taker/main.py CHANGED
@@ -2,7 +2,8 @@ import logging
2
2
  import yaml
3
3
  from logging.handlers import TimedRotatingFileHandler
4
4
 
5
- from taker.MultiAssetNewTradingBot import MultiAssetNewTradingBot
5
+ from taker.TrailingSLTaker import TrailingSLTaker
6
+ from taker.TrailingSLAndTPTaker import TrailingSLAndTPTaker
6
7
  from taker.ThreeLineTradingBot import ThreeLineTradingBot
7
8
 
8
9
  def build_logger(log_config) -> logging.Logger:
@@ -56,7 +57,7 @@ def main():
56
57
  package_name = __package__ or "taker"
57
58
 
58
59
  logger.info(f" ++ {package_name}:{version} is doing...")
59
- bot = MultiAssetNewTradingBot(config_data, platform_config, feishu_webhook=feishu_webhook_url, monitor_interval=monitor_interval,logger=logger)
60
+ bot = TrailingSLAndTPTaker(config_data, platform_config, feishu_webhook=feishu_webhook_url, monitor_interval=monitor_interval,logger=logger)
60
61
  bot.monitor_total_profit()
61
62
  # bot = ThreeLineTradingBot(platform_config, feishu_webhook=feishu_webhook_url, monitor_interval=monitor_interval)
62
63
  # bot.monitor_klines()