diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 87b172846..1dd7a2216 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -281,6 +281,7 @@ def fix_old_dry_orders(engine): ).values(ft_is_open=False) connection.execute(stmt) + # OLD stmt = update(Order).where( Order.ft_is_open.is_(True), tuple_(Order.ft_trade_id, Order.order_id).not_in( @@ -294,6 +295,28 @@ def fix_old_dry_orders(engine): ).values(ft_is_open=False) connection.execute(stmt) + # Update current Order where + # -current Order is open + # -current Order trade_id not equal to current Trade.id + # -current Order not stoploss + # -order_id not equal to current Trade.stoploss_order_id + # -current Order is dry + + # NEW WIP + stmt = update(Order).where( + Order.ft_is_open.is_(True), + Order.ft_trade_id.not_in( + select(Trade.id).where(Trade.open_orders_count.is_not(0)) + ), + Order.order_id.not_in( + select(Trade.open_orders).filter(Order.order_id).first() + ), + Order.ft_order_side != 'stoploss', + Order.order_id.like('dry%') + + ).values(ft_is_open=False) + connection.execute(stmt) + def check_migrate(engine, decl_base, previous_tables) -> None: """ @@ -340,7 +363,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None: "start with a fresh database.") set_sqlite_to_wal(engine) - fix_old_dry_orders(engine) + # fix_old_dry_orders(engine) # TODO Fix that if migrating: logger.info("Database migration finished.") diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 5d8aada6b..ead3f7a5d 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -9,6 +9,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Sequence, cast from sqlalchemy import (Enum, Float, ForeignKey, Integer, ScalarResult, Select, String, UniqueConstraint, desc, func, select) +from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship, validates from freqtrade.constants import (CUSTOM_TAG_MAX_LENGTH, DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, @@ -327,7 +328,7 @@ class LocalTrade(): amount_requested: Optional[float] = None open_date: datetime close_date: Optional[datetime] = None - open_order_id: Optional[str] = None + open_orders: List[Order] = [] # absolute value of the stop loss stop_loss: float = 0.0 # percentage value of the stop loss @@ -468,6 +469,14 @@ class LocalTrade(): except IndexError: return '' + @hybrid_property + def open_orders_count(self) -> int: + return len(self.open_orders) + + @hybrid_property + def open_orders_ids(self) -> list: + return [open_order.order_id for open_order in self.open_orders] + def __init__(self, **kwargs): for key in kwargs: setattr(self, key, kwargs[key]) @@ -680,12 +689,21 @@ class LocalTrade(): if self.is_open: payment = "SELL" if self.is_short else "BUY" logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') + + # TODO WIP but to rm if useless + # condition to avoid reset value when updating fees (new) + # if order.order_id in self.open_orders_ids: + # self.open_order_id = None + # else: + # logger.warning( + # f'Got different open_order_id {self.open_order_id} != {order.order_id}') + # TODO validate if this is still relevant # condition to avoid reset value when updating fees - if self.open_order_id == order.order_id: - self.open_order_id = None - else: - logger.warning( - f'Got different open_order_id {self.open_order_id} != {order.order_id}') + # if self.open_order_id == order.order_id: + # self.open_order_id = None + # else: + # logger.warning( + # f'Got different open_order_id {self.open_order_id} != {order.order_id}') self.recalc_trade_from_orders() elif order.ft_order_side == self.exit_side: if self.is_open: @@ -693,11 +711,11 @@ class LocalTrade(): # * On margin shorts, you buy a little bit more than the amount (amount + interest) logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') # condition to avoid reset value when updating fees - if self.open_order_id == order.order_id: - self.open_order_id = None - else: - logger.warning( - f'Got different open_order_id {self.open_order_id} != {order.order_id}') + # if self.open_order_id == order.order_id: + # self.open_order_id = None + # else: + # logger.warning( + # f'Got different open_order_id {self.open_order_id} != {order.order_id}') elif order.ft_order_side == 'stoploss' and order.status not in ('open', ): self.stoploss_order_id = None @@ -1244,7 +1262,6 @@ class Trade(ModelBase, LocalTrade): open_date: Mapped[datetime] = mapped_column( nullable=False, default=datetime.utcnow) # type: ignore close_date: Mapped[Optional[datetime]] = mapped_column() # type: ignore - open_order_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) # type: ignore # absolute value of the stop loss stop_loss: Mapped[float] = mapped_column(Float(), nullable=True, default=0.0) # type: ignore # percentage value of the stop loss @@ -1296,6 +1313,10 @@ class Trade(ModelBase, LocalTrade): funding_fees: Mapped[Optional[float]] = mapped_column( Float(), nullable=True, default=None) # type: ignore + @hybrid_property + def open_orders(self): + return [order for order in self.orders if order.ft_is_open] + def __init__(self, **kwargs): super().__init__(**kwargs) self.realized_profit = 0 diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index 4aa3b1e96..fcb8771d0 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -437,15 +437,15 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_ leverage=lev, trading_mode=trading_mode ) - assert trade.open_order_id is None + assert trade.open_orders_count == 0 assert trade.close_profit is None assert trade.close_date is None - trade.open_order_id = enter_order['id'] + trade.open_orders.append(enter_order) oobj = Order.parse_from_ccxt_object(enter_order, 'ADA/USDT', entry_side) trade.orders.append(oobj) trade.update_trade(oobj) - assert trade.open_order_id is None + assert trade.open_orders_count == 0 assert trade.open_rate == open_rate assert trade.close_profit is None assert trade.close_date is None @@ -456,13 +456,13 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_ caplog) caplog.clear() - trade.open_order_id = enter_order['id'] + trade.open_orders.append(enter_order) time_machine.move_to("2022-03-31 21:45:05 +00:00") oobj = Order.parse_from_ccxt_object(exit_order, 'ADA/USDT', exit_side) trade.orders.append(oobj) trade.update_trade(oobj) - assert trade.open_order_id is None + assert trade.open_orders_count == 0 assert trade.close_rate == close_rate assert pytest.approx(trade.close_profit) == profit assert trade.close_date is not None