openfund-taker 2.2.6__py3-none-any.whl → 2.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openfund_taker-2.2.6.dist-info → openfund_taker-2.2.8.dist-info}/METADATA +1 -1
- openfund_taker-2.2.8.dist-info/RECORD +16 -0
- taker/SMCSLAndTPTaker.py +62 -82
- taker/ThreeLineTradingTaker.py +3 -1
- taker/TrailingSLAndTPTaker.py +6 -4
- taker/TrailingSLTaker.py +242 -143
- taker/main.py +17 -35
- openfund_taker-2.2.6.dist-info/RECORD +0 -17
- taker/MultiAssetOldTradingBot.py +0 -300
- {openfund_taker-2.2.6.dist-info → openfund_taker-2.2.8.dist-info}/WHEEL +0 -0
- {openfund_taker-2.2.6.dist-info → openfund_taker-2.2.8.dist-info}/entry_points.txt +0 -0
- /taker/{chua_bitget.py → history_code/chua_bitget.py} +0 -0
- /taker/{chua_bn.py → history_code/chua_bn.py} +0 -0
- /taker/{chua_ok.py → history_code/chua_ok.py} +0 -0
- /taker/{chua_ok_all.py → history_code/chua_ok_all.py} +0 -0
- /taker/{chua_ok_bot.py → history_code/chua_ok_bot.py} +0 -0
- /taker/{config.py → history_code/config.py} +0 -0
taker/TrailingSLTaker.py
CHANGED
@@ -6,50 +6,26 @@ import traceback
|
|
6
6
|
import pandas as pd
|
7
7
|
# import talib as ta
|
8
8
|
from decimal import Decimal
|
9
|
+
|
9
10
|
'''
|
10
11
|
自动设置移动止损单
|
11
12
|
'''
|
12
13
|
class TrailingSLTaker:
|
13
|
-
def __init__(self,g_config, platform_config, feishu_webhook=None, monitor_interval=4,logger=None):
|
14
|
-
self.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self.higher_trail_stop_loss_pct = platform_config["all_higher_trail_stop_loss_pct"]# 第三档
|
22
|
-
# 止损阈值
|
23
|
-
self.low_trail_profit_threshold = platform_config["all_low_trail_profit_threshold"]# 第一档
|
24
|
-
self.first_trail_profit_threshold = platform_config["all_first_trail_profit_threshold"]# 第二档
|
25
|
-
self.second_trail_profit_threshold = platform_config["all_second_trail_profit_threshold"]# 第三档
|
26
|
-
|
27
|
-
# 追踪止盈回撤
|
28
|
-
self.open_trail_tp = platform_config.get("open_trail_tp",False)# 是否开启追踪止盈
|
29
|
-
self.low_trail_tp_pct = platform_config["all_low_trail_tp_pct"]# 第一档
|
30
|
-
self.trail_tp_pct = platform_config["all_trail_tp_pct"]# 第二档
|
31
|
-
self.higher_trail_tp_pct = platform_config["all_higher_trail_tp_pct"]# 第三档
|
32
|
-
|
14
|
+
def __init__(self, g_config, platform_config, common_config=None, feishu_webhook=None, monitor_interval=4,logger=None):
|
15
|
+
self.g_config = g_config
|
16
|
+
|
17
|
+
if not common_config:
|
18
|
+
self.common_config = self.g_config.get('common', {})
|
19
|
+
else:
|
20
|
+
self.common_config = common_config
|
21
|
+
|
33
22
|
self.feishu_webhook = feishu_webhook
|
34
23
|
self.monitor_interval = monitor_interval # 监控循环时间是分仓监控的3倍
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# self.current_tier = {} # 记录当前的仓位模式
|
39
|
-
|
40
|
-
self.positions_entry_price = {} # 记录每个symbol的开仓价格
|
41
|
-
|
42
|
-
self.global_symbol_stop_loss_flag = {} # 记录每个symbol是否设置全局止损
|
43
|
-
self.global_symbol_stop_loss_price = {} # 记录每个symbol的止损价格
|
44
|
-
|
45
|
-
# 保留在止盈挂单中最高最低两个价格,计算止盈价格。
|
46
|
-
self.max_market_price = {}
|
47
|
-
self.min_market_price = {}
|
48
|
-
|
49
|
-
self.cross_directions = {} # 持仓期间,存储每个交易对的交叉方向
|
24
|
+
self.logger = logger
|
25
|
+
|
50
26
|
proxies = {
|
51
|
-
"http":
|
52
|
-
"https":
|
27
|
+
"http": self.common_config.get('proxy', "http://localhost:7890"),
|
28
|
+
"https": self.common_config.get('proxy', "http://localhost:7890")
|
53
29
|
}
|
54
30
|
|
55
31
|
# 配置交易所
|
@@ -62,11 +38,66 @@ class TrailingSLTaker:
|
|
62
38
|
'options': {'defaultType': 'future'},
|
63
39
|
'proxies': proxies
|
64
40
|
})
|
65
|
-
|
41
|
+
|
42
|
+
self.strategy_config = self.g_config.get('strategy', {})
|
43
|
+
self.trading_pairs_config = self.g_config.get('tradingPairs', {})
|
44
|
+
|
45
|
+
# base
|
46
|
+
|
47
|
+
self.stop_loss_pct = float(self.strategy_config.get("all_stop_loss_pct",2)) # 全局止损百分比
|
48
|
+
self.all_TP_SL_ratio = float(self.strategy_config.get("all_TP_SL_ratio",2))
|
49
|
+
self.leverage = int(self.strategy_config.get("leverage",1))
|
50
|
+
|
51
|
+
# trailing_strategy
|
52
|
+
self.trailing_strategy_config = self.strategy_config.get('trailing_strategy', {})
|
53
|
+
|
54
|
+
|
55
|
+
# 设置利润触发规则
|
56
|
+
self.open_trail_profit = self.trailing_strategy_config.get("open_trail_profit",False)# 是否开启追踪利润
|
57
|
+
self.low_trail_profit_threshold = self.trailing_strategy_config["all_low_trail_profit_threshold"]# 第一档
|
58
|
+
self.first_trail_profit_threshold = self.trailing_strategy_config["all_first_trail_profit_threshold"]# 第二档
|
59
|
+
self.second_trail_profit_threshold = self.trailing_strategy_config["all_second_trail_profit_threshold"]# 第三档
|
60
|
+
# 设置SL 推损规则
|
61
|
+
self.low_trail_stop_loss_pct = self.trailing_strategy_config["all_low_trail_stop_loss_pct"] # 第一档
|
62
|
+
self.trail_stop_loss_pct = self.trailing_strategy_config["all_trail_stop_loss_pct"]# 第二档
|
63
|
+
self.higher_trail_stop_loss_pct = self.trailing_strategy_config["all_higher_trail_stop_loss_pct"]# 第三档
|
64
|
+
# 设置TP 止盈规则
|
65
|
+
self.open_trail_tp = self.trailing_strategy_config.get("open_trail_tp",False)# 是否开启追踪止盈
|
66
|
+
self.low_trail_tp_pct = self.trailing_strategy_config["all_low_trail_tp_pct"]# 第一档
|
67
|
+
self.trail_tp_pct = self.trailing_strategy_config["all_trail_tp_pct"]# 第二档
|
68
|
+
self.higher_trail_tp_pct = self.trailing_strategy_config["all_higher_trail_tp_pct"]# 第三档
|
69
|
+
|
70
|
+
self.highest_total_profit = {} # 记录最高总盈利
|
71
|
+
self.positions_entry_price = {} # 记录每个symbol的开仓价格
|
72
|
+
self.global_symbol_stop_loss_flag = {} # 记录每个symbol是否设置全局止损
|
73
|
+
self.global_symbol_stop_loss_price = {} # 记录每个symbol的止损价格
|
74
|
+
|
75
|
+
# 保留在止盈挂单中最高最低两个价格,计算止盈价格。
|
76
|
+
self.max_market_price = {}
|
77
|
+
self.min_market_price = {}
|
78
|
+
self.cross_directions = {} # 持仓期间,存储每个交易对的交叉方向
|
79
|
+
|
66
80
|
self.position_mode = self.get_position_mode() # 获取持仓模式
|
67
81
|
|
68
82
|
def get_pair_config(self,symbol):
|
69
|
-
|
83
|
+
# 获取交易对特定配置,如果没有则使用全局策略配置
|
84
|
+
pair_config = self.trading_pairs_config.get(symbol, {})
|
85
|
+
|
86
|
+
# 使用字典推导式合并配置,trading_pairs_config优先级高于strategy_config
|
87
|
+
pair_config = {
|
88
|
+
**self.strategy_config, # 基础配置
|
89
|
+
**pair_config # 交易对特定配置会覆盖基础配置
|
90
|
+
}
|
91
|
+
return pair_config
|
92
|
+
|
93
|
+
def format_price(self, symbol, price:Decimal) -> str:
|
94
|
+
precision = self.get_precision_length(symbol)
|
95
|
+
return f"{price:.{precision}f}"
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def toDecimal(value):
|
99
|
+
return Decimal(str(value))
|
100
|
+
|
70
101
|
# 获取市场信息
|
71
102
|
def getMarket(self,symbol):
|
72
103
|
self.exchange.load_markets()
|
@@ -78,18 +109,19 @@ class TrailingSLTaker:
|
|
78
109
|
market = self.getMarket(symbol)
|
79
110
|
if market and 'precision' in market and 'price' in market['precision']:
|
80
111
|
|
81
|
-
return
|
112
|
+
return self.toDecimal(market['precision']['price'])
|
82
113
|
else:
|
83
|
-
self.logger.
|
84
|
-
return
|
114
|
+
# self.logger.error(f"{symbol}: 无法从市场数据中获取价格精度")
|
115
|
+
# return self.toDecimal("0.00001") # 返回默认精度
|
116
|
+
raise ValueError(f"{symbol}: 无法从市场数据中获取价格精度")
|
85
117
|
except Exception as e:
|
86
|
-
self.logger.
|
87
|
-
|
88
|
-
|
118
|
+
self.logger.error(f"{symbol}: 获取市场价格精度时发生错误: {str(e)}")
|
119
|
+
self.send_feishu_notification(f"{symbol}: 获取市场价格精度时发生错误: {str(e)}")
|
120
|
+
|
89
121
|
# 获取价格精度
|
90
122
|
def get_precision_length(self,symbol) -> int:
|
91
123
|
tick_size = self.get_tick_size(symbol)
|
92
|
-
return len(f"{tick_size:.
|
124
|
+
return len(f"{tick_size:.15f}".rstrip('0').split('.')[1]) if '.' in f"{tick_size:.15f}" else 0
|
93
125
|
# 获取当前持仓模式
|
94
126
|
def get_position_mode(self):
|
95
127
|
try:
|
@@ -200,10 +232,10 @@ class TrailingSLTaker:
|
|
200
232
|
return all(current_low <= prev_lows) and all(current_low <= next_lows)
|
201
233
|
|
202
234
|
|
203
|
-
def get_last_solated_point(self,symbol,position,kLines ,prd=20):
|
235
|
+
def get_last_solated_point(self,symbol,position, kLines ,prd=20):
|
204
236
|
# 将K线数据转换为DataFrame格式
|
205
237
|
df = self.format_klines(kLines)
|
206
|
-
|
238
|
+
|
207
239
|
# 根据position方向寻找孤立点
|
208
240
|
side = position['side']
|
209
241
|
window = prd # 设置窗口大小,用于判断局部最值
|
@@ -222,7 +254,7 @@ class TrailingSLTaker:
|
|
222
254
|
if self.is_pivot_low(df, i, window):
|
223
255
|
isolated_points.append(df.iloc[i])
|
224
256
|
break
|
225
|
-
|
257
|
+
|
226
258
|
return isolated_points
|
227
259
|
|
228
260
|
|
@@ -344,7 +376,7 @@ class TrailingSLTaker:
|
|
344
376
|
side = position['side']
|
345
377
|
try:
|
346
378
|
|
347
|
-
klines_period = str(pair_config.get('
|
379
|
+
klines_period = str(pair_config.get('CHF', '1m'))
|
348
380
|
klines = self.get_historical_klines_except_last(symbol=symbol,bar=klines_period)
|
349
381
|
|
350
382
|
self.logger.debug(f"开始监控 {symbol} : klines {klines_period} - {len(klines)}")
|
@@ -401,27 +433,26 @@ class TrailingSLTaker:
|
|
401
433
|
# 计算平均利润
|
402
434
|
def calculate_average_profit(self,symbol,position):
|
403
435
|
|
404
|
-
total_profit_pct = 0
|
436
|
+
total_profit_pct = Decimal('0')
|
405
437
|
num_positions = 0
|
406
438
|
|
407
|
-
entry_price =
|
408
|
-
current_price =
|
439
|
+
entry_price = self.toDecimal(position['entryPrice'])
|
440
|
+
current_price = self.toDecimal(position['markPrice'])
|
409
441
|
side = position['side']
|
410
442
|
|
411
443
|
# 计算单个仓位的浮动盈利百分比
|
412
|
-
if side
|
413
|
-
profit_pct = (current_price - entry_price) / entry_price * 100
|
414
|
-
elif side == 'short':
|
415
|
-
profit_pct = (entry_price - current_price) / entry_price * 100
|
416
|
-
else:
|
444
|
+
if side not in ['long', 'short']:
|
417
445
|
return
|
446
|
+
|
447
|
+
# 使用三元运算符简化计算逻辑
|
448
|
+
profit_pct = ((current_price - entry_price) if side == 'long' else (entry_price - current_price)) / entry_price * 100
|
418
449
|
|
419
450
|
# 累加总盈利百分比
|
420
451
|
total_profit_pct += profit_pct
|
421
452
|
num_positions += 1
|
422
|
-
|
423
453
|
# 记录单个仓位的盈利情况
|
424
|
-
self.
|
454
|
+
precision = self.get_precision_length(symbol)
|
455
|
+
self.logger.info(f"仓位 {symbol},方向: {side},开仓价格: {entry_price:.{precision}},当前价格: {current_price:.{precision}},"
|
425
456
|
f"浮动盈亏: {profit_pct:.2f}%")
|
426
457
|
|
427
458
|
# 计算平均浮动盈利百分比
|
@@ -454,15 +485,14 @@ class TrailingSLTaker:
|
|
454
485
|
self.min_market_price[symbol] = float('inf') # 初始化为浮点数最大值
|
455
486
|
self.cross_directions[symbol] = None
|
456
487
|
|
457
|
-
def round_price_to_tick(self,symbol, price) -> Decimal:
|
488
|
+
def round_price_to_tick(self, symbol, price:Decimal) -> Decimal:
|
458
489
|
tick_size = self.get_tick_size(symbol)
|
459
|
-
# 计算 tick_size
|
460
|
-
|
461
|
-
|
490
|
+
# 计算 tick_size 的小数位数
|
491
|
+
tick_decimals = self.get_precision_length(symbol)
|
462
492
|
# 调整价格为 tick_size 的整数倍
|
463
|
-
adjusted_price = round(
|
464
|
-
return adjusted_price
|
465
|
-
|
493
|
+
adjusted_price = round(price / tick_size) * tick_size
|
494
|
+
return self.toDecimal(f"{adjusted_price:.{tick_decimals}f}")
|
495
|
+
# return self.toDecimal(self.exchange.price_to_precision(symbol, price))
|
466
496
|
|
467
497
|
def cancel_all_algo_orders(self, symbol, attachType=None) -> bool:
|
468
498
|
"""_summary_
|
@@ -508,7 +538,7 @@ class TrailingSLTaker:
|
|
508
538
|
except Exception as e:
|
509
539
|
self.logger.error(f"{symbol} Error cancelling order {algo_ids}: {e}")
|
510
540
|
|
511
|
-
def set_stop_loss_take_profit(self, symbol, position, stop_loss_price=None,take_profit_price=None) -> bool:
|
541
|
+
def set_stop_loss_take_profit(self, symbol, position, stop_loss_price:Decimal=None,take_profit_price:Decimal=None) -> bool:
|
512
542
|
if not stop_loss_price :
|
513
543
|
self.logger.warning(f"{symbol}: No stop loss price or take profit price provided for {symbol}")
|
514
544
|
return False
|
@@ -525,10 +555,10 @@ class TrailingSLTaker:
|
|
525
555
|
|
526
556
|
|
527
557
|
|
528
|
-
def set_take_profit(self, symbol, position, take_profit_price=None) -> bool:
|
558
|
+
def set_take_profit(self, symbol, position, take_profit_price:Decimal=None) -> bool:
|
529
559
|
|
530
560
|
# 计算下单数量
|
531
|
-
amount = abs(
|
561
|
+
amount = abs(self.toDecimal(position['contracts']))
|
532
562
|
|
533
563
|
if amount <= 0:
|
534
564
|
self.logger.warning(f"{symbol}: amount is 0 for {symbol}")
|
@@ -536,13 +566,13 @@ class TrailingSLTaker:
|
|
536
566
|
|
537
567
|
|
538
568
|
# 止损单逻辑
|
539
|
-
adjusted_price = self.
|
569
|
+
adjusted_price = self.format_price(symbol, take_profit_price)
|
540
570
|
|
541
571
|
tp_params = {
|
542
572
|
|
543
573
|
|
544
|
-
'tpTriggerPx':
|
545
|
-
'tpOrdPx' :
|
574
|
+
'tpTriggerPx':adjusted_price,
|
575
|
+
'tpOrdPx' : adjusted_price,
|
546
576
|
'tpOrdKind': 'condition',
|
547
577
|
'tpTriggerPxType':'last',
|
548
578
|
|
@@ -608,7 +638,7 @@ class TrailingSLTaker:
|
|
608
638
|
|
609
639
|
|
610
640
|
|
611
|
-
def set_stop_loss(self, symbol, position, stop_loss_price=None , ord_type='conditional') -> bool:
|
641
|
+
def set_stop_loss(self, symbol, position, stop_loss_price:Decimal=None , ord_type='conditional') -> bool:
|
612
642
|
"""
|
613
643
|
设置止盈单
|
614
644
|
:param symbol: 交易对
|
@@ -618,18 +648,18 @@ class TrailingSLTaker:
|
|
618
648
|
:return: 是否成功设置止盈单
|
619
649
|
"""
|
620
650
|
# 计算下单数量
|
621
|
-
amount = abs(
|
651
|
+
amount = abs(self.toDecimal(position['contracts']))
|
622
652
|
|
623
653
|
if amount <= 0:
|
624
654
|
self.logger.warning(f"{symbol}: amount is 0 for {symbol}")
|
625
655
|
return
|
626
656
|
|
627
657
|
# 止损单逻辑
|
628
|
-
adjusted_price = self.
|
658
|
+
adjusted_price = self.format_price(symbol, stop_loss_price)
|
629
659
|
|
630
660
|
# 默认市价止损,委托价格为-1时,执行市价止损。
|
631
661
|
sl_params = {
|
632
|
-
'slTriggerPx':
|
662
|
+
'slTriggerPx':adjusted_price ,
|
633
663
|
'slOrdPx':'-1', # 委托价格为-1时,执行市价止损
|
634
664
|
# 'slOrdPx' : adjusted_price,
|
635
665
|
'slTriggerPxType':'last',
|
@@ -695,17 +725,26 @@ class TrailingSLTaker:
|
|
695
725
|
|
696
726
|
|
697
727
|
def calculate_sl_price_by_pct(self, symbol, position, sl_pct) -> Decimal:
|
728
|
+
# 根据持仓方向计算止损价格
|
698
729
|
side = position['side']
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
730
|
+
|
731
|
+
# 使用decimal进行精确计算
|
732
|
+
sl_pct_decimal = self.toDecimal(sl_pct) / 100
|
733
|
+
entry_price_decimal = self.toDecimal(position['entryPrice'])
|
734
|
+
|
735
|
+
if side == 'long':
|
736
|
+
# 多仓止损价 = 开仓价 * (1 - 止损百分比)
|
737
|
+
sl_price = entry_price_decimal * (1 - sl_pct_decimal)
|
738
|
+
elif side == 'short':
|
739
|
+
# 空仓止损价 = 开仓价 * (1 + 止损百分比)
|
740
|
+
sl_price = entry_price_decimal * (1 + sl_pct_decimal)
|
741
|
+
else:
|
742
|
+
raise ValueError(f"无效的持仓方向: {side}")
|
705
743
|
|
744
|
+
# 将止损价格四舍五入到合法的价格精度
|
706
745
|
sl_order_price = self.round_price_to_tick(symbol, sl_price)
|
707
746
|
|
708
|
-
return sl_order_price
|
747
|
+
return sl_order_price
|
709
748
|
|
710
749
|
def set_global_stop_loss(self, symbol, position, pair_config , stop_loss_pct=None, kLines=None) -> bool:
|
711
750
|
"""设置全局止损
|
@@ -716,8 +755,9 @@ class TrailingSLTaker:
|
|
716
755
|
pair_config: 交易对配置
|
717
756
|
|
718
757
|
"""
|
758
|
+
global_sl_price = self.global_symbol_stop_loss_price.get(symbol, 0.0)
|
719
759
|
# 如果已经触发过全局止损并且有止损单,则跳过
|
720
|
-
self.logger.debug(f"{symbol} : 是否设置过全局止损 {self.global_symbol_stop_loss_flag.get(symbol, False)} global_sl_price={self.
|
760
|
+
self.logger.debug(f"{symbol} : 是否设置过全局止损 {self.global_symbol_stop_loss_flag.get(symbol, False)} global_sl_price={self.format_price(symbol, global_sl_price)}")
|
721
761
|
if self.global_symbol_stop_loss_flag.get(symbol, False):
|
722
762
|
return
|
723
763
|
|
@@ -729,46 +769,55 @@ class TrailingSLTaker:
|
|
729
769
|
side = position['side']
|
730
770
|
|
731
771
|
sl_order_price = self.calculate_sl_price_by_pct(symbol, position, stop_loss_pct)
|
732
|
-
|
733
|
-
|
772
|
+
|
773
|
+
precision = self.get_precision_length(symbol)
|
734
774
|
# 验证止损价格是否合理20250312
|
735
|
-
mark_price =
|
775
|
+
mark_price = self.toDecimal(position['markPrice'])
|
736
776
|
# 验证止损价格是否合理,如果止损价格已经触发则直接平仓
|
737
777
|
if (side == 'long' and sl_order_price >= mark_price) or \
|
738
778
|
(side == 'short' and sl_order_price <= mark_price):
|
739
779
|
self.close_all_positions(symbol, position)
|
740
780
|
direction = "多头" if side == "long" else "空头"
|
741
|
-
self.logger.warning(f"{symbol}: !! {direction}止损价格= {sl_order_price} {'大于等于' if side=='long' else '小于等于'}市场价格= {mark_price},触发止损")
|
781
|
+
self.logger.warning(f"{symbol}: !! {direction}止损价格= {sl_order_price:.{precision}} {'大于等于' if side=='long' else '小于等于'}市场价格= {mark_price:.{precision}},触发止损")
|
742
782
|
return
|
743
783
|
|
744
784
|
# 250228 没有指定止损回撤阈值stop_loss_pct...
|
745
785
|
open_swing_point = pair_config.get('open_swing_point', False)
|
746
786
|
if open_swing_point :
|
747
787
|
if kLines is None :
|
748
|
-
klines_period = str(pair_config.get('
|
788
|
+
klines_period = str(pair_config.get('CHF', '1m'))
|
749
789
|
kLines = self.get_historical_klines(symbol=symbol,bar=klines_period)
|
750
790
|
|
751
791
|
swing_points_length = pair_config.get('swing_points_length', 20)
|
752
792
|
isolated_points = self.get_last_solated_point(symbol ,position , kLines,prd=swing_points_length)
|
753
|
-
|
793
|
+
# self.logger.debug(f"--------00 {isolated_points[0]['high']}" )
|
754
794
|
if len(isolated_points) > 0 :
|
795
|
+
# 获取最近的孤立点
|
755
796
|
last_isolated_point = isolated_points[0]
|
756
|
-
|
757
|
-
#
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
797
|
+
|
798
|
+
# 将孤立点价格转换为Decimal类型
|
799
|
+
isolated_high = self.toDecimal(last_isolated_point['high'])
|
800
|
+
isolated_low = self.toDecimal(last_isolated_point['low'])
|
801
|
+
|
802
|
+
# 根据持仓方向设置止损价格
|
803
|
+
sl_order_price = (
|
804
|
+
min(isolated_low, sl_order_price) if side == 'long'
|
805
|
+
else max(isolated_high, sl_order_price)
|
806
|
+
)
|
807
|
+
|
808
|
+
# 记录日志
|
809
|
+
self.logger.debug(
|
810
|
+
f"{symbol} : {side} 止损价={sl_order_price:.{precision}} \n"
|
811
|
+
f"孤立点高点={isolated_high:.{precision}} \n"
|
812
|
+
f"孤立点低点={isolated_low:.{precision}}"
|
813
|
+
)
|
766
814
|
|
767
815
|
|
768
816
|
last_sl_price= self.global_symbol_stop_loss_price.get(symbol,0.0)
|
769
817
|
if last_sl_price != 0.0 and last_sl_price == sl_order_price:
|
770
818
|
self.global_symbol_stop_loss_flag[symbol] = True
|
771
|
-
self.logger.debug(f"{symbol} : [{side}] 全局止损价没变化, {last_sl_price} = {sl_order_price}")
|
819
|
+
self.logger.debug(f"{symbol} : [{side}] 全局止损价没变化, {last_sl_price:.{precision}} = {sl_order_price:.{precision}}")
|
820
|
+
|
772
821
|
return
|
773
822
|
|
774
823
|
try:
|
@@ -781,7 +830,7 @@ class TrailingSLTaker:
|
|
781
830
|
)
|
782
831
|
if if_success:
|
783
832
|
# 设置全局止损标志
|
784
|
-
self.logger.info(f"{symbol} : [{side}] 设置全局止损价={sl_order_price}")
|
833
|
+
self.logger.info(f"{symbol} : [{side}] 设置全局止损价={sl_order_price:.{precision}}")
|
785
834
|
self.global_symbol_stop_loss_flag[symbol] = True
|
786
835
|
self.global_symbol_stop_loss_price[symbol] = sl_order_price
|
787
836
|
|
@@ -793,50 +842,90 @@ class TrailingSLTaker:
|
|
793
842
|
traceback.print_exc()
|
794
843
|
self.send_feishu_notification(error_msg)
|
795
844
|
|
796
|
-
def calculate_take_profile_price(self, symbol, position, take_profile_pct, offset=1) -> Decimal:
|
845
|
+
def calculate_take_profile_price(self, symbol: str, position: dict, take_profile_pct: float, offset: int = 1) -> Decimal:
|
846
|
+
"""计算止盈价格
|
847
|
+
Args:
|
848
|
+
symbol: 交易对
|
849
|
+
position: 持仓信息
|
850
|
+
take_profile_pct: 止盈百分比
|
851
|
+
offset: 价格偏移量
|
852
|
+
Returns:
|
853
|
+
Decimal: 止盈价格
|
854
|
+
"""
|
855
|
+
# 获取最小价格变动单位和开仓价格
|
797
856
|
tick_size = self.get_tick_size(symbol)
|
798
|
-
|
799
|
-
|
857
|
+
|
858
|
+
# 获取开仓价格并转换为Decimal类型
|
859
|
+
entry_price = self.toDecimal(position['entryPrice'])
|
860
|
+
|
861
|
+
# 获取持仓方向
|
800
862
|
side = position['side']
|
801
863
|
|
802
|
-
#
|
803
|
-
|
804
|
-
if side == 'long':
|
805
|
-
take_profile_price = Decimal(entry_price * (1+take_profile_pct/100)) - offset * tick_size
|
806
|
-
|
807
|
-
|
808
|
-
elif side == 'short':
|
864
|
+
# 将止盈百分比转换为小数形式的Decimal
|
865
|
+
take_profile_pct = self.toDecimal(take_profile_pct) / 100
|
809
866
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
867
|
+
# 计算价格偏移量
|
868
|
+
price_offset = offset * tick_size
|
869
|
+
|
870
|
+
# 根据持仓方向计算基础止盈价格
|
871
|
+
# 多仓: 开仓价 * (1 + 止盈率)
|
872
|
+
# 空仓: 开仓价 * (1 - 止盈率)
|
873
|
+
base_price = entry_price * (
|
874
|
+
1 + take_profile_pct if side == 'long'
|
875
|
+
else 1 - take_profile_pct
|
876
|
+
)
|
877
|
+
|
878
|
+
# 应用偏移量调整最终止盈价格
|
879
|
+
# 多仓: 基础价格 - 偏移量
|
880
|
+
# 空仓: 基础价格 + 偏移量
|
881
|
+
take_profile_price = (
|
882
|
+
base_price - price_offset if side == 'long'
|
883
|
+
else base_price + price_offset
|
884
|
+
)
|
885
|
+
|
886
|
+
# 按照价格精度四舍五入并返回
|
887
|
+
return self.round_price_to_tick(symbol, take_profile_price)
|
814
888
|
|
815
889
|
# 计算回撤止盈价格
|
816
|
-
def calculate_stop_loss_price(self, symbol, position, stop_loss_pct, offset=1) ->
|
890
|
+
def calculate_stop_loss_price(self, symbol, position, stop_loss_pct, offset=1) -> Decimal:
|
817
891
|
tick_size = self.get_tick_size(symbol)
|
818
|
-
market_price = position['markPrice']
|
819
|
-
entry_price = position['entryPrice']
|
892
|
+
market_price = self.toDecimal(position['markPrice'])
|
893
|
+
entry_price = self.toDecimal(position['entryPrice'])
|
820
894
|
side = position['side']
|
821
895
|
# base_price = abs(market_price-entry_price) * (1-stop_loss_pct)
|
822
896
|
# 计算止盈价格,用市场价格(取持仓期间历史最高)减去开仓价格的利润,再乘以不同阶段的止盈百分比。
|
823
|
-
latest_stop_loss_price = self.
|
897
|
+
latest_stop_loss_price = self.toDecimal(self.global_symbol_stop_loss_price.get(symbol,None))
|
898
|
+
# 多头仓位止损价计算
|
824
899
|
if side == 'long':
|
825
|
-
|
826
|
-
self.max_market_price
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
900
|
+
# 获取并更新最高价
|
901
|
+
last_max_market_price = self.toDecimal(self.max_market_price.get(symbol, 0.0))
|
902
|
+
self.max_market_price[symbol] = max(market_price, last_max_market_price)
|
903
|
+
|
904
|
+
# 计算基准价格和止损价格
|
905
|
+
price_diff = abs(self.max_market_price[symbol] - entry_price)
|
906
|
+
base_price = price_diff * self.toDecimal(1 - stop_loss_pct)
|
907
|
+
stop_loss_price = entry_price + base_price - offset * tick_size
|
908
|
+
|
909
|
+
# 如果已有止损价,取较高值
|
910
|
+
if latest_stop_loss_price:
|
911
|
+
stop_loss_price = max(stop_loss_price, latest_stop_loss_price)
|
831
912
|
|
913
|
+
# 空头仓位止损价计算
|
832
914
|
elif side == 'short':
|
833
|
-
|
834
|
-
self.min_market_price
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
915
|
+
# 获取并更新最低价
|
916
|
+
last_min_market_price = self.toDecimal(self.min_market_price.get(symbol, float('inf')))
|
917
|
+
self.min_market_price[symbol] = min(market_price, last_min_market_price)
|
918
|
+
|
919
|
+
# 计算基准价格和止损价格
|
920
|
+
price_diff = abs(self.min_market_price[symbol] - entry_price)
|
921
|
+
base_price = price_diff * self.toDecimal(1 - stop_loss_pct)
|
922
|
+
stop_loss_price = entry_price - base_price + offset * tick_size
|
923
|
+
|
924
|
+
# 如果已有止损价,取较低值
|
925
|
+
if latest_stop_loss_price:
|
926
|
+
stop_loss_price = min(stop_loss_price, latest_stop_loss_price)
|
927
|
+
|
928
|
+
return self.round_price_to_tick(symbol,stop_loss_price)
|
840
929
|
|
841
930
|
# 市价仓位平仓
|
842
931
|
def close_all_positions(self,symbol,position):
|
@@ -911,7 +1000,8 @@ class TrailingSLTaker:
|
|
911
1000
|
|
912
1001
|
def check_position(self, symbol, position):
|
913
1002
|
# 清理趋势相反的仓位
|
914
|
-
pair_config = self.trading_pairs_config.get(symbol, {})
|
1003
|
+
# pair_config = self.trading_pairs_config.get(symbol, {})
|
1004
|
+
pair_config = self.get_pair_config(symbol)
|
915
1005
|
self.check_reverse_position(symbol=symbol, position=position, pair_config=pair_config)
|
916
1006
|
|
917
1007
|
# 检查止损是否触发止盈
|
@@ -926,8 +1016,16 @@ class TrailingSLTaker:
|
|
926
1016
|
"""
|
927
1017
|
|
928
1018
|
total_profit = self.calculate_average_profit(symbol, position)
|
1019
|
+
|
1020
|
+
# 获取开仓价和市价,转为Decimal类型提高精度
|
1021
|
+
entry_price = self.toDecimal(position['entryPrice'])
|
1022
|
+
mark_price = self.toDecimal(position['markPrice'])
|
1023
|
+
precision = self.get_precision_length(symbol)
|
929
1024
|
|
930
|
-
|
1025
|
+
# 格式化消息,使用f-string提高可读性
|
1026
|
+
msg = (f"{symbol}: 盈利={total_profit:.2f}% "
|
1027
|
+
f"开仓={entry_price:.{precision}f} "
|
1028
|
+
f"市价={mark_price:.{precision}f}")
|
931
1029
|
self.logger.info(msg)
|
932
1030
|
self.send_feishu_notification(msg)
|
933
1031
|
|
@@ -1002,9 +1100,9 @@ class TrailingSLTaker:
|
|
1002
1100
|
)
|
1003
1101
|
|
1004
1102
|
# 检查价格是否变化
|
1005
|
-
latest_sl_price = self.
|
1103
|
+
latest_sl_price = self.toDecimal(self.global_symbol_stop_loss_price.get(symbol, 0.0))
|
1006
1104
|
if sl_price == latest_sl_price:
|
1007
|
-
self.logger.debug(f"{symbol}: 回撤止损价格{latest_sl_price}未变化,不设置")
|
1105
|
+
self.logger.debug(f"{symbol}: 回撤止损价格{latest_sl_price:.{precision}}未变化,不设置")
|
1008
1106
|
return
|
1009
1107
|
|
1010
1108
|
# 设置止损
|
@@ -1018,8 +1116,8 @@ class TrailingSLTaker:
|
|
1018
1116
|
|
1019
1117
|
# 发送通知
|
1020
1118
|
msg = (f"{symbol}: 盈利达到【{current_tier}】阈值,最高总盈利: {cur_highest_total_profit:.2f}%,"
|
1021
|
-
f"当前盈利回撤到: {total_profit:.2f}%,市场价格:{
|
1022
|
-
f"设置回撤止损位: {sl_price:.
|
1119
|
+
f"当前盈利回撤到: {total_profit:.2f}%,市场价格:{mark_price:.{precision}},"
|
1120
|
+
f"设置回撤止损位: {sl_price:.{precision}}")
|
1023
1121
|
self.logger.info(msg)
|
1024
1122
|
self.send_feishu_notification(msg)
|
1025
1123
|
|
@@ -1074,15 +1172,16 @@ class TrailingSLTaker:
|
|
1074
1172
|
|
1075
1173
|
for position in positions:
|
1076
1174
|
symbol = position['symbol']
|
1077
|
-
cur_entry_price = position['entryPrice']
|
1078
|
-
|
1175
|
+
cur_entry_price = self.toDecimal(position['entryPrice'])
|
1176
|
+
|
1079
1177
|
if symbol in self.positions_entry_price and cur_entry_price != self.positions_entry_price[symbol]:
|
1080
1178
|
# 新开仓
|
1081
1179
|
self.reset_all_cache(symbol)
|
1082
1180
|
|
1083
1181
|
if symbol not in self.positions_entry_price:
|
1084
1182
|
self.positions_entry_price[symbol] = cur_entry_price
|
1085
|
-
|
1183
|
+
precision = self.get_precision_length(symbol)
|
1184
|
+
msg = f"{symbol} : ## 重新开仓。 入场方向={position['side']} 入场价格={cur_entry_price:.{precision}} ##"
|
1086
1185
|
self.logger.info(msg)
|
1087
1186
|
self.send_feishu_notification(msg)
|
1088
1187
|
|