From ed560f995d751efe12a85e452f4e83a3ebf3d56d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Apr 2025 19:40:06 +0200 Subject: [PATCH] feat: add WalletBalance model --- freqtrade/persistence/models.py | 2 ++ freqtrade/persistence/wallet_history.py | 28 +++++++++++++++++++++ freqtrade/wallets.py | 33 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 freqtrade/persistence/wallet_history.py diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 4d4808eeb..a9c8b8320 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -20,6 +20,7 @@ from freqtrade.persistence.key_value_store import _KeyValueStoreModel from freqtrade.persistence.migrations import check_migrate from freqtrade.persistence.pairlock import PairLock from freqtrade.persistence.trade_model import Order, Trade +from freqtrade.persistence.wallet_history import WalletBalance logger = logging.getLogger(__name__) @@ -91,6 +92,7 @@ def init_db(db_url: str) -> None: _CustomData.session = scoped_session( sessionmaker(bind=engine, autoflush=True), scopefunc=get_request_or_thread_id ) + WalletBalance.session = Trade.session previous_tables = inspect(engine).get_table_names() ModelBase.metadata.create_all(engine) diff --git a/freqtrade/persistence/wallet_history.py b/freqtrade/persistence/wallet_history.py new file mode 100644 index 000000000..aa84a65b3 --- /dev/null +++ b/freqtrade/persistence/wallet_history.py @@ -0,0 +1,28 @@ +from datetime import datetime +from typing import ClassVar + +from sqlalchemy import DateTime, Float, Index, Integer, String, UniqueConstraint +from sqlalchemy.orm import Mapped, mapped_column + +from freqtrade.persistence.base import ModelBase, SessionType +from freqtrade.wallets import Wallets + + +class WalletBalance(ModelBase): + """ + Daily wallet state tracking with minimal fields + """ + + __tablename__ = "wallet_balance" + session: ClassVar[SessionType] + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + timestamp: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True) + currency: Mapped[str] = mapped_column(String(25), nullable=False) + price: Mapped[float] = mapped_column(Float, nullable=True) + balance: Mapped[float] = mapped_column(Float, nullable=False) + + __table_args__ = ( + # Ensure one record per currency per day + UniqueConstraint("timestamp", "currency", name="unique_wallet_daily"), + ) diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 56c32adb6..64fba9656 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -11,6 +11,7 @@ from freqtrade.exceptions import DependencyException from freqtrade.exchange import Exchange from freqtrade.misc import safe_value_fallback from freqtrade.persistence import LocalTrade, Trade +from freqtrade.persistence.wallet_history import WalletBalance from freqtrade.util.datetime_helpers import dt_now @@ -445,3 +446,35 @@ class Wallets: logger.debug(msg) else: logger.info(msg) + + def record_wallet_state(self) -> None: + """ + Record daily wallet totals to database + """ + if self.is_backtest: + # only record in live mode. + return + timestamp = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + + # Record total balances for all currencies + for wallet in self.get_all_balances().values(): + # TODO: exclude minimal balances + price = self._exchange.get_conversion_rate(wallet.currency, self._stake_currency) + wallet_record = WalletBalance( + timestamp=timestamp, + currency=wallet.currency, + price=price, + balance=wallet.total, + ) + WalletBalance.session.add(wallet_record) + + for position in self.get_all_positions().values(): + price = self._exchange.get_conversion_rate(position.symbol, self._stake_currency) + position_record = WalletBalance( + timestamp=timestamp, + currency=position.pair, + price=position.price, + balance=position.amount, + ) + WalletBalance.session.add(position_record) + WalletBalance.session.commit()