openfund-taker 2.2.6__py3-none-any.whl → 2.2.7__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.
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.trading_pairs_config = g_config.get('tradingPairs', {})
15
-
16
-
17
- self.stop_loss_pct = float(platform_config.get("all_stop_loss_pct",2)) # 全局止损百分比
18
- # 回撤比例
19
- self.low_trail_stop_loss_pct = platform_config["all_low_trail_stop_loss_pct"] # 第一档
20
- self.trail_stop_loss_pct = platform_config["all_trail_stop_loss_pct"]# 第二档
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
- self.highest_total_profit = {} # 记录最高总盈利
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": g_config.get('proxy', "http://localhost:7890"),
52
- "https": g_config.get('proxy', "http://localhost:7890")
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
- self.logger = logger
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
- return self.trading_pairs_config.get(symbol,{})
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 Decimal(str(market['precision']['price']))
112
+ return self.toDecimal(market['precision']['price'])
82
113
  else:
83
- self.logger.warn(f"{symbol}: 无法从市场数据中获取价格精度")
84
- return Decimal(0.00001) # 返回默认精度
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.warn(f"{symbol}: 获取市场价格精度时发生错误: {str(e)}")
87
- return Decimal(0.00001) # 发生异常时返回默认精度
88
- # return float(self.getMarket(symbol)['precision']['price'])
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:.10f}".rstrip('0').split('.')[1]) if '.' in f"{tick_size:.10f}" else 0
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('klines_period', '1m'))
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)}")
@@ -404,24 +436,23 @@ class TrailingSLTaker:
404
436
  total_profit_pct = 0.0
405
437
  num_positions = 0
406
438
 
407
- entry_price = float(position['entryPrice'])
408
- current_price = float(position['markPrice'])
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 == 'long':
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.logger.info(f"仓位 {symbol},方向: {side},开仓价格: {entry_price},当前价格: {current_price},"
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
- # tick_decimals = len(f"{tick_size:.10f}".rstrip('0').split('.')[1]) if '.' in f"{tick_size:.10f}" else 0
461
-
490
+ # 计算 tick_size 的小数位数
491
+ tick_decimals = self.get_precision_length(symbol)
462
492
  # 调整价格为 tick_size 的整数倍
463
- adjusted_price = round(Decimal(price) / tick_size) * tick_size
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(float(position['contracts']))
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.round_price_to_tick(symbol, take_profit_price)
569
+ adjusted_price = self.format_price(symbol, take_profit_price)
540
570
 
541
571
  tp_params = {
542
572
 
543
573
 
544
- 'tpTriggerPx':str(adjusted_price),
545
- 'tpOrdPx' : str(adjusted_price),
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(float(position['contracts']))
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.round_price_to_tick(symbol, stop_loss_price)
658
+ adjusted_price = self.format_price(symbol, stop_loss_price)
629
659
 
630
660
  # 默认市价止损,委托价格为-1时,执行市价止损。
631
661
  sl_params = {
632
- 'slTriggerPx':str(adjusted_price) ,
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
- if side == 'long':
700
- sl_price = position['entryPrice'] * (1 - sl_pct/100)
701
-
702
- elif side == 'short':
703
- sl_price = position['entryPrice'] * (1 + sl_pct/100)
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.global_symbol_stop_loss_price.get(symbol, 0.0)}")
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
- # tp_order_price = float(self.round_price_to_tick(symbol, tp_price))
733
-
772
+
773
+ precision = self.get_precision_length(symbol)
734
774
  # 验证止损价格是否合理20250312
735
- mark_price = float(position['markPrice'])
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('klines_period', '1m'))
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
- if side == 'long':
759
- sl_order_price = min(last_isolated_point['low'], sl_order_price)
760
- # 空头仓位,取孤立点的高点和原止损价的最大值作为新的止损价
761
- else:
762
- sl_order_price = max(last_isolated_point['high'], sl_order_price)
763
-
764
- self.logger.debug(f"{symbol} : {side} 止损价={sl_order_price} 孤立点=\n{last_isolated_point} ")
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
- entry_price = position['entryPrice']
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
- # base_price = entry_price * (1-take_profile_pct)
811
- take_profile_price = Decimal(entry_price * (1-take_profile_pct/100)) + offset * tick_size
812
-
813
- return Decimal(self.round_price_to_tick(symbol,take_profile_price))
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) -> float:
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.exchange.safe_float(self.global_symbol_stop_loss_price,symbol,None)
897
+ latest_stop_loss_price = self.toDecimal(self.global_symbol_stop_loss_price.get(symbol,None))
898
+ # 多头仓位止损价计算
824
899
  if side == 'long':
825
- last_max_market_price = self.max_market_price.get(symbol,0.0)
826
- self.max_market_price[symbol] = max(market_price,last_max_market_price)
827
- base_price = abs(self.max_market_price[symbol] - entry_price) * (1-stop_loss_pct)
828
- stop_loss_price = Decimal(entry_price + base_price) - offset * tick_size
829
- if latest_stop_loss_price :
830
- stop_loss_price = max(stop_loss_price,latest_stop_loss_price)
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
- last_min_market_price = self.min_market_price.get(symbol,float('inf'))
834
- self.min_market_price[symbol] = min(market_price,last_min_market_price)
835
- base_price = abs(self.min_market_price[symbol] - entry_price) * (1-stop_loss_pct)
836
- stop_loss_price = Decimal(entry_price - base_price) + offset * tick_size
837
- if latest_stop_loss_price :
838
- stop_loss_price = min(stop_loss_price,latest_stop_loss_price)
839
- return float(self.round_price_to_tick(symbol,stop_loss_price))
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
- msg = f"{symbol}: 盈利= {total_profit:.2f}% 开仓= {position['entryPrice']:.10f} 市价= {position['markPrice']:.10f}"
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.exchange.safe_float(self.global_symbol_stop_loss_price, symbol, 0.0)
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}%,市场价格:{position['markPrice']},"
1022
- f"设置回撤止损位: {sl_price:.9f}")
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
- # 检查仓位是否有变化 TODO 永远不会执行。
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
- msg = f"{symbol} : ## 重新开仓。 入场方向={position['side']} 入场价格={cur_entry_price} ##"
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