Added liquidation_price and interest functions

This commit is contained in:
Sam Germain 2021-08-06 01:15:18 -06:00
parent ecdecb02fa
commit fe2efe0ccd
10 changed files with 364 additions and 133 deletions

View File

@ -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

View File

@ -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")

View File

@ -1 +1,3 @@
# flake8: noqa: F401
from freqtrade.leverage.interest import interest
from freqtrade.leverage.liquidation_price import liquidation_price

View File

@ -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")

View File

@ -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)

View File

@ -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}
"""))

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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