@@ -730,7 +730,7 @@ def process_open_trade_positions(self):
730
730
for trade in Trade .get_open_trades ():
731
731
# If there is any open orders, wait for them to finish.
732
732
# TODO Remove to allow mul open orders
733
- if not trade .has_open_orders :
733
+ if trade . has_open_position or trade .has_open_orders :
734
734
# Do a wallets update (will be ratelimited to once per hour)
735
735
self .wallets .update (False )
736
736
try :
@@ -808,7 +808,10 @@ def check_and_call_adjust_trade_position(self, trade: Trade):
808
808
)
809
809
810
810
if amount == 0.0 :
811
- logger .info ("Amount to exit is 0.0 due to exchange limits - not exiting." )
811
+ logger .info (
812
+ f"Wanted to exit of { stake_amount } amount, "
813
+ "but exit amount is now 0.0 due to exchange limits - not exiting."
814
+ )
812
815
return
813
816
814
817
remaining = (trade .amount - amount ) * current_exit_rate
@@ -923,6 +926,10 @@ def execute_entry(
923
926
):
924
927
logger .info (f"User denied entry for { pair } ." )
925
928
return False
929
+
930
+ if trade and self .handle_similar_open_order (trade , enter_limit_requested , amount , side ):
931
+ return False
932
+
926
933
order = self .exchange .create_order (
927
934
pair = pair ,
928
935
ordertype = order_type ,
@@ -1303,8 +1310,8 @@ def exit_positions(self, trades: list[Trade]) -> int:
1303
1310
logger .warning (
1304
1311
f"Unable to handle stoploss on exchange for { trade .pair } : { exception } "
1305
1312
)
1306
- # Check if we can exit our current pair
1307
- if not trade .has_open_orders and trade .is_open and self .handle_trade (trade ):
1313
+ # Check if we can exit our current position for this trade
1314
+ if trade .has_open_position and trade .is_open and self .handle_trade (trade ):
1308
1315
trades_closed += 1
1309
1316
1310
1317
except DependencyException as exception :
@@ -1448,9 +1455,7 @@ def handle_stoploss_on_exchange(self, trade: Trade) -> bool:
1448
1455
self .handle_protections (trade .pair , trade .trade_direction )
1449
1456
return True
1450
1457
1451
- if trade .has_open_orders or not trade .is_open :
1452
- # Trade has an open order, Stoploss-handling can't happen in this case
1453
- # as the Amount on the exchange is tied up in another trade.
1458
+ if not trade .has_open_position or not trade .is_open :
1454
1459
# The trade can be closed already (sell-order fill confirmation came in this iteration)
1455
1460
return False
1456
1461
@@ -1718,30 +1723,75 @@ def replace_order(self, order: CcxtOrder, order_obj: Order | None, trade: Trade)
1718
1723
logger .warning (f"Unable to replace order for { trade .pair } : { exception } " )
1719
1724
self .replace_order_failed (trade , f"Could not replace order for { trade } ." )
1720
1725
1726
+ def cancel_open_orders_of_trade (
1727
+ self , trade : Trade , sides : list [str ], reason : str , replacing : bool = False
1728
+ ) -> None :
1729
+ """
1730
+ Cancel trade orders of specified sides that are currently open
1731
+ :param trade: Trade object of the trade we're analyzing
1732
+ :param reason: The reason for that cancellation
1733
+ :param sides: The sides where cancellation should take place
1734
+ :return: None
1735
+ """
1736
+
1737
+ for open_order in trade .open_orders :
1738
+ try :
1739
+ order = self .exchange .fetch_order (open_order .order_id , trade .pair )
1740
+ except ExchangeError :
1741
+ logger .info ("Can't query order for %s due to %s" , trade , traceback .format_exc ())
1742
+ continue
1743
+
1744
+ if order ["side" ] in sides :
1745
+ if order ["side" ] == trade .entry_side :
1746
+ self .handle_cancel_enter (trade , order , open_order , reason , replacing )
1747
+
1748
+ elif order ["side" ] == trade .exit_side :
1749
+ self .handle_cancel_exit (trade , order , open_order , reason )
1750
+
1721
1751
def cancel_all_open_orders (self ) -> None :
1722
1752
"""
1723
1753
Cancel all orders that are currently open
1724
1754
:return: None
1725
1755
"""
1726
1756
1727
1757
for trade in Trade .get_open_trades ():
1728
- for open_order in trade .open_orders :
1729
- try :
1730
- order = self .exchange .fetch_order (open_order .order_id , trade .pair )
1731
- except ExchangeError :
1732
- logger .info ("Can't query order for %s due to %s" , trade , traceback .format_exc ())
1733
- continue
1758
+ self .cancel_open_orders_of_trade (
1759
+ trade , [trade .entry_side , trade .exit_side ], constants .CANCEL_REASON ["ALL_CANCELLED" ]
1760
+ )
1734
1761
1735
- if order ["side" ] == trade .entry_side :
1736
- self .handle_cancel_enter (
1737
- trade , order , open_order , constants .CANCEL_REASON ["ALL_CANCELLED" ]
1738
- )
1762
+ Trade .commit ()
1739
1763
1740
- elif order ["side" ] == trade .exit_side :
1741
- self .handle_cancel_exit (
1742
- trade , order , open_order , constants .CANCEL_REASON ["ALL_CANCELLED" ]
1764
+ def handle_similar_open_order (
1765
+ self , trade : Trade , price : float , amount : float , side : str
1766
+ ) -> bool :
1767
+ """
1768
+ Keep existing open order if same amount and side otherwise cancel
1769
+ :param trade: Trade object of the trade we're analyzing
1770
+ :param price: Limit price of the potential new order
1771
+ :param amount: Quantity of assets of the potential new order
1772
+ :param side: Side of the potential new order
1773
+ :return: True if an existing similar order was found
1774
+ """
1775
+ if trade .has_open_orders :
1776
+ oo = trade .select_order (side , True )
1777
+ if oo is not None :
1778
+ if (price == oo .price ) and (side == oo .side ) and (amount == oo .amount ):
1779
+ logger .info (
1780
+ f"A similar open order was found for { trade .pair } . "
1781
+ f"Keeping existing { trade .exit_side } order. { price = } , { amount = } "
1743
1782
)
1744
- Trade .commit ()
1783
+ return True
1784
+ # cancel open orders of this trade if order is different
1785
+ self .cancel_open_orders_of_trade (
1786
+ trade ,
1787
+ [trade .entry_side , trade .exit_side ],
1788
+ constants .CANCEL_REASON ["REPLACE" ],
1789
+ True ,
1790
+ )
1791
+ Trade .commit ()
1792
+ return False
1793
+
1794
+ return False
1745
1795
1746
1796
def handle_cancel_enter (
1747
1797
self ,
@@ -1924,7 +1974,11 @@ def _safe_exit_amount(self, trade: Trade, pair: str, amount: float) -> float:
1924
1974
return amount
1925
1975
1926
1976
trade_base_currency = self .exchange .get_pair_base_currency (pair )
1927
- wallet_amount = self .wallets .get_free (trade_base_currency )
1977
+ # Free + Used - open orders will eventually still be canceled.
1978
+ wallet_amount = self .wallets .get_free (trade_base_currency ) + self .wallets .get_used (
1979
+ trade_base_currency
1980
+ )
1981
+
1928
1982
logger .debug (f"{ pair } - Wallet: { wallet_amount } - Trade-amount: { amount } " )
1929
1983
if wallet_amount >= amount :
1930
1984
return amount
@@ -2017,6 +2071,10 @@ def execute_trade_exit(
2017
2071
logger .info (f"User denied exit for { trade .pair } ." )
2018
2072
return False
2019
2073
2074
+ if trade .has_open_orders :
2075
+ if self .handle_similar_open_order (trade , limit , amount , trade .exit_side ):
2076
+ return False
2077
+
2020
2078
try :
2021
2079
# Execute sell and update trade record
2022
2080
order = self .exchange .create_order (
0 commit comments