From fe2efe0ccd9207917430703be6670cfbfe3df6f7 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 6 Aug 2021 01:15:18 -0600 Subject: [PATCH] Added liquidation_price and interest functions --- freqtrade/enums/__init__.py | 1 - freqtrade/enums/interestmode.py | 28 ---- freqtrade/leverage/__init__.py | 2 + freqtrade/leverage/interest.py | 43 ++++++ freqtrade/leverage/liquidation_price.py | 106 +++++++++++++++ freqtrade/persistence/migrations.py | 6 +- freqtrade/persistence/models.py | 23 +++- tests/conftest_trades.py | 7 +- tests/leverage/test_leverage.py | 108 +++++++++++++++ tests/test_persistence.py | 173 ++++++++++++------------ 10 files changed, 364 insertions(+), 133 deletions(-) delete mode 100644 freqtrade/enums/interestmode.py create mode 100644 freqtrade/leverage/interest.py create mode 100644 freqtrade/leverage/liquidation_price.py create mode 100644 tests/leverage/test_leverage.py diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index ef73dd82a..692a7fcb6 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -1,7 +1,6 @@ # flake8: noqa: F401 from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.collateral import Collateral -from freqtrade.enums.interestmode import InterestMode 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/interestmode.py b/freqtrade/enums/interestmode.py deleted file mode 100644 index 89c71a8b4..000000000 --- a/freqtrade/enums/interestmode.py +++ /dev/null @@ -1,28 +0,0 @@ -from decimal import Decimal -from enum import Enum -from math import ceil - -from freqtrade.exceptions import OperationalException - - -one = Decimal(1.0) -four = Decimal(4.0) -twenty_four = Decimal(24.0) - - -class InterestMode(Enum): - """Equations to calculate interest""" - - HOURSPERDAY = "HOURSPERDAY" - HOURSPER4 = "HOURSPER4" # Hours per 4 hour segment - NONE = "NONE" - - def __call__(self, borrowed: Decimal, rate: Decimal, hours: Decimal): - - if self.name == "HOURSPERDAY": - return borrowed * rate * ceil(hours)/twenty_four - elif self.name == "HOURSPER4": - # Rounded based on https://kraken-fees-calculator.github.io/ - return borrowed * rate * (1+ceil(hours/four)) - else: - raise OperationalException("Leverage not available on this exchange with freqtrade") diff --git a/freqtrade/leverage/__init__.py b/freqtrade/leverage/__init__.py index 9186b160e..0bb2dd0be 100644 --- a/freqtrade/leverage/__init__.py +++ b/freqtrade/leverage/__init__.py @@ -1 +1,3 @@ # flake8: noqa: F401 +from freqtrade.leverage.interest import interest +from freqtrade.leverage.liquidation_price import liquidation_price diff --git a/freqtrade/leverage/interest.py b/freqtrade/leverage/interest.py new file mode 100644 index 000000000..b8bc47887 --- /dev/null +++ b/freqtrade/leverage/interest.py @@ -0,0 +1,43 @@ +from decimal import Decimal +from math import ceil + +from freqtrade.exceptions import OperationalException + + +one = Decimal(1.0) +four = Decimal(4.0) +twenty_four = Decimal(24.0) + + +def interest( + exchange_name: str, + borrowed: Decimal, + rate: Decimal, + hours: Decimal +) -> Decimal: + """Equation to calculate interest on margin trades + + + :param exchange_name: The exchanged being trading on + :param borrowed: The amount of currency being borrowed + :param rate: The rate of interest + :param hours: The time in hours that the currency has been borrowed for + + Raises: + OperationalException: Raised if freqtrade does + not support margin trading for this exchange + + Returns: The amount of interest owed (currency matches borrowed) + + """ + exchange_name = exchange_name.lower() + if exchange_name == "binance": + return borrowed * rate * ceil(hours)/twenty_four + elif exchange_name == "kraken": + # Rounded based on https://kraken-fees-calculator.github.io/ + return borrowed * rate * (one+ceil(hours/four)) + elif exchange_name == "ftx": + # TODO-lev: Add FTX interest formula + raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade") + else: + raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade") diff --git a/freqtrade/leverage/liquidation_price.py b/freqtrade/leverage/liquidation_price.py new file mode 100644 index 000000000..2aeba6b78 --- /dev/null +++ b/freqtrade/leverage/liquidation_price.py @@ -0,0 +1,106 @@ +from freqtrade.enums import Collateral, TradingMode +from freqtrade.exceptions import OperationalException + + +def liquidation_price( + exchange_name: str, + trading_mode: TradingMode, + ** k +): + + leverage_exchanges = [ + 'binance', + 'kraken', + 'ftx' + ] + if trading_mode == TradingMode.SPOT or exchange_name.lower() not in leverage_exchanges: + return None + + collateral: Collateral = k['collateral'] + + if exchange_name.lower() == "binance": + # TODO-lev: Get more variables from **k and pass them to binance + return binance(trading_mode, collateral) + elif exchange_name.lower() == "kraken": + # TODO-lev: Get more variables from **k and pass them to kraken + return kraken(trading_mode, collateral) + elif exchange_name.lower() == "ftx": + return ftx(trading_mode, collateral) + return + + +def exception( + exchange_name: str, + trading_mode: TradingMode, + collateral: Collateral +): + """ + Raises an exception if exchange used doesn't support desired leverage mode + :param name: Name of the exchange + :param trading_mode: spot, margin, futures + :param collateral: cross, isolated + """ + raise OperationalException( + f"{exchange_name} does not support {collateral.value} {trading_mode.value} trading") + + +def binance(trading_mode: TradingMode, collateral: Collateral): + """ + Calculates the liquidation price on Binance + :param name: Name of the exchange + :param trading_mode: spot, margin, futures + :param collateral: cross, isolated + """ + # TODO-lev: Additional arguments, fill in formulas + + if trading_mode == TradingMode.MARGIN and collateral == Collateral.CROSS: + # TODO-lev: perform a calculation based on this formula + # https://www.binance.com/en/support/faq/f6b010588e55413aa58b7d63ee0125ed + exception("binance", trading_mode, collateral) + elif trading_mode == TradingMode.FUTURES and collateral == Collateral.CROSS: + # TODO-lev: perform a calculation based on this formula + # https://www.binance.com/en/support/faq/b3c689c1f50a44cabb3a84e663b81d93 + exception("binance", trading_mode, collateral) + elif trading_mode == TradingMode.FUTURES and collateral == Collateral.ISOLATED: + # TODO-lev: perform a calculation based on this formula + # https://www.binance.com/en/support/faq/b3c689c1f50a44cabb3a84e663b81d93 + exception("binance", trading_mode, collateral) + + # If nothing was returned + exception("binance", trading_mode, collateral) + + +def kraken(trading_mode: TradingMode, collateral: Collateral): + """ + Calculates the liquidation price on Kraken + :param name: Name of the exchange + :param trading_mode: spot, margin, futures + :param collateral: cross, isolated + """ + # TODO-lev: Additional arguments, fill in formulas + + if collateral == Collateral.CROSS: + if trading_mode == TradingMode.MARGIN: + exception("kraken", trading_mode, collateral) + # TODO-lev: perform a calculation based on this formula + # https://support.kraken.com/hc/en-us/articles/203325763-Margin-Call-Level-and-Margin-Liquidation-Level + elif trading_mode == TradingMode.FUTURES: + exception("kraken", trading_mode, collateral) + + # If nothing was returned + exception("kraken", trading_mode, collateral) + + +def ftx(trading_mode: TradingMode, collateral: Collateral): + """ + Calculates the liquidation price on FTX + :param name: Name of the exchange + :param trading_mode: spot, margin, futures + :param collateral: cross, isolated + """ + if collateral == Collateral.CROSS: + # TODO-lev: Additional arguments, fill in formulas + exception("ftx", trading_mode, collateral) + + # If nothing was returned + exception("ftx", trading_mode, collateral) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 03f412724..cc33be87c 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -54,7 +54,6 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col isolated_liq = get_column_def(cols, 'isolated_liq', 'null') # sqlite does not support literals for booleans is_short = get_column_def(cols, 'is_short', '0') - interest_mode = get_column_def(cols, 'interest_mode', 'null') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') @@ -92,7 +91,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col stoploss_order_id, stoploss_last_update, max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag, timeframe, open_trade_value, close_profit_abs, - leverage, interest_rate, isolated_liq, is_short, interest_mode + leverage, interest_rate, isolated_liq, is_short ) select id, lower(exchange), pair, is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost, @@ -110,8 +109,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col {strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs, {leverage} leverage, {interest_rate} interest_rate, - {isolated_liq} isolated_liq, {is_short} is_short, - {interest_mode} interest_mode + {isolated_liq} isolated_liq, {is_short} is_short from {table_back_name} """)) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index d09c5ed68..3d0c9c913 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -14,8 +14,9 @@ from sqlalchemy.pool import StaticPool from sqlalchemy.sql.schema import UniqueConstraint from freqtrade.constants import DATETIME_PRINT_FORMAT -from freqtrade.enums import InterestMode, SellType +from freqtrade.enums import SellType, TradingMode from freqtrade.exceptions import DependencyException, OperationalException +from freqtrade.leverage import interest, liquidation_price from freqtrade.misc import safe_value_fallback from freqtrade.persistence.migrations import check_migrate @@ -236,7 +237,7 @@ class LocalTrade(): close_rate_requested: Optional[float] = None close_profit: Optional[float] = None close_profit_abs: Optional[float] = None - stake_amount: float = 0.0 # TODO: This should probably be computed + stake_amount: float = 0.0 amount: float = 0.0 amount_requested: Optional[float] = None open_date: datetime @@ -269,7 +270,7 @@ class LocalTrade(): isolated_liq: Optional[float] = None is_short: bool = False leverage: float = 1.0 - interest_mode: InterestMode = InterestMode.NONE + trading_mode: TradingMode = TradingMode.SPOT @property def has_no_leverage(self) -> bool: @@ -316,7 +317,7 @@ class LocalTrade(): for key in kwargs: setattr(self, key, kwargs[key]) if self.isolated_liq: - self.set_isolated_liq(self.isolated_liq) + self.set_isolated_liq(isolated_liq=self.isolated_liq) self.recalc_open_trade_value() def _set_stop_loss(self, stop_loss: float, percent: float): @@ -342,11 +343,19 @@ class LocalTrade(): self.stop_loss_pct = -1 * abs(percent) self.stoploss_last_update = datetime.utcnow() - def set_isolated_liq(self, isolated_liq: float): + def set_isolated_liq(self, **k): """ Method you should use to set self.liquidation price. Assures stop_loss is not passed the liquidation price """ + if k['isolated_liq']: + isolated_liq: float = k['isolated_liq'] + else: + isolated_liq: float = liquidation_price( + exchange=self.exchange_name, + **k + ) + if self.stop_loss is not None: if self.is_short: self.stop_loss = min(self.stop_loss, isolated_liq) @@ -650,7 +659,7 @@ class LocalTrade(): rate = Decimal(interest_rate or self.interest_rate) borrowed = Decimal(self.borrowed) - return self.interest_mode(borrowed=borrowed, rate=rate, hours=hours) + return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours) def calc_close_trade_value(self, rate: Optional[float] = None, fee: Optional[float] = None, @@ -894,7 +903,7 @@ class Trade(_DECL_BASE, LocalTrade): interest_rate = Column(Float, nullable=False, default=0.0) isolated_liq = Column(Float, nullable=True) is_short = Column(Boolean, nullable=False, default=False) - interest_mode = Column(Enum(InterestMode), nullable=True) + trading_mode = Column(Enum(TradingMode), default=TradingMode.SPOT) # End of margin trading properties def __init__(self, **kwargs): diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index cad6d195c..faba18371 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -1,6 +1,5 @@ from datetime import datetime, timedelta, timezone -from freqtrade.enums import InterestMode from freqtrade.persistence.models import Order, Trade @@ -383,8 +382,7 @@ def short_trade(fee): sell_reason='sell_signal', # TODO-mg: Update to exit/close reason open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), # close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), - is_short=True, - interest_mode=InterestMode.HOURSPERDAY + is_short=True ) o = Order.parse_from_ccxt_object(short_order(), 'ETC/BTC', 'sell') trade.orders.append(o) @@ -473,8 +471,7 @@ def leverage_trade(fee): sell_reason='sell_signal', # TODO-mg: Update to exit/close reason open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300), close_date=datetime.now(tz=timezone.utc), - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 + interest_rate=0.0005 ) o = Order.parse_from_ccxt_object(leverage_order(), 'DOGE/BTC', 'sell') trade.orders.append(o) diff --git a/tests/leverage/test_leverage.py b/tests/leverage/test_leverage.py new file mode 100644 index 000000000..c90e2962b --- /dev/null +++ b/tests/leverage/test_leverage.py @@ -0,0 +1,108 @@ +# from decimal import Decimal + +from freqtrade.enums import Collateral, TradingMode +from freqtrade.leverage import liquidation_price + + +# from freqtrade.leverage import interest, liquidation_price + +# from freqtrade.exceptions import OperationalException +binance = "binance" +kraken = "kraken" +ftx = "ftx" +other = "bittrex" + + +def test_interest(): + return + # Binance + # assert interest(binance, borrowed=60, rate=0.0005, + # hours = 1/6) == round(0.0008333333333333334, 8) + # TODO-lev: The below tests + # assert interest(binance, borrowed=60, rate=0.00025, hours=5.0) == 1.0 + + # # Kraken + # assert interest(kraken, borrowed=60, rate=0.0005, hours=1.0) == 1.0 + # assert interest(kraken, borrowed=60, rate=0.00025, hours=5.0) == 1.0 + + # # FTX + # assert interest(ftx, borrowed=60, rate=0.0005, hours=1.0) == 1.0 + # assert interest(ftx, borrowed=60, rate=0.00025, hours=5.0) == 1.0 + + +def test_liquidation_price(): + + spot = TradingMode.SPOT + margin = TradingMode.MARGIN + futures = TradingMode.FUTURES + + cross = Collateral.CROSS + isolated = Collateral.ISOLATED + + # NONE + assert liquidation_price(exchange_name=other, trading_mode=spot) is None + assert liquidation_price(exchange_name=other, trading_mode=margin, + collateral=cross) is None + assert liquidation_price(exchange_name=other, trading_mode=margin, + collateral=isolated) is None + assert liquidation_price( + exchange_name=other, trading_mode=futures, collateral=cross) is None + assert liquidation_price(exchange_name=other, trading_mode=futures, + collateral=isolated) is None + + # Binance + assert liquidation_price(exchange_name=binance, trading_mode=spot) is None + assert liquidation_price(exchange_name=binance, trading_mode=spot, + collateral=cross) is None + assert liquidation_price(exchange_name=binance, trading_mode=spot, + collateral=isolated) is None + # TODO-lev: Uncomment these assertions and make them real calculation tests + # TODO-lev: Replace 1.0 with real value + # assert liquidation_price( + # exchange_name=binance, + # trading_mode=margin, + # collateral=cross + # ) == 1.0 + # assert liquidation_price( + # exchange_name=binance, + # trading_mode=margin, + # collateral=isolated + # ) == 1.0 + # assert liquidation_price( + # exchange_name=binance, + # trading_mode=futures, + # collateral=cross + # ) == 1.0 + + # Binance supports isolated margin, but freqtrade likely won't for a while on Binance + # liquidation_price(exchange_name=binance, trading_mode=margin, collateral=isolated) + # assert exception thrown #TODO-lev: Check that exception is thrown + + # Kraken + assert liquidation_price(exchange_name=kraken, trading_mode=spot) is None + assert liquidation_price(exchange_name=kraken, trading_mode=spot, collateral=cross) is None + assert liquidation_price(exchange_name=kraken, trading_mode=spot, + collateral=isolated) is None + # TODO-lev: Uncomment these assertions and make them real calculation tests + # assert liquidation_price(kraken, trading_mode=margin, collateral=cross) == 1.0 + # assert liquidation_price(kraken, trading_mode=margin, collateral=isolated) == 1.0 + + # liquidation_price(kraken, trading_mode=futures, collateral=cross) + # assert exception thrown #TODO-lev: Check that exception is thrown + + # liquidation_price(kraken, trading_mode=futures, collateral=isolated) + # assert exception thrown #TODO-lev: Check that exception is thrown + + # FTX + assert liquidation_price(ftx, trading_mode=spot) is None + assert liquidation_price(ftx, trading_mode=spot, collateral=cross) is None + assert liquidation_price(ftx, trading_mode=spot, collateral=isolated) is None + # TODO-lev: Uncomment these assertions and make them real calculation tests + # assert liquidation_price(ftx, trading_mode=margin, collateral=cross) == 1.0 + # assert liquidation_price(ftx, trading_mode=margin, collateral=isolated) == 1.0 + + # liquidation_price(ftx, trading_mode=futures, collateral=cross) + # assert exception thrown #TODO-lev: Check that exception is thrown + + # liquidation_price(ftx, trading_mode=futures, collateral=isolated) + # assert exception thrown #TODO-lev: Check that exception is thrown diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 16469f6fc..d8a7bf400 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -11,7 +11,7 @@ import pytest from sqlalchemy import create_engine, inspect, text from freqtrade import constants -from freqtrade.enums import InterestMode +from freqtrade.enums import TradingMode from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db from tests.conftest import create_mock_trades, create_mock_trades_with_leverage, log_has, log_has_re @@ -91,7 +91,7 @@ def test_enter_exit_side(fee): @pytest.mark.usefixtures("init_persistence") -def test__set_stop_loss_isolated_liq(fee): +def test_set_stop_loss_isolated_liq(fee): trade = Trade( id=2, pair='ADA/USDT', @@ -106,7 +106,7 @@ def test__set_stop_loss_isolated_liq(fee): is_short=False, leverage=2.0 ) - trade.set_isolated_liq(0.09) + trade.set_isolated_liq(isolated_liq=0.09) assert trade.isolated_liq == 0.09 assert trade.stop_loss == 0.09 assert trade.initial_stop_loss == 0.09 @@ -116,12 +116,12 @@ def test__set_stop_loss_isolated_liq(fee): assert trade.stop_loss == 0.1 assert trade.initial_stop_loss == 0.09 - trade.set_isolated_liq(0.08) + trade.set_isolated_liq(isolated_liq=0.08) assert trade.isolated_liq == 0.08 assert trade.stop_loss == 0.1 assert trade.initial_stop_loss == 0.09 - trade.set_isolated_liq(0.11) + trade.set_isolated_liq(isolated_liq=0.11) assert trade.isolated_liq == 0.11 assert trade.stop_loss == 0.11 assert trade.initial_stop_loss == 0.09 @@ -145,7 +145,7 @@ def test__set_stop_loss_isolated_liq(fee): trade.stop_loss = None trade.initial_stop_loss = None - trade.set_isolated_liq(0.09) + trade.set_isolated_liq(isolated_liq=0.09) assert trade.isolated_liq == 0.09 assert trade.stop_loss == 0.09 assert trade.initial_stop_loss == 0.09 @@ -155,12 +155,12 @@ def test__set_stop_loss_isolated_liq(fee): assert trade.stop_loss == 0.08 assert trade.initial_stop_loss == 0.09 - trade.set_isolated_liq(0.1) + trade.set_isolated_liq(isolated_liq=0.1) assert trade.isolated_liq == 0.1 assert trade.stop_loss == 0.08 assert trade.initial_stop_loss == 0.09 - trade.set_isolated_liq(0.07) + trade.set_isolated_liq(isolated_liq=0.07) assert trade.isolated_liq == 0.07 assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.09 @@ -234,26 +234,26 @@ def test_interest(market_buy_order_usdt, fee): open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), fee_open=fee.return_value, fee_close=fee.return_value, - exchange='kraken', + exchange='binance', leverage=3.0, interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY + trading_mode=TradingMode.MARGIN ) # 10min, 3x leverage # binance assert round(float(trade.calculate_interest()), 8) == round(0.0008333333333333334, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.040 # Short trade.is_short = True trade.recalc_open_trade_value() # binace - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert float(trade.calculate_interest()) == 0.000625 # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert isclose(float(trade.calculate_interest()), 0.030) # 5hr, long @@ -261,40 +261,40 @@ def test_interest(market_buy_order_usdt, fee): trade.is_short = False trade.recalc_open_trade_value() # binance - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest()), 8) == round(0.004166666666666667, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.06 # short trade.is_short = True trade.recalc_open_trade_value() # binace - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest()), 8) == round(0.0031249999999999997, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.045 # 0.00025 interest, 5hr, long trade.is_short = False trade.recalc_open_trade_value() # binance - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest(interest_rate=0.00025)), 8) == round(0.0020833333333333333, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert isclose(float(trade.calculate_interest(interest_rate=0.00025)), 0.03) # short trade.is_short = True trade.recalc_open_trade_value() # binace - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest(interest_rate=0.00025)), 8) == round(0.0015624999999999999, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest(interest_rate=0.00025)) == 0.0225 # 5x leverage, 0.0005 interest, 5hr, long @@ -302,19 +302,19 @@ def test_interest(market_buy_order_usdt, fee): trade.recalc_open_trade_value() trade.leverage = 5.0 # binance - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest()), 8) == 0.005 # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == round(0.07200000000000001, 8) # short trade.is_short = True trade.recalc_open_trade_value() # binace - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(float(trade.calculate_interest()), 8) == round(0.0031249999999999997, 8) # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.045 # 1x leverage, 0.0005 interest, 5hr @@ -322,19 +322,19 @@ def test_interest(market_buy_order_usdt, fee): trade.recalc_open_trade_value() trade.leverage = 1.0 # binance - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert float(trade.calculate_interest()) == 0.0 # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.0 # short trade.is_short = True trade.recalc_open_trade_value() # binace - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert float(trade.calculate_interest()) == 0.003125 # kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert float(trade.calculate_interest()) == 0.045 @@ -506,7 +506,7 @@ def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, ca open_date=arrow.utcnow().datetime, fee_open=fee.return_value, fee_close=fee.return_value, - exchange='binance', + exchange='binance' ) assert trade.open_order_id is None assert trade.close_profit is None @@ -550,7 +550,7 @@ def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, ca is_short=True, leverage=3.0, interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY + trading_mode=TradingMode.MARGIN ) trade.open_order_id = 'something' trade.update(limit_sell_order_usdt) @@ -628,7 +628,6 @@ def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt amount=30.0, open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', @@ -644,12 +643,12 @@ def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) # 3x leverage, binance trade.leverage = 3 - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert trade._calc_open_trade_value() == 60.15 assert round(trade.calc_close_trade_value(), 8) == 65.83416667 assert trade.calc_profit() == round(5.684166670000003, 8) assert trade.calc_profit_ratio() == round(0.2834995845386534, 8) - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" # 3x leverage, kraken assert trade._calc_open_trade_value() == 60.15 assert trade.calc_close_trade_value() == 65.795 @@ -662,7 +661,7 @@ def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt assert trade.calc_close_trade_value() == 66.231165 assert trade.calc_profit() == round(-6.381165000000003, 8) assert trade.calc_profit_ratio() == round(-0.319857894736842, 8) - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" # 3x leverage, short, binance assert trade._calc_open_trade_value() == 59.85 assert trade.calc_close_trade_value() == 66.1663784375 @@ -675,7 +674,7 @@ def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt assert trade.calc_profit() == round(-6.316378437500013, 8) assert trade.calc_profit_ratio() == round(-0.1055368159983292, 8) # 1x leverage, short, kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert trade._calc_open_trade_value() == 59.850 assert trade.calc_close_trade_value() == 66.231165 assert trade.calc_profit() == -6.381165 @@ -694,7 +693,6 @@ def test_trade_close(limit_buy_order_usdt, limit_sell_order_usdt, fee): fee_close=fee.return_value, open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY, exchange='binance', ) assert trade.close_profit is None @@ -804,9 +802,13 @@ def test_calc_open_trade_value(limit_buy_order_usdt, fee): trade.is_short = True trade.recalc_open_trade_value() assert trade._calc_open_trade_value() == 59.85 + + # 3x short margin leverage trade.leverage = 3 - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert trade._calc_open_trade_value() == 59.85 + + # 3x long margin leverage trade.is_short = False trade.recalc_open_trade_value() assert trade._calc_open_trade_value() == 60.15 @@ -831,8 +833,7 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY + interest_rate=0.0005 ) trade.open_order_id = 'close_trade' trade.update(limit_buy_order_usdt) @@ -849,7 +850,7 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee assert round(trade.calc_close_trade_value(rate=2.5, fee=0.003), 8) == 74.77416667 # 3x leverage kraken - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert trade.calc_close_trade_value(rate=2.5) == 74.7725 assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 74.735 @@ -860,7 +861,7 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 75.300225 # 3x leverage binance, short - trade.interest_mode = InterestMode.HOURSPERDAY + trade.exchange = "binance" assert round(trade.calc_close_trade_value(rate=2.5), 8) == 75.18906641 assert round(trade.calc_close_trade_value(rate=2.5, fee=0.003), 8) == 75.22656719 @@ -870,7 +871,7 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee assert round(trade.calc_close_trade_value(rate=2.5, fee=0.003), 8) == 75.22656719 # 1x leverage kraken, short - trade.interest_mode = InterestMode.HOURSPER4 + trade.exchange = "kraken" assert round(trade.calc_close_trade_value(rate=2.5), 8) == 75.2626875 assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 75.300225 @@ -1013,7 +1014,6 @@ def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): open_rate=2.0, open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance' @@ -1047,62 +1047,62 @@ def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): # 3x leverage, long ################################################### trade.leverage = 3.0 # Higher than open rate - 2.1 quote - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=2.1, fee=0.0025) == 2.69166667 - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=2.1, fee=0.0025) == 2.6525 # 1.9 quote - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=1.9, fee=0.0025) == -3.29333333 - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=1.9, fee=0.0025) == -3.3325 # 2.2 quote - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(fee=0.0025) == 5.68416667 - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(fee=0.0025) == 5.645 # 3x leverage, short ################################################### trade.is_short = True trade.recalc_open_trade_value() # 2.1 quote - Higher than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=2.1, fee=0.0025) == round(-3.308815781249997, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=2.1, fee=0.0025) == -3.3706575 # 1.9 quote - Lower than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=1.9, fee=0.0025) == round(2.7063095312499996, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=1.9, fee=0.0025) == 2.6503575 # Test when we apply a Sell order. Uses sell order used above - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(fee=0.0025) == round(-6.316378437499999, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(fee=0.0025) == -6.381165 # 1x leverage, short ################################################### trade.leverage = 1.0 # 2.1 quote - Higher than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=2.1, fee=0.0025) == round(-3.308815781249997, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=2.1, fee=0.0025) == -3.3706575 # 1.9 quote - Lower than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(rate=1.9, fee=0.0025) == round(2.7063095312499996, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(rate=1.9, fee=0.0025) == 2.6503575 # Test when we apply a Sell order. Uses sell order used above - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit(fee=0.0025) == round(-6.316378437499999, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit(fee=0.0025) == -6.381165 @@ -1115,7 +1115,6 @@ def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee): open_rate=2.0, open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance' @@ -1150,62 +1149,62 @@ def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee): # 3x leverage, long ################################################### trade.leverage = 3.0 # 2.1 quote - Higher than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio(rate=2.1) == round(0.13424771421446402, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=2.1) == round(0.13229426433915248, 8) # 1.9 quote - Lower than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio(rate=1.9) == round(-0.16425602643391513, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=1.9) == round(-0.16620947630922667, 8) # Test when we apply a Sell order. Uses sell order used above - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio() == round(0.2834995845386534, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio() == round(0.2815461346633419, 8) # 3x leverage, short ################################################### trade.is_short = True trade.recalc_open_trade_value() # 2.1 quote - Higher than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio(rate=2.1) == round(-0.1658554276315789, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=2.1) == round(-0.16895526315789455, 8) # 1.9 quote - Lower than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio(rate=1.9) == round(0.13565461309523819, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=1.9) == round(0.13285000000000002, 8) # Test when we apply a Sell order. Uses sell order used above - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio() == round(-0.3166104479949876, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio() == round(-0.319857894736842, 8) # 1x leverage, short ################################################### trade.leverage = 1.0 # 2.1 quote - Higher than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" # binance assert trade.calc_profit_ratio(rate=2.1) == round(-0.05528514254385963, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=2.1) == round(-0.05631842105263152, 8) # 1.9 quote - Lower than open rate - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" assert trade.calc_profit_ratio(rate=1.9) == round(0.045218204365079395, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio(rate=1.9) == round(0.04428333333333334, 8) # Test when we apply a Sell order. Uses sell order used above - trade.interest_mode = InterestMode.HOURSPERDAY # binance + trade.exchange = "binance" assert trade.calc_profit_ratio() == round(-0.1055368159983292, 8) - trade.interest_mode = InterestMode.HOURSPER4 # kraken + trade.exchange = "kraken" assert trade.calc_profit_ratio() == round(-0.106619298245614, 8) @@ -1541,8 +1540,7 @@ def test_adjust_stop_loss_short(fee): exchange='binance', open_rate=1, max_rate=1, - is_short=True, - interest_mode=InterestMode.HOURSPERDAY + is_short=True ) trade.adjust_stop_loss(trade.open_rate, 0.05, True) assert trade.stop_loss == 1.05 @@ -1575,11 +1573,11 @@ def test_adjust_stop_loss_short(fee): assert trade.initial_stop_loss_pct == 0.05 # Initial is true but stop_loss set - so doesn't do anything trade.adjust_stop_loss(0.3, -0.1, True) - assert round(trade.stop_loss, 8) == 0.66 # TODO-mg: What is this test? + assert round(trade.stop_loss, 8) == 0.66 assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss_pct == 0.05 assert trade.stop_loss_pct == 0.1 - trade.set_isolated_liq(0.63) + trade.set_isolated_liq(isolated_liq=0.63) trade.adjust_stop_loss(0.59, -0.1) assert trade.stop_loss == 0.63 assert trade.isolated_liq == 0.63 @@ -1858,8 +1856,7 @@ def test_stoploss_reinitialization_short(default_conf, fee): open_rate=1, max_rate=1, is_short=True, - leverage=3.0, - interest_mode=InterestMode.HOURSPERDAY + leverage=3.0 ) trade.adjust_stop_loss(trade.open_rate, -0.05, True) assert trade.stop_loss == 1.05 @@ -1899,7 +1896,7 @@ def test_stoploss_reinitialization_short(default_conf, fee): assert trade_adj.initial_stop_loss == 1.04 assert trade_adj.initial_stop_loss_pct == 0.04 # Stoploss can't go above liquidation price - trade_adj.set_isolated_liq(1.0) + trade_adj.set_isolated_liq(isolated_liq=1.0) trade.adjust_stop_loss(0.97, -0.04) assert trade_adj.stop_loss == 1.0 assert trade_adj.stop_loss == 1.0