diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index ac5f804c9..c1b6be0e3 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa: F401 from freqtrade.enums.backteststate import BacktestState +from freqtrade.enums.maintenancemarginformula import MaintenanceMarginFormula from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType diff --git a/freqtrade/enums/maintenancemarginformula.py b/freqtrade/enums/maintenancemarginformula.py new file mode 100644 index 000000000..60e9d6bd5 --- /dev/null +++ b/freqtrade/enums/maintenancemarginformula.py @@ -0,0 +1,26 @@ +from enum import Enum +from freqtrade.exceptions import OperationalException + + +class MaintenanceMarginFormula(Enum): + """Equations to calculate maintenance margin""" + + BINANCE = "BINANCE" + FTX = "FTX" + KRAKEN = "KRAKEN" + + # TODO: Add arguments + def __call__(self): + if self.name == "BINANCE": + raise OperationalException("Cross margin not available on this exchange with freqtrade") + # TODO: return This formula + # https://www.binance.com/en/support/faq/f6b010588e55413aa58b7d63ee0125ed + elif self.name == "FTX": + # TODO: Implement + raise OperationalException("Cross margin not available on this exchange with freqtrade") + elif self.name == "KRAKEN": + # TODO: Implement + raise OperationalException("Cross margin not available on this exchange with freqtrade") + # https://support.kraken.com/hc/en-us/articles/203325763-Margin-Call-Level-and-Margin-Liquidation-Level + else: + raise OperationalException("Cross margin not available on this exchange with freqtrade") diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 0c470cb24..fe984e408 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -3,7 +3,7 @@ import logging from typing import Dict import ccxt - +from freqtrade.enums import MaintenanceMarginFormula from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, OperationalException, TemporaryError) from freqtrade.exchange import Exchange @@ -24,6 +24,8 @@ class Binance(Exchange): "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], } + maintenance_margin_formula = MaintenanceMarginFormula.BINANCE + def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: """ Verify stop_loss against stoploss-order value (limit or price) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 706fd2a67..4040b4074 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -21,6 +21,7 @@ from pandas import DataFrame from freqtrade.constants import DEFAULT_AMOUNT_RESERVE_PERCENT, ListPairsWithTimeframes from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list +from freqtrade.enums import MaintenanceMarginFormula from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError, InvalidOrderException, OperationalException, PricingError, RetryableOrderError, TemporaryError) @@ -68,6 +69,7 @@ class Exchange: "l2_limit_range_required": True, # Allow Empty L2 limit (kucoin) } _ft_has: Dict = {} + maintenance_margin_formula: MaintenanceMarginFormula def __init__(self, config: Dict[str, Any], validate: bool = True) -> None: """ diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 6cd549d60..dfac0c5d1 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -4,6 +4,7 @@ from typing import Any, Dict import ccxt +from freqtrade.enums import MaintenanceMarginFormula from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, OperationalException, TemporaryError) from freqtrade.exchange import Exchange @@ -21,6 +22,8 @@ class Ftx(Exchange): "ohlcv_candle_limit": 1500, } + maintenance_margin_formula = MaintenanceMarginFormula.FTX + def market_is_tradable(self, market: Dict[str, Any]) -> bool: """ Check if the market symbol is tradable by Freqtrade. diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 1b069aa6c..a9063a0a7 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -4,6 +4,7 @@ from typing import Any, Dict import ccxt +from freqtrade.enums import MaintenanceMarginFormula from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, OperationalException, TemporaryError) from freqtrade.exchange import Exchange @@ -23,6 +24,8 @@ class Kraken(Exchange): "trades_pagination_arg": "since", } + maintenance_margin_formula = MaintenanceMarginFormula.KRAKEN + def market_is_tradable(self, market: Dict[str, Any]) -> bool: """ Check if the market symbol is tradable by Freqtrade. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d430dbc48..903f3df17 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -20,6 +20,7 @@ from freqtrade.enums import RPCMessageType, SellType, State from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, InvalidOrderException, PricingError) from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds +from freqtrade.maintenance_margin import MaintenanceMargin from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.mixins import LoggingMixin from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db @@ -102,6 +103,12 @@ class FreqtradeBot(LoggingMixin): self._sell_lock = Lock() LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) + # Start calculating maintenance margin if on cross margin + # TODO: Add margin_mode to freqtrade.configuration? + if self.config.get('margin_mode') == "cross": + self.maintenance_margin = MaintenanceMargin(self.exchange.maintenance_margin_formula) + self.maintenance_margin.run + def notify_status(self, msg: str) -> None: """ Public method for users of this class (worker, etc.) to send notifications diff --git a/freqtrade/maintenance_margin.py b/freqtrade/maintenance_margin.py new file mode 100644 index 000000000..68402e0b2 --- /dev/null +++ b/freqtrade/maintenance_margin.py @@ -0,0 +1,38 @@ +from freqtrade.enums import MaintenanceMarginFormula +from freqtrade.persistence import Trade + + +class MaintenanceMargin: + + trades: list[Trade] + formula: MaintenanceMarginFormula + + @property + def margin_level(self): + return self.formula() # TODO: Add args to formula + + @property + def liq_level(self): # This may be a constant value and may not need a function + return + + def __init__(self, formula: MaintenanceMarginFormula): + return + + def add_new_trade(self, trade): + return + + def remove_trade(self, trade): + return + + # ? def update_trade_pric(self): + + def sell_all(self): + return + + def run(self): + # TODO-mg: implement a thread that constantly updates with every price change, + # TODO-mg: must update at least every second + # while true: + # if self.margin_level <= self.liq_level: + # self.sell_all() + return