diff --git a/freqtrade/persistence/__init__.py b/freqtrade/persistence/__init__.py index 9e1a7e922..c69a54b7f 100644 --- a/freqtrade/persistence/__init__.py +++ b/freqtrade/persistence/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa: F401 +from freqtrade.persistence.key_value_store import KeyValueStore from freqtrade.persistence.models import init_db from freqtrade.persistence.pairlock_middleware import PairLocks from freqtrade.persistence.trade_model import LocalTrade, Order, Trade diff --git a/freqtrade/persistence/key_value_store.py b/freqtrade/persistence/key_value_store.py new file mode 100644 index 000000000..5ad98d69d --- /dev/null +++ b/freqtrade/persistence/key_value_store.py @@ -0,0 +1,97 @@ +from datetime import datetime, timezone +from enum import Enum +from typing import ClassVar, Optional, Union + +from sqlalchemy import String +from sqlalchemy.orm import Mapped, mapped_column + +from freqtrade.persistence.base import ModelBase, SessionType + + +ValueTypes = Union[str, datetime, float, int] + + +class ValueTypesEnum(str, Enum): + STRING = 'str' + DATETIME = 'datetime' + FLOAT = 'float' + INT = 'int' + + +class _KeyValueStoreModel(ModelBase): + """ + Pair Locks database model. + """ + __tablename__ = 'KeyValueStore' + session: ClassVar[SessionType] + + id: Mapped[int] = mapped_column(primary_key=True) + + key: Mapped[str] = mapped_column(String(25), nullable=False, index=True) + + value_type: Mapped[ValueTypesEnum] = mapped_column(String(25), nullable=False) + + string_value: Mapped[Optional[str]] + datetime_value: Mapped[Optional[datetime]] + float_value: Mapped[Optional[float]] + int_value: Mapped[Optional[int]] + + +class KeyValueStore(): + + @staticmethod + def get_value(key: str) -> Optional[ValueTypes]: + """ + Get the value for the given key. + """ + kv = _KeyValueStoreModel.session.query(_KeyValueStoreModel).filter( + _KeyValueStoreModel.key == key).first() + if kv is None: + return None + if kv.value_type == ValueTypesEnum.STRING: + return kv.string_value + if kv.value_type == ValueTypesEnum.DATETIME and kv.datetime_value is not None: + return kv.datetime_value.replace(tzinfo=timezone.utc) + if kv.value_type == ValueTypesEnum.FLOAT: + return kv.float_value + if kv.value_type == ValueTypesEnum.INT: + return kv.int_value + # This should never happen unless someone messed with the database manually + raise ValueError(f'Unknown value type {kv.value_type}') # pragma: no cover + + @staticmethod + def store_value(key: str, value: ValueTypes) -> None: + """ + Store the given value for the given key. + """ + kv = _KeyValueStoreModel.session.query(_KeyValueStoreModel).filter( + _KeyValueStoreModel.key == key).first() + if kv is None: + kv = _KeyValueStoreModel(key=key) + if isinstance(value, str): + kv.value_type = ValueTypesEnum.STRING + kv.string_value = value + elif isinstance(value, datetime): + kv.value_type = ValueTypesEnum.DATETIME + kv.datetime_value = value + elif isinstance(value, float): + kv.value_type = ValueTypesEnum.FLOAT + kv.float_value = value + elif isinstance(value, int): + kv.value_type = ValueTypesEnum.INT + kv.int_value = value + else: + raise ValueError(f'Unknown value type {kv.value_type}') + _KeyValueStoreModel.session.add(kv) + _KeyValueStoreModel.session.commit() + + @staticmethod + def delete_value(key: str) -> None: + """ + Delete the value for the given key. + """ + kv = _KeyValueStoreModel.session.query(_KeyValueStoreModel).filter( + _KeyValueStoreModel.key == key).first() + if kv is not None: + _KeyValueStoreModel.session.delete(kv) + _KeyValueStoreModel.session.commit() diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 2315c0acc..e561e727b 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -13,6 +13,7 @@ from sqlalchemy.pool import StaticPool from freqtrade.exceptions import OperationalException from freqtrade.persistence.base import ModelBase +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 @@ -76,6 +77,7 @@ def init_db(db_url: str) -> None: bind=engine, autoflush=False), scopefunc=get_request_or_thread_id) Order.session = Trade.session PairLock.session = Trade.session + _KeyValueStoreModel.session = Trade.session previous_tables = inspect(engine).get_table_names() ModelBase.metadata.create_all(engine)