openfund-taker 1.2.13__py3-none-any.whl → 1.3.1__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-1.2.13.dist-info → openfund_taker-1.3.1.dist-info}/METADATA +2 -2
- {openfund_taker-1.2.13.dist-info → openfund_taker-1.3.1.dist-info}/RECORD +6 -6
- taker/{MultiAssetNewTradingBot.py → TrailingSLTaker.py} +324 -277
- taker/main.py +2 -2
- {openfund_taker-1.2.13.dist-info → openfund_taker-1.3.1.dist-info}/WHEEL +0 -0
- {openfund_taker-1.2.13.dist-info → openfund_taker-1.3.1.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: openfund-taker
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.1
|
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.
|
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,6 @@
|
|
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/TrailingSLTaker.py,sha256=vYUrE-UO6s4xkDyToPoj_65-wsnuXnnCQT70X6Hmr30,42228
|
4
4
|
taker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
taker/chua_bitget.py,sha256=YY6XK5Bd-wlArsN5BnAfiqE0h1DPkti6i4TEB4F5zDA,12918
|
6
6
|
taker/chua_bn.py,sha256=GnTePWlgDwdHgroBbEp1Ajcsf5_m_Vn_RV63SYzu2jI,10668
|
@@ -8,8 +8,8 @@ taker/chua_ok.py,sha256=5pPAoEYbFuKxfZwqNvOO890s-2cy6n69QiI0ZA0GTCQ,12474
|
|
8
8
|
taker/chua_ok_all.py,sha256=2XnZM6QdB3juSE1pqQIJyh2x1XuhlTlnBKNA3owlJ9E,15267
|
9
9
|
taker/chua_ok_bot.py,sha256=9SW0ujhi6PfN4yR1JZ9NaA37HtnXJ2QAWUfW52NG68w,13109
|
10
10
|
taker/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
|
11
|
-
taker/main.py,sha256=
|
12
|
-
openfund_taker-1.
|
13
|
-
openfund_taker-1.
|
14
|
-
openfund_taker-1.
|
15
|
-
openfund_taker-1.
|
11
|
+
taker/main.py,sha256=sNLe49TZZzE0FXmmGNzh8b3PyS5KnKS3SFcNKO8q8RU,2492
|
12
|
+
openfund_taker-1.3.1.dist-info/METADATA,sha256=iUV2Dwu936Z5WXcVOmmgwBQo1GLOWnldfW7LXfthHuU,7527
|
13
|
+
openfund_taker-1.3.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
14
|
+
openfund_taker-1.3.1.dist-info/entry_points.txt,sha256=a7mG8F7aOA5-Gk2vPWuAR4537faxaHUgM_jwIDBZoEc,50
|
15
|
+
openfund_taker-1.3.1.dist-info/RECORD,,
|
@@ -4,11 +4,15 @@ import time
|
|
4
4
|
import requests
|
5
5
|
import traceback
|
6
6
|
import pandas as pd
|
7
|
-
|
8
|
-
|
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
|
|
15
|
+
|
12
16
|
self.stop_loss_pct = platform_config["all_stop_loss_pct"] # 全局止损百分比
|
13
17
|
# 止盈比例
|
14
18
|
self.low_trail_stop_loss_pct = platform_config["all_low_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 =
|
26
|
-
|
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 =
|
32
|
-
self.min_market_price =
|
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
|
-
|
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
|
121
|
-
#
|
122
|
-
df = pd.DataFrame(
|
123
|
-
|
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
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
return {
|
140
|
-
'cross': -1, # 无交叉
|
141
|
-
'index': None
|
142
|
-
}
|
133
|
+
# 根据position方向寻找孤立点
|
134
|
+
side = position['side']
|
135
|
+
window = 1 # 设置窗口大小,用于判断局部最值
|
143
136
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
#
|
166
|
-
|
167
|
-
#
|
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
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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
|
-
|
228
|
-
|
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
|
-
|
238
|
-
|
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
|
-
#
|
263
|
-
|
264
|
-
|
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.
|
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
|
-
|
281
|
+
order_stop_loss_pct = self.stop_loss_pct
|
282
|
+
self.logger.info(f"{symbol}: 持仓方向={side} 与 正确方向={correct_side} 相同 , 恢复正常。")
|
284
283
|
|
285
|
-
|
286
|
-
self.
|
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.
|
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
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
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
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
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
|
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
|
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}
|
470
|
+
self.logger.debug(f"{symbol}: {orderSide} - TP at {take_profit_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
|
-
|
481
|
+
|
482
|
+
self.logger.info(f"{symbol}: TP at {take_profit_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}
|
553
|
+
self.logger.debug(f"{symbol}: {orderSide} - Pre SL at {stop_loss_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.
|
565
|
+
self.logger.info(f"{symbol}: SL at {stop_loss_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
|
-
|
597
|
-
|
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}
|
647
|
+
self.logger.debug(f"{symbol}: - {side} ,全局止损价没变化: {last_sl_price} = {sl_order_price}")
|
617
648
|
return
|
618
649
|
|
619
650
|
try:
|
@@ -656,6 +687,7 @@ class MultiAssetNewTradingBot:
|
|
656
687
|
|
657
688
|
return take_profile_price
|
658
689
|
|
690
|
+
# 计算回撤止盈价格
|
659
691
|
def calculate_stop_loss_price(self, symbol, position, stop_loss_pct, offset=1) -> float:
|
660
692
|
tick_size = float(self.exchange.market(symbol)['precision']['price'])
|
661
693
|
market_price = position['markPrice']
|
@@ -665,15 +697,17 @@ class MultiAssetNewTradingBot:
|
|
665
697
|
# 计算止盈价格,用市场价格(取持仓期间历史最高)减去开仓价格的利润,再乘以不同阶段的止盈百分比。
|
666
698
|
latest_stop_loss_price = self.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,None)
|
667
699
|
if side == 'long':
|
668
|
-
self.max_market_price
|
669
|
-
|
700
|
+
last_max_market_price = self.max_market_price.get(symbol,0.0)
|
701
|
+
self.max_market_price[symbol] = max(market_price,last_max_market_price)
|
702
|
+
base_price = abs(self.max_market_price[symbol] - entry_price) * (1-stop_loss_pct)
|
670
703
|
stop_loss_price = entry_price + base_price - offset * tick_size
|
671
704
|
if latest_stop_loss_price :
|
672
705
|
stop_loss_price = max(stop_loss_price,latest_stop_loss_price)
|
673
706
|
|
674
707
|
elif side == 'short':
|
675
|
-
|
676
|
-
|
708
|
+
last_min_market_price = self.min_market_price.get(symbol,float('inf'))
|
709
|
+
self.min_market_price[symbol] = min(market_price,last_min_market_price)
|
710
|
+
base_price = abs(self.min_market_price[symbol] - entry_price) * (1-stop_loss_pct)
|
677
711
|
stop_loss_price = entry_price - base_price + offset * tick_size
|
678
712
|
if latest_stop_loss_price :
|
679
713
|
stop_loss_price = min(stop_loss_price,latest_stop_loss_price)
|
@@ -714,7 +748,7 @@ class MultiAssetNewTradingBot:
|
|
714
748
|
params=params
|
715
749
|
)
|
716
750
|
time.sleep(0.1) # 短暂延迟后再试
|
717
|
-
self.reset_take_profie()
|
751
|
+
self.reset_take_profie(symbol)
|
718
752
|
self.logger.info(f"{symbol} Close position response for {symbol}: {order}")
|
719
753
|
self.send_feishu_notification(f"{symbol} 平仓订单完全成交 -{symbol} side: {side}")
|
720
754
|
|
@@ -733,25 +767,27 @@ class MultiAssetNewTradingBot:
|
|
733
767
|
"""
|
734
768
|
latest_stop_loss_price = self.exchange.safe_float(self.global_symbol_stop_loss_price, symbol, 0.0)
|
735
769
|
if latest_stop_loss_price == 0.0:
|
736
|
-
self.logger.warning(f"{symbol} 未设置止损价格,执行平仓")
|
770
|
+
self.logger.warning(f"{symbol}: 未设置止损价格,执行平仓")
|
737
771
|
return True
|
738
772
|
|
739
773
|
mark_price = position['markPrice']
|
740
774
|
side = position['side']
|
741
775
|
|
742
|
-
|
743
|
-
|
776
|
+
sl_price = float(self.round_price_to_tick(symbol=symbol,price=latest_stop_loss_price))
|
777
|
+
|
778
|
+
if side == 'long' and mark_price < sl_price:
|
779
|
+
self.logger.warning(f"{'*'*60}\n[非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 低于止盈 {latest_stop_loss_price},触发全局止盈\n{'*'*60}")
|
744
780
|
return True
|
745
|
-
elif side == 'short' and mark_price >
|
746
|
-
self.logger.warning(f"
|
781
|
+
elif side == 'short' and mark_price > sl_price:
|
782
|
+
self.logger.warning(f"{'*'*60}\n[非正常关闭]: {symbol} 方向 {side} - 市场价格 {mark_price} 高于止盈价 {latest_stop_loss_price},触发全局止盈\n{'*'*60}")
|
747
783
|
return True
|
748
784
|
|
749
785
|
return False
|
750
786
|
|
751
787
|
def check_position(self, symbol, position):
|
752
788
|
# 清理趋势相反的仓位
|
753
|
-
|
754
|
-
|
789
|
+
pair_config = self.trading_pairs_config.get(symbol, {})
|
790
|
+
self.check_reverse_position(symbol=symbol, position=position, pair_config=pair_config)
|
755
791
|
|
756
792
|
# 检查止损是否触发止盈
|
757
793
|
if self.check_stop_loss_trigger(symbol, position):
|
@@ -760,105 +796,105 @@ class MultiAssetNewTradingBot:
|
|
760
796
|
|
761
797
|
|
762
798
|
def check_total_profit(self, symbol, position):
|
763
|
-
|
799
|
+
"""
|
800
|
+
检查当前总盈利
|
801
|
+
"""
|
802
|
+
|
764
803
|
total_profit = self.calculate_average_profit(symbol, position)
|
765
804
|
if total_profit > 0.0 :
|
766
805
|
self.logger.info(f"{symbol} 当前总盈利: {total_profit:.2f}%")
|
767
806
|
self.send_feishu_notification(f"{symbol} 当前总盈利: {total_profit:.2f}%")
|
768
|
-
|
769
|
-
|
807
|
+
|
808
|
+
cur_highest_total_profit = self.highest_total_profit.get(symbol, 0.0)
|
809
|
+
|
810
|
+
if total_profit > cur_highest_total_profit:
|
811
|
+
cur_highest_total_profit = total_profit
|
812
|
+
self.highest_total_profit[symbol] = total_profit
|
813
|
+
|
814
|
+
current_tier = '无'
|
770
815
|
# 确定当前盈利档位
|
771
|
-
if
|
772
|
-
|
816
|
+
if cur_highest_total_profit >= self.second_trail_profit_threshold:
|
817
|
+
current_tier = "高档"
|
773
818
|
|
774
|
-
elif
|
775
|
-
|
819
|
+
elif cur_highest_total_profit>= self.first_trail_profit_threshold:
|
820
|
+
current_tier = "中档"
|
776
821
|
|
777
|
-
elif
|
778
|
-
|
822
|
+
elif cur_highest_total_profit >= self.low_trail_profit_threshold:
|
823
|
+
current_tier = "低档"
|
779
824
|
|
780
825
|
|
781
826
|
if total_profit > 0.0 :
|
782
827
|
self.logger.info(
|
783
|
-
f"{symbol} 档位[{
|
784
|
-
self.send_feishu_notification(
|
785
|
-
f"{symbol} 档位[{self.current_tier} ]: 当前总盈利: {total_profit:.2f}%,最高总盈利: {self.highest_total_profit:.2f}%")
|
828
|
+
f"{symbol} 档位[{current_tier}]: 当前总盈利: {total_profit:.2f}%,最高总盈利: {cur_highest_total_profit:.2f}%")
|
786
829
|
|
787
830
|
'''
|
788
|
-
|
831
|
+
第一档:低档保护止盈:当盈利达到0.3%触发,要么到第二档,要么回到0.2%止盈
|
789
832
|
第二档:盈利达到1%触发,记录最高价,最高价的80%是止盈位
|
790
833
|
第三档:盈利达到3%触发,记录最高价,最高价的75%是止盈位
|
834
|
+
|
791
835
|
'''
|
792
836
|
# 各档止盈逻辑
|
793
837
|
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
838
|
+
# 根据不同档位设置止损价格和通知
|
839
|
+
tier_config = {
|
840
|
+
"低档": {
|
841
|
+
"stop_loss_pct": float(self.low_trail_stop_loss_pct)
|
842
|
+
},
|
843
|
+
"中档": {
|
844
|
+
"stop_loss_pct": float(self.trail_stop_loss_pct)
|
845
|
+
},
|
846
|
+
"高档": {
|
847
|
+
"stop_loss_pct": float(self.higher_trail_stop_loss_pct)
|
848
|
+
}
|
849
|
+
}
|
850
|
+
|
851
|
+
if current_tier in tier_config:
|
852
|
+
config = tier_config[current_tier]
|
853
|
+
|
854
|
+
# 记录日志
|
855
|
+
self.logger.debug(f"{symbol} 回撤止盈阈值: {config['stop_loss_pct']*100}%")
|
856
|
+
|
857
|
+
# 计算回撤止损价格
|
858
|
+
sl_price = self.calculate_stop_loss_price(
|
859
|
+
symbol=symbol,
|
860
|
+
position=position,
|
861
|
+
stop_loss_pct=config['stop_loss_pct']
|
862
|
+
)
|
863
|
+
|
864
|
+
# 检查价格是否变化
|
865
|
+
latest_sl_price = self.exchange.safe_float(self.global_symbol_stop_loss_price, symbol, 0.0)
|
866
|
+
if sl_price == latest_sl_price:
|
867
|
+
self.logger.debug(f"{symbol} 回撤止损价格{latest_sl_price}未变化,不设置")
|
811
868
|
return
|
812
|
-
|
813
|
-
|
814
|
-
self.
|
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}%")
|
869
|
+
|
870
|
+
# 设置止损
|
871
|
+
if_success = self.set_stop_loss_take_profit(symbol, position, stop_loss_price=sl_price)
|
853
872
|
|
854
|
-
|
873
|
+
if if_success:
|
874
|
+
# 更新回撤止损价格
|
875
|
+
|
876
|
+
self.global_symbol_stop_loss_price[symbol] = sl_price
|
877
|
+
self.global_symbol_stop_loss_flag[symbol] = True
|
878
|
+
|
879
|
+
# 发送通知
|
880
|
+
msg = (f"{symbol}: 盈利达到【{current_tier}】阈值,最高总盈利: {cur_highest_total_profit:.2f}%,"
|
881
|
+
f"当前盈利回撤到: {total_profit:.2f}%,市场价格:{position['markPrice']},"
|
882
|
+
f"设置回撤止损位: {sl_price:.9f}")
|
883
|
+
self.logger.info(msg)
|
884
|
+
self.send_feishu_notification(msg)
|
885
|
+
|
886
|
+
else:
|
855
887
|
|
856
|
-
|
888
|
+
# 默认全局止损
|
889
|
+
self.set_global_stop_loss(symbol, position)
|
890
|
+
self.logger.info(f"{symbol} 全局止损阈值: {self.stop_loss_pct:.2f}%")
|
891
|
+
|
892
|
+
return
|
857
893
|
|
858
894
|
def monitor_total_profit(self):
|
859
895
|
self.logger.info("启动主循环,开始监控总盈利...")
|
860
|
-
previous_position_size = sum(
|
861
|
-
|
896
|
+
# previous_position_size = sum(
|
897
|
+
# abs(float(position['contracts'])) for position in self.fetch_positions()) # 初始总仓位大小
|
862
898
|
while True:
|
863
899
|
try:
|
864
900
|
|
@@ -868,21 +904,31 @@ class MultiAssetNewTradingBot:
|
|
868
904
|
# self.logger.debug("没有持仓,等待下一次检查...")
|
869
905
|
self.reset_highest_profit_and_tier()
|
870
906
|
self.reset_take_profie()
|
907
|
+
self.positions_entry_price= {}
|
871
908
|
time.sleep(1)
|
872
909
|
continue
|
910
|
+
|
873
911
|
self.logger.info("+" * 60)
|
874
912
|
# 检查仓位总规模变化
|
875
|
-
current_position_size = sum(abs(float(position['contracts'])) for position in self.fetch_positions())
|
876
|
-
if current_position_size > previous_position_size:
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
913
|
+
# current_position_size = sum(abs(float(position['contracts'])) for position in self.fetch_positions())
|
914
|
+
# if current_position_size > previous_position_size:
|
915
|
+
# self.send_feishu_notification(f"检测到仓位变化操作,重置最高盈利和档位状态")
|
916
|
+
# self.logger.info("检测到新增仓位操作,重置最高盈利和档位状态")
|
917
|
+
# self.reset_highest_profit_and_tier()
|
918
|
+
# previous_position_size = current_position_size
|
919
|
+
# time.sleep(0.1)
|
920
|
+
# continue # 跳过本次循环
|
883
921
|
|
884
922
|
for position in positions:
|
885
923
|
symbol = position['symbol']
|
924
|
+
cur_entry_price = position['entryPrice']
|
925
|
+
# 检查仓位是否有变化
|
926
|
+
if symbol in self.positions_entry_price and cur_entry_price != self.positions_entry_price[symbol]:
|
927
|
+
# 新开仓
|
928
|
+
self.reset_highest_profit_and_tier(symbol)
|
929
|
+
self.reset_take_profie(symbol)
|
930
|
+
self.positions_entry_price[symbol] = cur_entry_price
|
931
|
+
|
886
932
|
self.check_total_profit(symbol, position)
|
887
933
|
time.sleep(0.1)
|
888
934
|
# 检查仓位和挂单是否有问题
|
@@ -892,8 +938,9 @@ class MultiAssetNewTradingBot:
|
|
892
938
|
time.sleep(self.monitor_interval)
|
893
939
|
|
894
940
|
except Exception as e:
|
895
|
-
print(e)
|
941
|
+
# print(e)
|
896
942
|
error_message = f"程序异常退出: {str(e)}"
|
943
|
+
traceback.print_exc()
|
897
944
|
self.logger.error(error_message)
|
898
945
|
self.send_feishu_notification(error_message)
|
899
946
|
continue
|
taker/main.py
CHANGED
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
import yaml
|
3
3
|
from logging.handlers import TimedRotatingFileHandler
|
4
4
|
|
5
|
-
from taker.
|
5
|
+
from taker.TrailingSLTaker import TrailingSLTaker
|
6
6
|
from taker.ThreeLineTradingBot import ThreeLineTradingBot
|
7
7
|
|
8
8
|
def build_logger(log_config) -> logging.Logger:
|
@@ -56,7 +56,7 @@ def main():
|
|
56
56
|
package_name = __package__ or "taker"
|
57
57
|
|
58
58
|
logger.info(f" ++ {package_name}:{version} is doing...")
|
59
|
-
bot =
|
59
|
+
bot = TrailingSLTaker(config_data, platform_config, feishu_webhook=feishu_webhook_url, monitor_interval=monitor_interval,logger=logger)
|
60
60
|
bot.monitor_total_profit()
|
61
61
|
# bot = ThreeLineTradingBot(platform_config, feishu_webhook=feishu_webhook_url, monitor_interval=monitor_interval)
|
62
62
|
# bot.monitor_klines()
|
File without changes
|
File without changes
|