diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 3a3457354..39997a8f4 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -151,8 +151,6 @@ def migrate_orders_table(decl_base, inspector, engine, table_back_name: str, col decl_base.metadata.create_all(engine) leverage = get_column_def(cols, 'leverage', '1.0') # sqlite does not support literals for booleans - is_short = get_column_def(cols, 'is_short', '0') - # TODO-mg: Should liquidation price go in here? with engine.begin() as connection: connection.execute(text(f""" insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 3500b9c8a..9e2e99063 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -236,7 +236,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 + stake_amount: float = 0.0 # TODO: This should probably be computed amount: float = 0.0 amount_requested: Optional[float] = None open_date: datetime @@ -273,7 +273,7 @@ class LocalTrade(): @property def has_no_leverage(self) -> bool: """Returns true if this is a non-leverage, non-short trade""" - return (self.leverage == 1.0 and not self.is_short) or self.leverage is None + return ((self.leverage or self.leverage is None) == 1.0 and not self.is_short) @property def borrowed(self) -> float: @@ -285,7 +285,7 @@ class LocalTrade(): if self.has_no_leverage: return 0.0 elif not self.is_short: - return self.stake_amount * (self.leverage-1) + return (self.amount * self.open_rate) * ((self.leverage-1)/self.leverage) else: return self.amount @@ -351,6 +351,10 @@ class LocalTrade(): self.liquidation_price = liquidation_price + def set_is_short(self, is_short: bool): + self.is_short = is_short + self.recalc_open_trade_value() + def __repr__(self): open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed' @@ -635,7 +639,8 @@ class LocalTrade(): def recalc_open_trade_value(self) -> None: """ Recalculate open_trade_value. - Must be called whenever open_rate or fee_open is changed. + Must be called whenever open_rate, fee_open or is_short is changed. + """ self.open_trade_value = self._calc_open_trade_value() diff --git a/tests/conftest.py b/tests/conftest.py index eb0c14a45..2b0aee336 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -205,16 +205,22 @@ def create_mock_trades(fee, use_db: bool = True): # Simulate dry_run entries trade = mock_trade_1(fee) add_trade(trade) + trade = mock_trade_2(fee) add_trade(trade) + trade = mock_trade_3(fee) add_trade(trade) + trade = mock_trade_4(fee) add_trade(trade) + trade = mock_trade_5(fee) add_trade(trade) + trade = mock_trade_6(fee) add_trade(trade) + if use_db: Trade.query.session.flush() @@ -231,6 +237,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True): # Simulate dry_run entries trade = mock_trade_1(fee) add_trade(trade) + trade = mock_trade_2(fee) add_trade(trade) @@ -248,6 +255,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True): trade = short_trade(fee) add_trade(trade) + trade = leverage_trade(fee) add_trade(trade) if use_db: @@ -2111,105 +2119,12 @@ def saved_hyperopt_results(): for res in hyperopt_res: res['results_metrics']['holding_avg_s'] = res['results_metrics']['holding_avg' ].total_seconds() + return hyperopt_res -# * Margin Tests - @pytest.fixture(scope='function') -def limit_short_order_open(): - return { - 'id': 'mocked_limit_short', - 'type': 'limit', - 'side': 'sell', - 'symbol': 'mocked', - 'datetime': arrow.utcnow().isoformat(), - 'timestamp': arrow.utcnow().int_timestamp, - 'price': 0.00001173, - 'amount': 90.99181073, - 'leverage': 1.0, - 'filled': 0.0, - 'cost': 0.00106733393, - 'remaining': 90.99181073, - 'status': 'open', - 'is_short': True - } - - -@pytest.fixture -def limit_exit_short_order_open(): - return { - 'id': 'mocked_limit_exit_short', - 'type': 'limit', - 'side': 'buy', - 'pair': 'mocked', - 'datetime': arrow.utcnow().isoformat(), - 'timestamp': arrow.utcnow().int_timestamp, - 'price': 0.00001099, - 'amount': 90.99370639272354, - 'filled': 0.0, - 'remaining': 90.99370639272354, - 'status': 'open', - 'leverage': 1.0 - } - - -@pytest.fixture(scope='function') -def limit_short_order(limit_short_order_open): - order = deepcopy(limit_short_order_open) - order['status'] = 'closed' - order['filled'] = order['amount'] - order['remaining'] = 0.0 - return order - - -@pytest.fixture -def limit_exit_short_order(limit_exit_short_order_open): - order = deepcopy(limit_exit_short_order_open) - order['remaining'] = 0.0 - order['filled'] = order['amount'] - order['status'] = 'closed' - return order - - -@pytest.fixture(scope='function') -def market_short_order(): - return { - 'id': 'mocked_market_short', - 'type': 'market', - 'side': 'sell', - 'symbol': 'mocked', - 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004173, - 'amount': 275.97543219, - 'filled': 275.97543219, - 'remaining': 0.0, - 'status': 'closed', - 'is_short': True, - 'leverage': 3.0, - } - - -@pytest.fixture -def market_exit_short_order(): - return { - 'id': 'mocked_limit_exit_short', - 'type': 'market', - 'side': 'buy', - 'symbol': 'mocked', - 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004099, - 'amount': 276.113419906095, - 'filled': 276.113419906095, - 'remaining': 0.0, - 'status': 'closed', - 'leverage': 3.0 - } - - -# leverage 3x -@pytest.fixture(scope='function') -def limit_lev_buy_order_open(): +def limit_buy_order_usdt_open(): return { 'id': 'mocked_limit_buy', 'type': 'limit', @@ -2217,20 +2132,18 @@ def limit_lev_buy_order_open(): 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, - 'price': 0.00001099, - 'amount': 272.97543219, + 'price': 2.00, + 'amount': 30.0, 'filled': 0.0, - 'cost': 0.0009999999999226999, - 'remaining': 272.97543219, - 'leverage': 3.0, - 'status': 'open', - 'exchange': 'binance', + 'cost': 60.0, + 'remaining': 30.0, + 'status': 'open' } @pytest.fixture(scope='function') -def limit_lev_buy_order(limit_lev_buy_order_open): - order = deepcopy(limit_lev_buy_order_open) +def limit_buy_order_usdt(limit_buy_order_usdt_open): + order = deepcopy(limit_buy_order_usdt_open) order['status'] = 'closed' order['filled'] = order['amount'] order['remaining'] = 0.0 @@ -2238,7 +2151,7 @@ def limit_lev_buy_order(limit_lev_buy_order_open): @pytest.fixture -def limit_lev_sell_order_open(): +def limit_sell_order_usdt_open(): return { 'id': 'mocked_limit_sell', 'type': 'limit', @@ -2246,19 +2159,17 @@ def limit_lev_sell_order_open(): 'pair': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, - 'price': 0.00001173, - 'amount': 272.97543219, + 'price': 2.20, + 'amount': 30.0, 'filled': 0.0, - 'remaining': 272.97543219, - 'leverage': 3.0, - 'status': 'open', - 'exchange': 'binance' + 'remaining': 30.0, + 'status': 'open' } @pytest.fixture -def limit_lev_sell_order(limit_lev_sell_order_open): - order = deepcopy(limit_lev_sell_order_open) +def limit_sell_order_usdt(limit_sell_order_usdt_open): + order = deepcopy(limit_sell_order_usdt_open) order['remaining'] = 0.0 order['filled'] = order['amount'] order['status'] = 'closed' @@ -2266,36 +2177,32 @@ def limit_lev_sell_order(limit_lev_sell_order_open): @pytest.fixture(scope='function') -def market_lev_buy_order(): +def market_buy_order_usdt(): return { 'id': 'mocked_market_buy', 'type': 'market', 'side': 'buy', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004099, - 'amount': 275.97543219, - 'filled': 275.97543219, + 'price': 2.00, + 'amount': 30.0, + 'filled': 30.0, 'remaining': 0.0, - 'status': 'closed', - 'exchange': 'kraken', - 'leverage': 3.0 + 'status': 'closed' } @pytest.fixture -def market_lev_sell_order(): +def market_sell_order_usdt(): return { 'id': 'mocked_limit_sell', 'type': 'market', 'side': 'sell', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004173, - 'amount': 275.97543219, - 'filled': 275.97543219, + 'price': 2.20, + 'amount': 30.0, + 'filled': 30.0, 'remaining': 0.0, - 'status': 'closed', - 'leverage': 3.0, - 'exchange': 'kraken' + 'status': 'closed' } diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index 226c49305..cad6d195c 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta, timezone +from freqtrade.enums import InterestMode from freqtrade.persistence.models import Order, Trade @@ -382,8 +383,8 @@ 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), - # borrowed= - is_short=True + is_short=True, + interest_mode=InterestMode.HOURSPERDAY ) o = Order.parse_from_ccxt_object(short_order(), 'ETC/BTC', 'sell') trade.orders.append(o) @@ -466,13 +467,14 @@ def leverage_trade(fee): close_profit_abs=2.5983135000000175, exchange='kraken', is_open=False, - open_order_id='dry_run_leverage_sell_12345', + open_order_id='dry_run_leverage_buy_12368', strategy='DefaultStrategy', timeframe=5, 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_rate=0.0005, + interest_mode=InterestMode.HOURSPER4 ) o = Order.parse_from_ccxt_object(leverage_order(), 'DOGE/BTC', 'sell') trade.orders.append(o) diff --git a/tests/persistence/test_persistence_leverage.py b/tests/persistence/test_persistence_leverage.py deleted file mode 100644 index da1cbd265..000000000 --- a/tests/persistence/test_persistence_leverage.py +++ /dev/null @@ -1,638 +0,0 @@ -from datetime import datetime, timedelta -from math import isclose - -import pytest - -from freqtrade.enums import InterestMode -from freqtrade.persistence import Trade -from tests.conftest import log_has_re - - -@pytest.mark.usefixtures("init_persistence") -def test_interest_kraken_lev(market_lev_buy_order, fee): - """ - Market trade on Kraken at 3x and 5x leverage - Short trade - interest_rate: 0.05%, 0.25% per 4 hrs - open_rate: 0.00004099 base - close_rate: 0.00004173 base - stake_amount: 0.0037707443218227 - borrowed: 0.0075414886436454 - amount: - 275.97543219 crypto - 459.95905365 crypto - borrowed: - 0.0075414886436454 base - 0.0150829772872908 base - time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) - 5 hours = 5/4 - - interest: borrowed * interest_rate * ceil(1 + time-periods) - = 0.0075414886436454 * 0.0005 * ceil(2) = 7.5414886436454e-06 base - = 0.0075414886436454 * 0.00025 * ceil(9/4) = 5.65611648273405e-06 base - = 0.0150829772872908 * 0.0005 * ceil(9/4) = 2.26244659309362e-05 base - = 0.0150829772872908 * 0.00025 * ceil(2) = 7.5414886436454e-06 base - """ - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0037707443218227, - amount=275.97543219, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - - assert float(trade.calculate_interest()) == 7.5414886436454e-06 - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 11) - ) == round(5.65611648273405e-06, 11) - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0037707443218227, - amount=459.95905365, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - leverage=5.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - - assert float(round(trade.calculate_interest(), 11) - ) == round(2.26244659309362e-05, 11) - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - trade.interest_rate = 0.00025 - assert float(trade.calculate_interest(interest_rate=0.00025)) == 7.5414886436454e-06 - - -@pytest.mark.usefixtures("init_persistence") -def test_interest_binance_lev(market_lev_buy_order, fee): - """ - Market trade on Kraken at 3x and 5x leverage - Short trade - interest_rate: 0.05%, 0.25% per 4 hrs - open_rate: 0.00001099 base - close_rate: 0.00001173 base - stake_amount: 0.0009999999999226999 - borrowed: 0.0019999999998453998 - amount: - 90.99181073 * leverage(3) = 272.97543219 crypto - 90.99181073 * leverage(5) = 454.95905365 crypto - borrowed: - 0.0019999999998453998 base - 0.0039999999996907995 base - time-periods: 10 minutes(rounds up to 1/24 time-period of 24hrs) - 5 hours = 5/24 - - interest: borrowed * interest_rate * time-periods - = 0.0019999999998453998 * 0.00050 * 1/24 = 4.166666666344583e-08 base - = 0.0019999999998453998 * 0.00025 * 5/24 = 1.0416666665861459e-07 base - = 0.0039999999996907995 * 0.00050 * 5/24 = 4.1666666663445834e-07 base - = 0.0039999999996907995 * 0.00025 * 1/24 = 4.166666666344583e-08 base - """ - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0009999999999226999, - amount=272.97543219, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - # 10 minutes round up to 4 hours evenly on kraken so we can predict the them more accurately - assert round(float(trade.calculate_interest()), 22) == round(4.166666666344583e-08, 22) - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - # All trade > 5 hours will vary slightly due to execution time and interest calculated - assert float(round(trade.calculate_interest(interest_rate=0.00025), 14) - ) == round(1.0416666665861459e-07, 14) - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0009999999999226999, - amount=459.95905365, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - leverage=5.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - - assert float(round(trade.calculate_interest(), 14)) == round(4.1666666663445834e-07, 14) - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 22) - ) == round(4.166666666344583e-08, 22) - - -@pytest.mark.usefixtures("init_persistence") -def test_update_open_order_lev(limit_lev_buy_order): - trade = Trade( - pair='ETH/BTC', - stake_amount=1.00, - open_rate=0.01, - amount=5, - fee_open=0.1, - fee_close=0.1, - interest_rate=0.0005, - exchange='binance', - interest_mode=InterestMode.HOURSPERDAY - ) - assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - limit_lev_buy_order['status'] = 'open' - trade.update(limit_lev_buy_order) - assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - - -@pytest.mark.usefixtures("init_persistence") -def test_calc_open_trade_value_lev(market_lev_buy_order, fee): - """ - 10 minute leveraged market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.00004099 base - close_rate: 0.00004173 base - amount: 91.99181073 * leverage(3) = 275.97543219 crypto - stake_amount: 0.0037707443218227 - borrowed: 0.0075414886436454 base - time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) - interest: borrowed * interest_rate * time-periods - = 0.0075414886436454 * 0.0005 * 1 = 7.5414886436454e-06 crypto - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) - = 0.01134051354788177 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00004099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - interest_rate=0.0005, - exchange='kraken', - leverage=3, - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'open_trade' - trade.update(market_lev_buy_order) # Buy @ 0.00001099 - # Get the open rate price with the standard fee rate - assert trade._calc_open_trade_value() == 0.01134051354788177 - trade.fee_open = 0.003 - # Get the open rate price with a custom fee rate - assert trade._calc_open_trade_value() == 0.011346169664364504 - - -@pytest.mark.usefixtures("init_persistence") -def test_calc_open_close_trade_price_lev(limit_lev_buy_order, limit_lev_sell_order, fee): - """ - 5 hour leveraged trade on Binance - - fee: 0.25% base - interest_rate: 0.05% per day - open_rate: 0.00001099 base - close_rate: 0.00001173 base - amount: 272.97543219 crypto - stake_amount: 0.0009999999999226999 base - borrowed: 0.0019999999998453998 base - time-periods: 5 hours(rounds up to 5/24 time-period of 1 day) - interest: borrowed * interest_rate * time-periods - = 0.0019999999998453998 * 0.0005 * 5/24 = 2.0833333331722917e-07 base - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (272.97543219 * 0.00001099) + (272.97543219 * 0.00001099 * 0.0025) - = 0.0030074999997675204 - close_value: ((amount_closed * close_rate) - (amount_closed * close_rate * fee)) - interest - = (272.97543219 * 0.00001173) - - (272.97543219 * 0.00001173 * 0.0025) - - 2.0833333331722917e-07 - = 0.003193788481706411 - stake_value: (amount/lev * open_rate) + (amount/lev * open_rate * fee) - = (272.97543219/3 * 0.00001099) + (272.97543219/3 * 0.00001099 * 0.0025) - = 0.0010024999999225066 - total_profit = close_value - open_value - = 0.003193788481706411 - 0.0030074999997675204 - = 0.00018628848193889044 - total_profit_percentage = total_profit / stake_value - = 0.00018628848193889054 / 0.0010024999999225066 - = 0.18582392214792087 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0009999999999226999, - open_rate=0.01, - amount=5, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.open_order_id = 'something' - trade.update(limit_lev_buy_order) - assert trade._calc_open_trade_value() == 0.00300749999976752 - trade.update(limit_lev_sell_order) - - # Is slightly different due to compilation time changes. Interest depends on time - assert round(trade.calc_close_trade_value(), 11) == round(0.003193788481706411, 11) - # Profit in BTC - assert round(trade.calc_profit(), 8) == round(0.00018628848193889054, 8) - # Profit in percent - assert round(trade.calc_profit_ratio(), 8) == round(0.18582392214792087, 8) - - -@pytest.mark.usefixtures("init_persistence") -def test_trade_close_lev(fee): - """ - 5 hour leveraged market trade on Kraken at 3x leverage - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.1 base - close_rate: 0.2 base - amount: 5 * leverage(3) = 15 crypto - stake_amount: 0.5 - borrowed: 1 base - time-periods: 5/4 periods of 4hrs - interest: borrowed * interest_rate * ceil(1 + time-periods) - = 1 * 0.0005 * ceil(9/4) = 0.0015 crypto - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (15 * 0.1) + (15 * 0.1 * 0.0025) - = 1.50375 - close_value: (amount * close_rate) + (amount * close_rate * fee) - interest - = (15 * 0.2) - (15 * 0.2 * 0.0025) - 0.0015 - = 2.991 - total_profit = close_value - open_value - = 2.991 - 1.50375 - = 1.4872500000000002 - total_profit_ratio = ((close_value/open_value) - 1) * leverage - = ((2.991/1.50375) - 1) * 3 - = 2.96708229426434 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.5, - open_rate=0.1, - amount=15, - is_open=True, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - exchange='kraken', - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - assert trade.close_profit is None - assert trade.close_date is None - assert trade.is_open is True - trade.close(0.2) - assert trade.is_open is False - assert trade.close_profit == round(2.96708229426434, 8) - assert trade.close_date is not None - - # TODO-mg: Remove these comments probably - # new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime, - # assert trade.close_date != new_date - # # Close should NOT update close_date if the trade has been closed already - # assert trade.is_open is False - # trade.close_date = new_date - # trade.close(0.02) - # assert trade.close_date == new_date - - -@pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_lev(market_lev_buy_order, market_lev_sell_order, fee): - """ - 10 minute leveraged market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.00004099 base - close_rate: 0.00004173 base - amount: 91.99181073 * leverage(3) = 275.97543219 crypto - stake_amount: 0.0037707443218227 - borrowed: 0.0075414886436454 base - time-periods: 10 minutes = 2 - interest: borrowed * interest_rate * time-periods - = 0.0075414886436454 * 0.0005 * 2 = 7.5414886436454e-06 crypto - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) - = 0.01134051354788177 - close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest - = (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.0025) - 7.5414886436454e-06 - = 0.0033894815024978933 - = (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.003) - 7.5414886436454e-06 - = 0.003387778734081281 - = (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.005) - 7.5414886436454e-06 - = 0.011451331022718612 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0037707443218227, - amount=5, - open_rate=0.00004099, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - interest_rate=0.0005, - leverage=3.0, - exchange='kraken', - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'close_trade' - trade.update(market_lev_buy_order) # Buy @ 0.00001099 - # Get the close rate price with a custom close rate and a regular fee rate - assert isclose(trade.calc_close_trade_value(rate=0.00001234), 0.0033894815024978933) - # Get the close rate price with a custom close rate and a custom fee rate - assert isclose(trade.calc_close_trade_value(rate=0.00001234, fee=0.003), 0.003387778734081281) - # Test when we apply a Sell order, and ask price with a custom fee rate - trade.update(market_lev_sell_order) - assert isclose(trade.calc_close_trade_value(fee=0.005), 0.011451331022718612) - - -@pytest.mark.usefixtures("init_persistence") -def test_update_with_binance_lev(limit_lev_buy_order, limit_lev_sell_order, fee, caplog): - """ - 10 minute leveraged limit trade on binance at 3x leverage - - Leveraged trade - fee: 0.25% base - interest_rate: 0.05% per day - open_rate: 0.00001099 base - close_rate: 0.00001173 base - amount: 272.97543219 crypto - stake_amount: 0.0009999999999226999 base - borrowed: 0.0019999999998453998 base - time-periods: 10 minutes(rounds up to 1/24 time-period of 1 day) - interest: borrowed * interest_rate * time-periods - = 0.0019999999998453998 * 0.0005 * 1/24 = 4.166666666344583e-08 base - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (272.97543219 * 0.00001099) + (272.97543219 * 0.00001099 * 0.0025) - = 0.0030074999997675204 - stake_value = (amount/lev * open_rate) + (amount/lev * open_rate * fee) - = (272.97543219 * 0.00001099) + (272.97543219 * 0.00001099 * 0.0025) - = 0.0010024999999225066 - close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - = (272.97543219 * 0.00001173) - (272.97543219 * 0.00001173 * 0.0025) - = 0.003193996815039728 - stake_value: (amount/lev * open_rate) + (amount/lev * open_rate * fee) - = (272.97543219/3 * 0.00001099) + (272.97543219/3 * 0.00001099 * 0.0025) - = 0.0010024999999225066 - total_profit = close_value - open_value - interest - = 0.003193996815039728 - 0.0030074999997675204 - 4.166666666344583e-08 - = 0.00018645514860554435 - total_profit_percentage = total_profit / stake_value - = 0.00018645514860554435 / 0.0010024999999225066 - = 0.1859901731869899 - - """ - trade = Trade( - id=2, - pair='ETH/BTC', - stake_amount=0.0009999999999226999, - open_rate=0.01, - amount=5, - is_open=True, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - interest_rate=0.0005, - exchange='binance', - interest_mode=InterestMode.HOURSPERDAY - ) - # assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - - # trade.open_order_id = 'something' - trade.update(limit_lev_buy_order) - # assert trade.open_order_id is None - assert trade.open_rate == 0.00001099 - assert trade.close_profit is None - assert trade.close_date is None - assert trade.borrowed == 0.0019999999998453998 - assert log_has_re(r"LIMIT_BUY has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=272.97543219, open_rate=0.00001099, open_since=.*\).", - caplog) - caplog.clear() - # trade.open_order_id = 'something' - trade.update(limit_lev_sell_order) - # assert trade.open_order_id is None - assert trade.close_rate == 0.00001173 - assert trade.close_profit == round(0.1859901731869899, 8) - assert trade.close_date is not None - assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=272.97543219, open_rate=0.00001099, open_since=.*\).", - caplog) - - -@pytest.mark.usefixtures("init_persistence") -def test_update_market_order_lev(market_lev_buy_order, market_lev_sell_order, fee, caplog): - """ - 10 minute leveraged market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.00004099 base - close_rate: 0.00004173 base - amount: = 275.97543219 crypto - stake_amount: 0.0037707443218227 - borrowed: 0.0075414886436454 base - interest: borrowed * interest_rate * 1+ceil(hours) - = 0.0075414886436454 * 0.0005 * (1+ceil(1)) = 7.5414886436454e-06 crypto - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) - = 0.01134051354788177 - close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest - = (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.0025) - 7.5414886436454e-06 - = 0.011480122159681833 - stake_value: (amount/lev * open_rate) + (amount/lev * open_rate * fee) - = (275.97543219/3 * 0.00004099) + (275.97543219/3 * 0.00004099 * 0.0025) - = 0.0037801711826272568 - total_profit = close_value - open_value - = 0.011480122159681833 - 0.01134051354788177 - = 0.00013960861180006392 - total_profit_percentage = ((close_value/open_value) - 1) * leverage - = ((0.011480122159681833 / 0.01134051354788177)-1) * 3 - = 0.036931822675563275 - """ - trade = Trade( - id=1, - pair='ETH/BTC', - stake_amount=0.0037707443218227, - amount=5, - open_rate=0.00004099, - is_open=True, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - interest_rate=0.0005, - exchange='kraken', - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'something' - trade.update(market_lev_buy_order) - assert trade.leverage == 3.0 - assert trade.open_order_id is None - assert trade.open_rate == 0.00004099 - assert trade.close_profit is None - assert trade.close_date is None - assert trade.interest_rate == 0.0005 - # TODO: Uncomment the next assert and make it work. - # The logger also has the exact same but there's some spacing in there - assert log_has_re(r"MARKET_BUY has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=275.97543219, open_rate=0.00004099, open_since=.*\).", - caplog) - caplog.clear() - trade.is_open = True - trade.open_order_id = 'something' - trade.update(market_lev_sell_order) - assert trade.open_order_id is None - assert trade.close_rate == 0.00004173 - assert trade.close_profit == round(0.036931822675563275, 8) - assert trade.close_date is not None - # TODO: The amount should maybe be the opening amount + the interest - # TODO: Uncomment the next assert and make it work. - # The logger also has the exact same but there's some spacing in there - assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=275.97543219, open_rate=0.00004099, open_since=.*\).", - caplog) - - -@pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_exception_lev(limit_lev_buy_order, fee): - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.1, - amount=5, - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - interest_rate=0.0005, - leverage=3.0, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.open_order_id = 'something' - trade.update(limit_lev_buy_order) - assert trade.calc_close_trade_value() == 0.0 - - -@pytest.mark.usefixtures("init_persistence") -def test_calc_profit_lev(market_lev_buy_order, market_lev_sell_order, fee): - """ - Leveraged trade on Kraken at 3x leverage - fee: 0.25% base or 0.3% - interest_rate: 0.05%, 0.25% per 4 hrs - open_rate: 0.00004099 base - close_rate: 0.00004173 base - stake_amount: 0.0037707443218227 - amount: 91.99181073 * leverage(3) = 275.97543219 crypto - borrowed: 0.0075414886436454 base - hours: 1/6, 5 hours - - interest: borrowed * interest_rate * ceil(1+hours/4) - = 0.0075414886436454 * 0.0005 * ceil(1+((1/6)/4)) = 7.5414886436454e-06 crypto - = 0.0075414886436454 * 0.00025 * ceil(1+(5/4)) = 5.65611648273405e-06 crypto - = 0.0075414886436454 * 0.0005 * ceil(1+(5/4)) = 1.13122329654681e-05 crypto - = 0.0075414886436454 * 0.00025 * ceil(1+((1/6)/4)) = 3.7707443218227e-06 crypto - open_value: (amount * open_rate) + (amount * open_rate * fee) - = (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) - = 0.01134051354788177 - close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest - (275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.0025) - 7.5414886436454e-06 - = 0.014786300937932227 - (275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.0025) - 5.65611648273405e-06 - = 0.0011973414905908902 - (275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.003) - 1.13122329654681e-05 - = 0.01477511473374746 - (275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.003) - 3.7707443218227e-06 - = 0.0011986238564324662 - stake_value: (amount/lev * open_rate) + (amount/lev * open_rate * fee) - = (275.97543219/3 * 0.00004099) + (275.97543219/3 * 0.00004099 * 0.0025) - = 0.0037801711826272568 - total_profit = close_value - open_value - = 0.014786300937932227 - 0.01134051354788177 = 0.0034457873900504577 - = 0.0011973414905908902 - 0.01134051354788177 = -0.01014317205729088 - = 0.01477511473374746 - 0.01134051354788177 = 0.00343460118586569 - = 0.0011986238564324662 - 0.01134051354788177 = -0.010141889691449303 - total_profit_percentage = ((close_value/open_value) - 1) * leverage - ((0.014786300937932227/0.01134051354788177) - 1) * 3 = 0.9115426851266561 - ((0.0011973414905908902/0.01134051354788177) - 1) * 3 = -2.683257336045103 - ((0.01477511473374746/0.01134051354788177) - 1) * 3 = 0.908583505860866 - ((0.0011986238564324662/0.01134051354788177) - 1) * 3 = -2.6829181011851926 - - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0037707443218227, - amount=5, - open_rate=0.00004099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'something' - trade.update(market_lev_buy_order) # Buy @ 0.00001099 - # Custom closing rate and regular fee rate - - # Higher than open rate - assert trade.calc_profit(rate=0.00005374, interest_rate=0.0005) == round( - 0.0034457873900504577, 8) - assert trade.calc_profit_ratio( - rate=0.00005374, interest_rate=0.0005) == round(0.9115426851266561, 8) - - # Lower than open rate - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - assert trade.calc_profit( - rate=0.00000437, interest_rate=0.00025) == round(-0.01014317205729088, 8) - assert trade.calc_profit_ratio( - rate=0.00000437, interest_rate=0.00025) == round(-2.683257336045103, 8) - - # Custom closing rate and custom fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00005374, fee=0.003, - interest_rate=0.0005) == round(0.00343460118586569, 8) - assert trade.calc_profit_ratio(rate=0.00005374, fee=0.003, - interest_rate=0.0005) == round(0.908583505860866, 8) - - # Lower than open rate - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - assert trade.calc_profit(rate=0.00000437, fee=0.003, - interest_rate=0.00025) == round(-0.010141889691449303, 8) - assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003, - interest_rate=0.00025) == round(-2.6829181011851926, 8) - - # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 - trade.update(market_lev_sell_order) - assert trade.calc_profit() == round(0.00013960861180006392, 8) - assert trade.calc_profit_ratio() == round(0.036931822675563275, 8) - - # Test with a custom fee rate on the close trade - # assert trade.calc_profit(fee=0.003) == 0.00006163 - # assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 diff --git a/tests/persistence/test_persistence_short.py b/tests/persistence/test_persistence_short.py deleted file mode 100644 index 2a1e46615..000000000 --- a/tests/persistence/test_persistence_short.py +++ /dev/null @@ -1,780 +0,0 @@ -from datetime import datetime, timedelta -from math import isclose - -import arrow -import pytest - -from freqtrade.enums import InterestMode -from freqtrade.persistence import Trade, init_db -from tests.conftest import create_mock_trades_with_leverage, log_has_re - - -@pytest.mark.usefixtures("init_persistence") -def test_interest_kraken_short(market_short_order, fee): - """ - Market trade on Kraken at 3x and 8x leverage - Short trade - interest_rate: 0.05%, 0.25% per 4 hrs - open_rate: 0.00004173 base - close_rate: 0.00004099 base - amount: - 275.97543219 crypto - 459.95905365 crypto - borrowed: - 275.97543219 crypto - 459.95905365 crypto - time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) - 5 hours = 5/4 - - interest: borrowed * interest_rate * ceil(1 + time-periods) - = 275.97543219 * 0.0005 * ceil(1+1) = 0.27597543219 crypto - = 275.97543219 * 0.00025 * ceil(9/4) = 0.20698157414249999 crypto - = 459.95905365 * 0.0005 * ceil(9/4) = 0.689938580475 crypto - = 459.95905365 * 0.00025 * ceil(1+1) = 0.229979526825 crypto - """ - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=275.97543219, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - is_short=True, - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - - assert float(round(trade.calculate_interest(), 8)) == round(0.27597543219, 8) - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 8) - ) == round(0.20698157414249999, 8) - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=459.95905365, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - is_short=True, - leverage=5.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - - assert float(round(trade.calculate_interest(), 8)) == round(0.689938580475, 8) - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 8) - ) == round(0.229979526825, 8) - - -@ pytest.mark.usefixtures("init_persistence") -def test_interest_binance_short(market_short_order, fee): - """ - Market trade on Binance at 3x and 5x leverage - Short trade - interest_rate: 0.05%, 0.25% per 1 day - open_rate: 0.00004173 base - close_rate: 0.00004099 base - amount: - 91.99181073 * leverage(3) = 275.97543219 crypto - 91.99181073 * leverage(5) = 459.95905365 crypto - borrowed: - 275.97543219 crypto - 459.95905365 crypto - time-periods: 10 minutes(rounds up to 1/24 time-period of 1 day) - 5 hours = 5/24 - - interest: borrowed * interest_rate * time-periods - = 275.97543219 * 0.0005 * 1/24 = 0.005749488170625 crypto - = 275.97543219 * 0.00025 * 5/24 = 0.0143737204265625 crypto - = 459.95905365 * 0.0005 * 5/24 = 0.047912401421875 crypto - = 459.95905365 * 0.00025 * 1/24 = 0.0047912401421875 crypto - """ - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=275.97543219, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - is_short=True, - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - - assert float(round(trade.calculate_interest(), 8)) == 0.00574949 - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 0.01437372 - - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=459.95905365, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - is_short=True, - leverage=5.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - - assert float(round(trade.calculate_interest(), 8)) == 0.04791240 - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 0.00479124 - - -@ pytest.mark.usefixtures("init_persistence") -def test_calc_open_trade_value_short(market_short_order, fee): - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00004173, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - interest_rate=0.0005, - is_short=True, - leverage=3.0, - exchange='kraken', - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'open_trade' - trade.update(market_short_order) # Buy @ 0.00001099 - # Get the open rate price with the standard fee rate - assert trade._calc_open_trade_value() == 0.011487663648325479 - trade.fee_open = 0.003 - # Get the open rate price with a custom fee rate - assert trade._calc_open_trade_value() == 0.011481905420932834 - - -@ pytest.mark.usefixtures("init_persistence") -def test_update_open_order_short(limit_short_order): - trade = Trade( - pair='ETH/BTC', - stake_amount=1.00, - open_rate=0.01, - amount=5, - leverage=3.0, - fee_open=0.1, - fee_close=0.1, - interest_rate=0.0005, - is_short=True, - exchange='binance', - interest_mode=InterestMode.HOURSPERDAY - ) - assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - limit_short_order['status'] = 'open' - trade.update(limit_short_order) - assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - - -@ pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_exception_short(limit_short_order, fee): - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.1, - amount=15.0, - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - interest_rate=0.0005, - leverage=3.0, - is_short=True, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.open_order_id = 'something' - trade.update(limit_short_order) - assert trade.calc_close_trade_value() == 0.0 - - -@ pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_short(market_short_order, market_exit_short_order, fee): - """ - 10 minute short market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.00004173 base - close_rate: 0.00001234 base - amount: = 275.97543219 crypto - borrowed: 275.97543219 crypto - hours: 10 minutes = 1/6 - interest: borrowed * interest_rate * ceil(1 + hours/4) - = 275.97543219 * 0.0005 * ceil(1 + ((1/6)/4)) = 0.27597543219 crypto - amount_closed: amount + interest = 275.97543219 + 0.27597543219 = 276.25140762219 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - = (276.25140762219 * 0.00004099) + (276.25140762219 * 0.00004099 * 0.005) - = 0.011380162924425737 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - interest_rate=0.0005, - is_short=True, - leverage=3.0, - exchange='kraken', - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'close_trade' - trade.update(market_short_order) # Buy @ 0.00001099 - # Get the close rate price with a custom close rate and a regular fee rate - assert isclose(trade.calc_close_trade_value(rate=0.00001234), 0.0034174647259) - # Get the close rate price with a custom close rate and a custom fee rate - assert isclose(trade.calc_close_trade_value(rate=0.00001234, fee=0.003), 0.0034191691971679986) - # Test when we apply a Sell order, and ask price with a custom fee rate - trade.update(market_exit_short_order) - assert isclose(trade.calc_close_trade_value(fee=0.005), 0.011380162924425737) - - -@ pytest.mark.usefixtures("init_persistence") -def test_calc_open_close_trade_price_short(limit_short_order, limit_exit_short_order, fee): - """ - 5 hour short trade on Binance - Short trade - fee: 0.25% base - interest_rate: 0.05% per day - open_rate: 0.00001173 base - close_rate: 0.00001099 base - amount: 90.99181073 crypto - borrowed: 90.99181073 crypto - stake_amount: 0.0010673339398629 - time-periods: 5 hours = 5/24 - interest: borrowed * interest_rate * time-periods - = 90.99181073 * 0.0005 * 5/24 = 0.009478313617708333 crypto - open_value: (amount * open_rate) - (amount * open_rate * fee) - = (90.99181073 * 0.00001173) - (90.99181073 * 0.00001173 * 0.0025) - = 0.0010646656050132426 - amount_closed: amount + interest = 90.99181073 + 0.009478313617708333 = 91.0012890436177 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - = (91.0012890436177 * 0.00001099) + (91.0012890436177 * 0.00001099 * 0.0025) - = 0.001002604427005832 - stake_value = (amount/lev * open_rate) - (amount/lev * open_rate * fee) - = 0.0010646656050132426 - total_profit = open_value - close_value - = 0.0010646656050132426 - 0.001002604427005832 - = 0.00006206117800741065 - total_profit_percentage = (close_value - open_value) / stake_value - = (0.0010646656050132426 - 0.001002604427005832)/0.0010646656050132426 - = 0.05829170935473088 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0010673339398629, - open_rate=0.01, - amount=5, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.open_order_id = 'something' - trade.update(limit_short_order) - assert trade._calc_open_trade_value() == 0.0010646656050132426 - trade.update(limit_exit_short_order) - - # Is slightly different due to compilation time. Interest depends on time - assert round(trade.calc_close_trade_value(), 11) == round(0.001002604427005832, 11) - # Profit in BTC - assert round(trade.calc_profit(), 8) == round(0.00006206117800741065, 8) - # Profit in percent - assert round(trade.calc_profit_ratio(), 8) == round(0.05829170935473088, 8) - - -@ pytest.mark.usefixtures("init_persistence") -def test_trade_close_short(fee): - """ - Five hour short trade on Kraken at 3x leverage - Short trade - Exchange: Kraken - fee: 0.25% base - interest_rate: 0.05% per 4 hours - open_rate: 0.02 base - close_rate: 0.01 base - leverage: 3.0 - amount: 15 crypto - borrowed: 15 crypto - time-periods: 5 hours = 5/4 - - interest: borrowed * interest_rate * time-periods - = 15 * 0.0005 * ceil(1 + 5/4) = 0.0225 crypto - open_value: (amount * open_rate) - (amount * open_rate * fee) - = (15 * 0.02) - (15 * 0.02 * 0.0025) - = 0.29925 - amount_closed: amount + interest = 15 + 0.009375 = 15.0225 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - = (15.0225 * 0.01) + (15.0225 * 0.01 * 0.0025) - = 0.15060056250000003 - total_profit = open_value - close_value - = 0.29925 - 0.15060056250000003 - = 0.14864943749999998 - total_profit_percentage = (1-(close_value/open_value)) * leverage - = (1 - (0.15060056250000003/0.29925)) * 3 - = 1.4902199248120298 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.1, - open_rate=0.02, - amount=15, - is_open=True, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=4, minutes=55), - exchange='kraken', - is_short=True, - leverage=3.0, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - assert trade.close_profit is None - assert trade.close_date is None - assert trade.is_open is True - trade.close(0.01) - assert trade.is_open is False - assert trade.close_profit == round(1.4902199248120298, 8) - assert trade.close_date is not None - - # TODO-mg: Remove these comments probably - # new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime, - # assert trade.close_date != new_date - # # Close should NOT update close_date if the trade has been closed already - # assert trade.is_open is False - # trade.close_date = new_date - # trade.close(0.02) - # assert trade.close_date == new_date - - -@ pytest.mark.usefixtures("init_persistence") -def test_update_with_binance_short(limit_short_order, limit_exit_short_order, fee, caplog): - """ - 10 minute short limit trade on binance - - Short trade - fee: 0.25% base - interest_rate: 0.05% per day - open_rate: 0.00001173 base - close_rate: 0.00001099 base - amount: 90.99181073 crypto - stake_amount: 0.0010673339398629 base - borrowed: 90.99181073 crypto - time-periods: 10 minutes(rounds up to 1/24 time-period of 1 day) - interest: borrowed * interest_rate * time-periods - = 90.99181073 * 0.0005 * 1/24 = 0.0018956627235416667 crypto - open_value: (amount * open_rate) - (amount * open_rate * fee) - = 90.99181073 * 0.00001173 - 90.99181073 * 0.00001173 * 0.0025 - = 0.0010646656050132426 - amount_closed: amount + interest = 90.99181073 + 0.0018956627235416667 = 90.99370639272354 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - = (90.99370639272354 * 0.00001099) + (90.99370639272354 * 0.00001099 * 0.0025) - = 0.0010025208853391716 - total_profit = open_value - close_value - = 0.0010646656050132426 - 0.0010025208853391716 - = 0.00006214471967407108 - total_profit_percentage = (1 - (close_value/open_value)) * leverage - = (1 - (0.0010025208853391716/0.0010646656050132426)) * 1 - = 0.05837017687191848 - - """ - trade = Trade( - id=2, - pair='ETH/BTC', - stake_amount=0.0010673339398629, - open_rate=0.01, - amount=5, - is_open=True, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - # borrowed=90.99181073, - interest_rate=0.0005, - exchange='binance', - interest_mode=InterestMode.HOURSPERDAY - ) - # assert trade.open_order_id is None - assert trade.close_profit is None - assert trade.close_date is None - assert trade.borrowed == 0.0 - assert trade.is_short is None - # trade.open_order_id = 'something' - trade.update(limit_short_order) - # assert trade.open_order_id is None - assert trade.open_rate == 0.00001173 - assert trade.close_profit is None - assert trade.close_date is None - assert trade.borrowed == 90.99181073 - assert trade.is_short is True - assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001173, open_since=.*\).", - caplog) - caplog.clear() - # trade.open_order_id = 'something' - trade.update(limit_exit_short_order) - # assert trade.open_order_id is None - assert trade.close_rate == 0.00001099 - assert trade.close_profit == round(0.05837017687191848, 8) - assert trade.close_date is not None - assert log_has_re(r"LIMIT_BUY has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001173, open_since=.*\).", - caplog) - - -@ pytest.mark.usefixtures("init_persistence") -def test_update_market_order_short( - market_short_order, - market_exit_short_order, - fee, - caplog -): - """ - 10 minute short market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base - interest_rate: 0.05% per 4 hrs - open_rate: 0.00004173 base - close_rate: 0.00004099 base - amount: = 275.97543219 crypto - stake_amount: 0.0038388182617629 - borrowed: 275.97543219 crypto - time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) - interest: borrowed * interest_rate * time-periods - = 275.97543219 * 0.0005 * 2 = 0.27597543219 crypto - open_value: (amount * open_rate) - (amount * open_rate * fee) - = 275.97543219 * 0.00004173 - 275.97543219 * 0.00004173 * 0.0025 - = 0.011487663648325479 - amount_closed: amount + interest = 275.97543219 + 0.27597543219 = 276.25140762219 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - = (276.25140762219 * 0.00004099) + (276.25140762219 * 0.00004099 * 0.0025) - = 0.0034174647259 - total_profit = open_value - close_value - = 0.011487663648325479 - 0.0034174647259 - = 0.00013580958689582596 - total_profit_percentage = total_profit / stake_amount - = (1 - (close_value/open_value)) * leverage - = (1 - (0.0034174647259/0.011487663648325479)) * 3 - = 0.03546663387440563 - """ - trade = Trade( - id=1, - pair='ETH/BTC', - stake_amount=0.0038388182617629, - amount=5, - open_rate=0.01, - is_open=True, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - interest_rate=0.0005, - exchange='kraken', - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'something' - trade.update(market_short_order) - assert trade.leverage == 3.0 - assert trade.is_short is True - assert trade.open_order_id is None - assert trade.open_rate == 0.00004173 - assert trade.close_profit is None - assert trade.close_date is None - assert trade.interest_rate == 0.0005 - # The logger also has the exact same but there's some spacing in there - assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=275.97543219, open_rate=0.00004173, open_since=.*\).", - caplog) - caplog.clear() - trade.is_open = True - trade.open_order_id = 'something' - trade.update(market_exit_short_order) - assert trade.open_order_id is None - assert trade.close_rate == 0.00004099 - assert trade.close_profit == round(0.03546663387440563, 8) - assert trade.close_date is not None - # TODO-mg: The amount should maybe be the opening amount + the interest - # TODO-mg: Uncomment the next assert and make it work. - # The logger also has the exact same but there's some spacing in there - assert log_has_re(r"MARKET_BUY has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=275.97543219, open_rate=0.00004173, open_since=.*\).", - caplog) - - -@ pytest.mark.usefixtures("init_persistence") -def test_calc_profit_short(market_short_order, market_exit_short_order, fee): - """ - Market trade on Kraken at 3x leverage - Short trade - fee: 0.25% base or 0.3% - interest_rate: 0.05%, 0.025% per 4 hrs - open_rate: 0.00004173 base - close_rate: 0.00004099 base - stake_amount: 0.0038388182617629 - amount: = 275.97543219 crypto - borrowed: 275.97543219 crypto - time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) - 5 hours = 5/4 - - interest: borrowed * interest_rate * time-periods - = 275.97543219 * 0.0005 * ceil(1+1) = 0.27597543219 crypto - = 275.97543219 * 0.00025 * ceil(1+5/4) = 0.20698157414249999 crypto - = 275.97543219 * 0.0005 * ceil(1+5/4) = 0.41396314828499997 crypto - = 275.97543219 * 0.00025 * ceil(1+1) = 0.27597543219 crypto - = 275.97543219 * 0.00025 * ceil(1+1) = 0.27597543219 crypto - open_value: (amount * open_rate) - (amount * open_rate * fee) - = (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.0025) - = 0.011487663648325479 - amount_closed: amount + interest - = 275.97543219 + 0.27597543219 = 276.25140762219 - = 275.97543219 + 0.20698157414249999 = 276.1824137641425 - = 275.97543219 + 0.41396314828499997 = 276.389395338285 - = 275.97543219 + 0.27597543219 = 276.25140762219 - close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee) - (276.25140762219 * 0.00004374) + (276.25140762219 * 0.00004374 * 0.0025) - = 0.012113444660818078 - (276.1824137641425 * 0.00000437) + (276.1824137641425 * 0.00000437 * 0.0025) - = 0.0012099344410196758 - (276.389395338285 * 0.00004374) + (276.389395338285 * 0.00004374 * 0.003) - = 0.012125539968552874 - (276.25140762219 * 0.00000437) + (276.25140762219 * 0.00000437 * 0.003) - = 0.0012102354919246037 - (276.25140762219 * 0.00004099) + (276.25140762219 * 0.00004099 * 0.0025) - = 0.011351854061429653 - total_profit = open_value - close_value - = 0.011487663648325479 - 0.012113444660818078 = -0.0006257810124925996 - = 0.011487663648325479 - 0.0012099344410196758 = 0.010277729207305804 - = 0.011487663648325479 - 0.012125539968552874 = -0.0006378763202273957 - = 0.011487663648325479 - 0.0012102354919246037 = 0.010277428156400875 - = 0.011487663648325479 - 0.011351854061429653 = 0.00013580958689582596 - total_profit_percentage = (1-(close_value/open_value)) * leverage - (1-(0.012113444660818078 /0.011487663648325479))*3 = -0.16342252828332549 - (1-(0.0012099344410196758/0.011487663648325479))*3 = 2.6840259748040123 - (1-(0.012125539968552874 /0.011487663648325479))*3 = -0.16658121435868578 - (1-(0.0012102354919246037/0.011487663648325479))*3 = 2.68394735544829 - (1-(0.011351854061429653/0.011487663648325479))*3 = 0.03546663387440563 - """ - trade = Trade( - pair='ETH/BTC', - stake_amount=0.0038388182617629, - amount=5, - open_rate=0.00001099, - open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='kraken', - is_short=True, - interest_rate=0.0005, - interest_mode=InterestMode.HOURSPER4 - ) - trade.open_order_id = 'something' - trade.update(market_short_order) # Buy @ 0.00001099 - # Custom closing rate and regular fee rate - - # Higher than open rate - assert trade.calc_profit( - rate=0.00004374, interest_rate=0.0005) == round(-0.0006257810124925996, 8) - assert trade.calc_profit_ratio( - rate=0.00004374, interest_rate=0.0005) == round(-0.16342252828332549, 8) - - # Lower than open rate - trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) - assert trade.calc_profit(rate=0.00000437, interest_rate=0.00025) == round( - 0.010277729207305804, 8) - assert trade.calc_profit_ratio( - rate=0.00000437, interest_rate=0.00025) == round(2.6840259748040123, 8) - - # Custom closing rate and custom fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00004374, fee=0.003, - interest_rate=0.0005) == round(-0.0006378763202273957, 8) - assert trade.calc_profit_ratio(rate=0.00004374, fee=0.003, - interest_rate=0.0005) == round(-0.16658121435868578, 8) - - # Lower than open rate - trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10) - assert trade.calc_profit(rate=0.00000437, fee=0.003, - interest_rate=0.00025) == round(0.010277428156400875, 8) - assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003, - interest_rate=0.00025) == round(2.68394735544829, 8) - - # Test when we apply a exit short order. - trade.update(market_exit_short_order) - assert trade.calc_profit(rate=0.00004099) == round(0.00013580958689582596, 8) - assert trade.calc_profit_ratio() == round(0.03546663387440563, 8) - - # Test with a custom fee rate on the close trade - # assert trade.calc_profit(fee=0.003) == 0.00006163 - # assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 - - -def test_adjust_stop_loss_short(fee): - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='binance', - open_rate=1, - max_rate=1, - is_short=True, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.adjust_stop_loss(trade.open_rate, 0.05, True) - assert trade.stop_loss == 1.05 - assert trade.stop_loss_pct == 0.05 - assert trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - # Get percent of profit with a lower rate - trade.adjust_stop_loss(1.04, 0.05) - assert trade.stop_loss == 1.05 - assert trade.stop_loss_pct == 0.05 - assert trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - # Get percent of profit with a custom rate (Higher than open rate) - trade.adjust_stop_loss(0.7, 0.1) - # If the price goes down to 0.7, with a trailing stop of 0.1, - # the new stoploss at 0.1 above 0.7 would be 0.7*0.1 higher - assert round(trade.stop_loss, 8) == 0.77 - assert trade.stop_loss_pct == 0.1 - assert trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - # current rate lower again ... should not change - trade.adjust_stop_loss(0.8, -0.1) - assert round(trade.stop_loss, 8) == 0.77 - assert trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - # current rate higher... should raise stoploss - trade.adjust_stop_loss(0.6, -0.1) - assert round(trade.stop_loss, 8) == 0.66 - assert trade.initial_stop_loss == 1.05 - 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 trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - assert trade.stop_loss_pct == 0.1 - trade.set_liquidation_price(0.63) - trade.adjust_stop_loss(0.59, -0.1) - assert trade.stop_loss == 0.63 - assert trade.liquidation_price == 0.63 - - # TODO-mg: Do a test with a trade that has a liquidation price - - -@ pytest.mark.usefixtures("init_persistence") -@ pytest.mark.parametrize('use_db', [True, False]) -def test_get_open_short(fee, use_db): - Trade.use_db = use_db - Trade.reset_trades() - create_mock_trades_with_leverage(fee, use_db) - assert len(Trade.get_open_trades()) == 5 - Trade.use_db = True - - -def test_stoploss_reinitialization_short(default_conf, fee): - init_db(default_conf['db_url']) - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - fee_open=fee.return_value, - open_date=arrow.utcnow().shift(hours=-2).datetime, - amount=10, - fee_close=fee.return_value, - exchange='binance', - open_rate=1, - max_rate=1, - is_short=True, - leverage=3.0, - interest_mode=InterestMode.HOURSPERDAY - ) - trade.adjust_stop_loss(trade.open_rate, -0.05, True) - assert trade.stop_loss == 1.05 - assert trade.stop_loss_pct == 0.05 - assert trade.initial_stop_loss == 1.05 - assert trade.initial_stop_loss_pct == 0.05 - Trade.query.session.add(trade) - # Lower stoploss - Trade.stoploss_reinitialization(-0.06) - trades = Trade.get_open_trades() - assert len(trades) == 1 - trade_adj = trades[0] - assert trade_adj.stop_loss == 1.06 - assert trade_adj.stop_loss_pct == 0.06 - assert trade_adj.initial_stop_loss == 1.06 - assert trade_adj.initial_stop_loss_pct == 0.06 - # Raise stoploss - Trade.stoploss_reinitialization(-0.04) - trades = Trade.get_open_trades() - assert len(trades) == 1 - trade_adj = trades[0] - assert trade_adj.stop_loss == 1.04 - assert trade_adj.stop_loss_pct == 0.04 - assert trade_adj.initial_stop_loss == 1.04 - assert trade_adj.initial_stop_loss_pct == 0.04 - # Trailing stoploss - trade.adjust_stop_loss(0.98, -0.04) - assert trade_adj.stop_loss == 1.0192 - assert trade_adj.initial_stop_loss == 1.04 - Trade.stoploss_reinitialization(-0.04) - trades = Trade.get_open_trades() - assert len(trades) == 1 - trade_adj = trades[0] - # Stoploss should not change in this case. - assert trade_adj.stop_loss == 1.0192 - assert trade_adj.stop_loss_pct == 0.04 - 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_liquidation_price(1.0) - trade.adjust_stop_loss(0.97, -0.04) - assert trade_adj.stop_loss == 1.0 - assert trade_adj.stop_loss == 1.0 - - -@ pytest.mark.usefixtures("init_persistence") -@ pytest.mark.parametrize('use_db', [True, False]) -def test_total_open_trades_stakes_short(fee, use_db): - Trade.use_db = use_db - Trade.reset_trades() - res = Trade.total_open_trades_stakes() - assert res == 0 - create_mock_trades_with_leverage(fee, use_db) - res = Trade.total_open_trades_stakes() - assert res == 15.133 - Trade.use_db = True - - -@ pytest.mark.usefixtures("init_persistence") -def test_get_best_pair_short(fee): - res = Trade.get_best_pair() - assert res is None - create_mock_trades_with_leverage(fee) - res = Trade.get_best_pair() - assert len(res) == 2 - assert res[0] == 'DOGE/BTC' - assert res[1] == 0.1713156134055116 diff --git a/tests/persistence/test_persistence.py b/tests/test_persistence.py similarity index 51% rename from tests/persistence/test_persistence.py rename to tests/test_persistence.py index 913a40ca1..7c9df6258 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/test_persistence.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, C0103 import logging from datetime import datetime, timedelta, timezone +from math import isclose from pathlib import Path from types import FunctionType from unittest.mock import MagicMock @@ -10,9 +11,10 @@ import pytest from sqlalchemy import create_engine, inspect, text from freqtrade import constants +from freqtrade.enums import InterestMode 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, log_has, log_has_re +from tests.conftest import create_mock_trades, create_mock_trades_with_leverage, log_has, log_has_re def test_init_create_session(default_conf): @@ -158,7 +160,7 @@ def test_set_stop_loss_liquidation_price(fee): assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.07 - trade.is_short = True + trade.set_is_short(True) trade.stop_loss = None trade.initial_stop_loss = None @@ -189,40 +191,318 @@ def test_set_stop_loss_liquidation_price(fee): @pytest.mark.usefixtures("init_persistence") -def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog): +def test_interest(market_buy_order_usdt, fee): + """ + 10min, 5hr limit trade on Binance/Kraken at 3x,5x leverage + fee: 0.25 % quote + interest_rate: 0.05 % per 4 hrs + open_rate: 2.00 quote + close_rate: 2.20 quote + amount: = 30.0 crypto + stake_amount + 3x, -3x: 20.0 quote + 5x, -5x: 12.0 quote + borrowed + 10min + 3x: 40 quote + -3x: 30 crypto + 5x: 48 quote + -5x: 30 crypto + 1x: 0 + -1x: 30 crypto + hours: 1/6 (10 minutes) + time-periods: + 10min + kraken: (1 + 1) 4hr_periods = 2 4hr_periods + binance: 1/24 24hr_periods + 4.95hr + kraken: ceil(1 + 4.95/4) 4hr_periods = 3 4hr_periods + binance: ceil(4.95)/24 24hr_periods = 5/24 24hr_periods + interest: borrowed * interest_rate * time-periods + 10min + binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote + kraken 3x: 40 * 0.0005 * 2 = 0.040 quote + binace -3x: 30 * 0.0005 * 1/24 = 0.000625 crypto + kraken -3x: 30 * 0.0005 * 2 = 0.030 crypto + 5hr + binance 3x: 40 * 0.0005 * 5/24 = 0.004166666666666667 quote + kraken 3x: 40 * 0.0005 * 3 = 0.06 quote + binace -3x: 30 * 0.0005 * 5/24 = 0.0031249999999999997 crypto + kraken -3x: 30 * 0.0005 * 3 = 0.045 crypto + 0.00025 interest + binance 3x: 40 * 0.00025 * 5/24 = 0.0020833333333333333 quote + kraken 3x: 40 * 0.00025 * 3 = 0.03 quote + binace -3x: 30 * 0.00025 * 5/24 = 0.0015624999999999999 crypto + kraken -3x: 30 * 0.00025 * 3 = 0.0225 crypto + 5x leverage, 0.0005 interest, 5hr + binance 5x: 48 * 0.0005 * 5/24 = 0.005 quote + kraken 5x: 48 * 0.0005 * 3 = 0.07200000000000001 quote + binace -5x: 30 * 0.0005 * 5/24 = 0.0031249999999999997 crypto + kraken -5x: 30 * 0.0005 * 3 = 0.045 crypto + 1x leverage, 0.0005 interest, 5hr + binance,kraken 1x: 0.0 quote + binace -1x: 30 * 0.0005 * 5/24 = 0.003125 crypto + kraken -1x: 30 * 0.0005 * 3 = 0.045 crypto """ - On this test we will buy and sell a crypto currency. - Buy - - Buy: 90.99181073 Crypto at 0.00001099 BTC - (90.99181073*0.00001099 = 0.0009999 BTC) - - Buying fee: 0.25% - - Total cost of buy trade: 0.001002500 BTC - ((90.99181073*0.00001099) + ((90.99181073*0.00001099)*0.0025)) + trade = Trade( + pair='ETH/BTC', + stake_amount=20.0, + amount=30.0, + open_rate=2.0, + open_date=datetime.utcnow() - timedelta(hours=0, minutes=10), + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='kraken', + leverage=3.0, + interest_rate=0.0005, + interest_mode=InterestMode.HOURSPERDAY + ) - Sell - - Sell: 90.99181073 Crypto at 0.00001173 BTC - (90.99181073*0.00001173 = 0,00106733394 BTC) - - Selling fee: 0.25% - - Total cost of sell trade: 0.001064666 BTC - ((90.99181073*0.00001173) - ((90.99181073*0.00001173)*0.0025)) + # 10min, 3x leverage + # binance + assert round(float(trade.calculate_interest()), 8) == round(0.0008333333333333334, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.040 + # Short + trade.set_is_short(True) + # binace + trade.interest_mode = InterestMode.HOURSPERDAY + assert float(trade.calculate_interest()) == 0.000625 + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert isclose(float(trade.calculate_interest()), 0.030) - Profit/Loss: +0.000062166 BTC - (Sell:0.001064666 - Buy:0.001002500) - Profit/Loss percentage: 0.0620 - ((0.001064666/0.001002500)-1 = 6.20%) + # 5hr, long + trade.open_date = datetime.utcnow() - timedelta(hours=4, minutes=55) + trade.set_is_short(False) + # binance + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest()), 8) == round(0.004166666666666667, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.06 + # short + trade.set_is_short(True) + # binace + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest()), 8) == round(0.0031249999999999997, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.045 - :param limit_buy_order: - :param limit_sell_order: - :return: + # 0.00025 interest, 5hr, long + trade.set_is_short(False) + # binance + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest(interest_rate=0.00025)), + 8) == round(0.0020833333333333333, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert isclose(float(trade.calculate_interest(interest_rate=0.00025)), 0.03) + # short + trade.set_is_short(True) + # binace + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest(interest_rate=0.00025)), + 8) == round(0.0015624999999999999, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest(interest_rate=0.00025)) == 0.0225 + + # 5x leverage, 0.0005 interest, 5hr, long + trade.set_is_short(False) + trade.leverage = 5.0 + # binance + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest()), 8) == 0.005 + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == round(0.07200000000000001, 8) + # short + trade.set_is_short(True) + # binace + trade.interest_mode = InterestMode.HOURSPERDAY + assert round(float(trade.calculate_interest()), 8) == round(0.0031249999999999997, 8) + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.045 + + # 1x leverage, 0.0005 interest, 5hr + trade.set_is_short(False) + trade.leverage = 1.0 + # binance + trade.interest_mode = InterestMode.HOURSPERDAY + assert float(trade.calculate_interest()) == 0.0 + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.0 + # short + trade.set_is_short(True) + # binace + trade.interest_mode = InterestMode.HOURSPERDAY + assert float(trade.calculate_interest()) == 0.003125 + # kraken + trade.interest_mode = InterestMode.HOURSPER4 + assert float(trade.calculate_interest()) == 0.045 + + +@pytest.mark.usefixtures("init_persistence") +def test_borrowed(limit_buy_order_usdt, limit_sell_order_usdt, fee, caplog): + """ + 10 minute limit trade on Binance/Kraken at 1x, 3x leverage + fee: 0.25% quote + interest_rate: 0.05% per 4 hrs + open_rate: 2.00 quote + close_rate: 2.20 quote + amount: = 30.0 crypto + stake_amount + 1x,-1x: 60.0 quote + 3x,-3x: 20.0 quote + borrowed + 1x: 0 quote + 3x: 40 quote + -1x: 30 crypto + -3x: 30 crypto + hours: 1/6 (10 minutes) + time-periods: + kraken: (1 + 1) 4hr_periods = 2 4hr_periods + binance: 1/24 24hr_periods + interest: borrowed * interest_rate * time-periods + 1x : / + binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote + kraken 3x: 40 * 0.0005 * 2 = 0.040 quote + binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto + kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto + open_value: (amount * open_rate) ± (amount * open_rate * fee) + 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.850 quote + amount_closed: + 1x, 3x : amount + -1x, -3x : amount + interest + binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto + kraken -1x,-3x: 30 + 0.03 = 30.03 crypto + close_value: + 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest + -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee) + binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + binance 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667 + kraken 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795 + binance -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.16637843750001 + kraken -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025) = 66.231165 + total_profit: + 1x, 3x : close_value - open_value + -1x,-3x: open_value - close_value + binance,kraken 1x: 65.835 - 60.15 = 5.685 + binance 3x: 65.83416667 - 60.15 = 5.684166670000003 + kraken 3x: 65.795 - 60.15 = 5.645 + binance -1x,-3x: 59.850 - 66.16637843750001 = -6.316378437500013 + kraken -1x,-3x: 59.850 - 66.231165 = -6.381165 + total_profit_ratio: + 1x, 3x : ((close_value/open_value) - 1) * leverage + -1x,-3x: (1 - (close_value/open_value)) * leverage + binance 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 + binance 3x: ((65.83416667 / 60.15) - 1) * 3 = 0.2834995845386534 + kraken 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 + kraken 3x: ((65.795 / 60.15) - 1) * 3 = 0.2815461346633419 + binance -1x: (1-(66.1663784375 / 59.85)) * 1 = -0.1055368159983292 + binance -3x: (1-(66.1663784375 / 59.85)) * 3 = -0.3166104479949876 + kraken -1x: (1-(66.2311650 / 59.85)) * 1 = -0.106619298245614 + kraken -3x: (1-(66.2311650 / 59.85)) * 3 = -0.319857894736842 """ trade = Trade( id=2, pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + stake_amount=60.0, + open_rate=2.0, + amount=30.0, + is_open=True, + open_date=arrow.utcnow().datetime, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='binance', + ) + assert trade.borrowed == 0 + trade.set_is_short(True) + assert trade.borrowed == 30.0 + trade.leverage = 3.0 + assert trade.borrowed == 30.0 + trade.set_is_short(False) + assert trade.borrowed == 40.0 + + +@pytest.mark.usefixtures("init_persistence") +def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, caplog): + """ + 10 minute limit trade on Binance/Kraken at 1x, 3x leverage + fee: 0.25% quote + interest_rate: 0.05% per 4 hrs + open_rate: 2.00 quote + close_rate: 2.20 quote + amount: = 30.0 crypto + stake_amount + 1x,-1x: 60.0 quote + 3x,-3x: 20.0 quote + borrowed + 1x: 0 quote + 3x: 40 quote + -1x: 30 crypto + -3x: 30 crypto + hours: 1/6 (10 minutes) + time-periods: + kraken: (1 + 1) 4hr_periods = 2 4hr_periods + binance: 1/24 24hr_periods + interest: borrowed * interest_rate * time-periods + 1x : / + binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote + kraken 3x: 40 * 0.0005 * 2 = 0.040 quote + binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto + kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto + open_value: (amount * open_rate) ± (amount * open_rate * fee) + 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.850 quote + amount_closed: + 1x, 3x : amount + -1x, -3x : amount + interest + binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto + kraken -1x,-3x: 30 + 0.03 = 30.03 crypto + close_value: + 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest + -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee) + binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + binance 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667 + kraken 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795 + binance -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.16637843750001 + kraken -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025) = 66.231165 + total_profit: + 1x, 3x : close_value - open_value + -1x,-3x: open_value - close_value + binance,kraken 1x: 65.835 - 60.15 = 5.685 + binance 3x: 65.83416667 - 60.15 = 5.684166670000003 + kraken 3x: 65.795 - 60.15 = 5.645 + binance -1x,-3x: 59.850 - 66.16637843750001 = -6.316378437500013 + kraken -1x,-3x: 59.850 - 66.231165 = -6.381165 + total_profit_ratio: + 1x, 3x : ((close_value/open_value) - 1) * leverage + -1x,-3x: (1 - (close_value/open_value)) * leverage + binance 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 + binance 3x: ((65.83416667 / 60.15) - 1) * 3 = 0.2834995845386534 + kraken 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 + kraken 3x: ((65.795 / 60.15) - 1) * 3 = 0.2815461346633419 + binance -1x: (1-(66.1663784375 / 59.85)) * 1 = -0.1055368159983292 + binance -3x: (1-(66.1663784375 / 59.85)) * 3 = -0.3166104479949876 + kraken -1x: (1-(66.2311650 / 59.85)) * 1 = -0.106619298245614 + kraken -3x: (1-(66.2311650 / 59.85)) * 3 = -0.319857894736842 + """ + + trade = Trade( + id=2, + pair='ETH/BTC', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, open_date=arrow.utcnow().datetime, fee_open=fee.return_value, @@ -234,35 +514,35 @@ def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog): assert trade.close_date is None trade.open_order_id = 'something' - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) assert trade.open_order_id is None - assert trade.open_rate == 0.00001099 + assert trade.open_rate == 2.00 assert trade.close_profit is None assert trade.close_date is None assert log_has_re(r"LIMIT_BUY has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).", + r"pair=ETH/BTC, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) caplog.clear() trade.open_order_id = 'something' - trade.update(limit_sell_order) + trade.update(limit_sell_order_usdt) assert trade.open_order_id is None - assert trade.close_rate == 0.00001173 - assert trade.close_profit == 0.06201058 + assert trade.close_rate == 2.20 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).", + r"pair=ETH/BTC, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) @pytest.mark.usefixtures("init_persistence") -def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): +def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee, caplog): trade = Trade( id=1, pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.01, + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, fee_open=fee.return_value, fee_close=fee.return_value, @@ -271,73 +551,111 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): ) trade.open_order_id = 'something' - trade.update(market_buy_order) + trade.update(market_buy_order_usdt) assert trade.open_order_id is None - assert trade.open_rate == 0.00004099 + assert trade.open_rate == 2.0 assert trade.close_profit is None assert trade.close_date is None assert log_has_re(r"MARKET_BUY has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).", + r"pair=ETH/BTC, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) caplog.clear() trade.is_open = True trade.open_order_id = 'something' - trade.update(market_sell_order) + trade.update(market_sell_order_usdt) assert trade.open_order_id is None - assert trade.close_rate == 0.00004173 - assert trade.close_profit == 0.01297561 + assert trade.close_rate == 2.2 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).", + r"pair=ETH/BTC, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) @pytest.mark.usefixtures("init_persistence") -def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee): +def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + stake_amount=60.0, + open_rate=2.0, + 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', ) trade.open_order_id = 'something' - trade.update(limit_buy_order) - assert trade._calc_open_trade_value() == 0.0010024999999225068 - - trade.update(limit_sell_order) - assert trade.calc_close_trade_value() == 0.0010646656050132426 - - # Profit in BTC - assert trade.calc_profit() == 0.00006217 - - # Profit in percent - assert trade.calc_profit_ratio() == 0.06201058 + trade.update(limit_buy_order_usdt) + trade.update(limit_sell_order_usdt) + # 1x leverage, binance + assert trade._calc_open_trade_value() == 60.15 + assert isclose(trade.calc_close_trade_value(), 65.835) + assert trade.calc_profit() == 5.685 + assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) + # 3x leverage, binance + trade.leverage = 3 + trade.interest_mode = InterestMode.HOURSPERDAY + 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 + # 3x leverage, kraken + assert trade._calc_open_trade_value() == 60.15 + assert trade.calc_close_trade_value() == 65.795 + assert trade.calc_profit() == 5.645 + assert trade.calc_profit_ratio() == round(0.2815461346633419, 8) + trade.set_is_short(True) + # 3x leverage, short, kraken + assert trade._calc_open_trade_value() == 59.850 + 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 + # 3x leverage, short, binance + assert trade._calc_open_trade_value() == 59.85 + assert trade.calc_close_trade_value() == 66.1663784375 + assert trade.calc_profit() == round(-6.316378437500013, 8) + assert trade.calc_profit_ratio() == round(-0.3166104479949876, 8) + # 1x leverage, short, binance + trade.leverage = 1.0 + assert trade._calc_open_trade_value() == 59.850 + assert trade.calc_close_trade_value() == 66.1663784375 + 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 + assert trade._calc_open_trade_value() == 59.850 + assert trade.calc_close_trade_value() == 66.231165 + assert trade.calc_profit() == -6.381165 + assert trade.calc_profit_ratio() == round(-0.106619298245614, 8) @pytest.mark.usefixtures("init_persistence") -def test_trade_close(limit_buy_order, limit_sell_order, fee): +def test_trade_close(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, fee_open=fee.return_value, fee_close=fee.return_value, - open_date=arrow.Arrow(2020, 2, 1, 15, 5, 1).datetime, + 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 assert trade.close_date is None assert trade.is_open is True - trade.close(0.02) + trade.close(2.2) assert trade.is_open is False - assert trade.close_profit == 0.99002494 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime, @@ -350,29 +668,29 @@ def test_trade_close(limit_buy_order, limit_sell_order, fee): @pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_exception(limit_buy_order, fee): +def test_calc_close_trade_price_exception(limit_buy_order_usdt, fee): trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.1, - amount=5, + stake_amount=60.0, + open_rate=2.0, + amount=30.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'something' - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) assert trade.calc_close_trade_value() == 0.0 @pytest.mark.usefixtures("init_persistence") -def test_update_open_order(limit_buy_order): +def test_update_open_order(limit_buy_order_usdt): trade = Trade( pair='ETH/BTC', - stake_amount=1.00, - open_rate=0.01, - amount=5, + stake_amount=60.0, + open_rate=2.0, + amount=30.0, fee_open=0.1, fee_close=0.1, exchange='binance', @@ -382,8 +700,8 @@ def test_update_open_order(limit_buy_order): assert trade.close_profit is None assert trade.close_date is None - limit_buy_order['status'] = 'open' - trade.update(limit_buy_order) + limit_buy_order_usdt['status'] = 'open' + trade.update(limit_buy_order_usdt) assert trade.open_order_id is None assert trade.close_profit is None @@ -391,130 +709,451 @@ def test_update_open_order(limit_buy_order): @pytest.mark.usefixtures("init_persistence") -def test_update_invalid_order(limit_buy_order): +def test_update_invalid_order(limit_buy_order_usdt): trade = Trade( pair='ETH/BTC', - stake_amount=1.00, - amount=5, - open_rate=0.001, + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=0.1, fee_close=0.1, exchange='binance', ) - limit_buy_order['type'] = 'invalid' + limit_buy_order_usdt['type'] = 'invalid' with pytest.raises(ValueError, match=r'Unknown order type'): - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) @pytest.mark.usefixtures("init_persistence") -def test_calc_open_trade_value(limit_buy_order, fee): +def test_calc_open_trade_value(limit_buy_order_usdt, fee): + # 10 minute limit trade on Binance/Kraken at 1x, 3x leverage + # fee: 0.25 %, 0.3% quote + # open_rate: 2.00 quote + # amount: = 30.0 crypto + # stake_amount + # 1x, -1x: 60.0 quote + # 3x, -3x: 20.0 quote + # open_value: (amount * open_rate) ± (amount * open_rate * fee) + # 0.25% fee + # 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + # -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote + # 0.3% fee + # 1x, 3x: 30 * 2 + 30 * 2 * 0.003 = 60.18 quote + # -1x,-3x: 30 * 2 - 30 * 2 * 0.003 = 59.82 quote trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'open_trade' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Get the open rate price with the standard fee rate - assert trade._calc_open_trade_value() == 0.0010024999999225068 - trade.fee_open = 0.003 + assert trade._calc_open_trade_value() == 60.15 + trade.set_is_short(True) + assert trade._calc_open_trade_value() == 59.85 + trade.leverage = 3 + trade.interest_mode = InterestMode.HOURSPERDAY + assert trade._calc_open_trade_value() == 59.85 + trade.set_is_short(False) + assert trade._calc_open_trade_value() == 60.15 + # Get the open rate price with a custom fee rate - assert trade._calc_open_trade_value() == 0.001002999999922468 + trade.fee_open = 0.003 + + assert trade._calc_open_trade_value() == 60.18 + trade.set_is_short(True) + assert trade._calc_open_trade_value() == 59.82 @pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee): +def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + stake_amount=60.0, + amount=30.0, + open_rate=2.0, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', + interest_rate=0.0005, + interest_mode=InterestMode.HOURSPERDAY ) trade.open_order_id = 'close_trade' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) - # Get the close rate price with a custom close rate and a regular fee rate - assert trade.calc_close_trade_value(rate=0.00001234) == 0.0011200318470471794 + # 1x leverage binance + assert trade.calc_close_trade_value(rate=2.5) == 74.8125 + assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 74.775 + trade.update(limit_sell_order_usdt) + assert trade.calc_close_trade_value(fee=0.005) == 65.67 - # Get the close rate price with a custom close rate and a custom fee rate - assert trade.calc_close_trade_value(rate=0.00001234, fee=0.003) == 0.0011194704275749754 + # 3x leverage binance + trade.leverage = 3.0 + assert round(trade.calc_close_trade_value(rate=2.5), 8) == 74.81166667 + assert round(trade.calc_close_trade_value(rate=2.5, fee=0.003), 8) == 74.77416667 - # Test when we apply a Sell order, and ask price with a custom fee rate - trade.update(limit_sell_order) - assert trade.calc_close_trade_value(fee=0.005) == 0.0010619972701635854 + # 3x leverage kraken + trade.interest_mode = InterestMode.HOURSPER4 + 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 + + # 3x leverage kraken, short + trade.set_is_short(True) + 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 + + # 3x leverage binance, short + trade.interest_mode = InterestMode.HOURSPERDAY + 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 + + trade.leverage = 1.0 + # 1x leverage binance, short + 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 + + # 1x leverage kraken, short + trade.interest_mode = InterestMode.HOURSPER4 + 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 @pytest.mark.usefixtures("init_persistence") -def test_calc_profit(limit_buy_order, limit_sell_order, fee): +def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): + """ + 10 minute limit trade on Binance/Kraken at 1x, 3x leverage + arguments: + fee: + 0.25% quote + 0.30% quote + interest_rate: 0.05% per 4 hrs + open_rate: 2.0 quote + close_rate: + 1.9 quote + 2.1 quote + 2.2 quote + amount: = 30.0 crypto + stake_amount + 1x,-1x: 60.0 quote + 3x,-3x: 20.0 quote + hours: 1/6 (10 minutes) + borrowed + 1x: 0 quote + 3x: 40 quote + -1x: 30 crypto + -3x: 30 crypto + time-periods: + kraken: (1 + 1) 4hr_periods = 2 4hr_periods + binance: 1/24 24hr_periods + interest: borrowed * interest_rate * time-periods + 1x : / + binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote + kraken 3x: 40 * 0.0005 * 2 = 0.040 quote + binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto + kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto + open_value: (amount * open_rate) ± (amount * open_rate * fee) + 0.0025 fee + 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote + 0.003 fee: Is only applied to close rate in this test + amount_closed: + 1x, 3x = amount + -1x, -3x = amount + interest + binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto + kraken -1x,-3x: 30 + 0.03 = 30.03 crypto + close_value: + equations: + 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest + -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee) + 2.1 quote + bin,krak 1x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) = 62.8425 + bin 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.0008333333 = 62.8416666667 + krak 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.040 = 62.8025 + bin -1x,-3x: (30.000625 * 2.1) + (30.000625 * 2.1 * 0.0025) = 63.15881578125 + krak -1x,-3x: (30.03 * 2.1) + (30.03 * 2.1 * 0.0025) = 63.2206575 + 1.9 quote + bin,krak 1x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) = 56.8575 + bin 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.0008333333 = 56.85666667 + krak 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.040 = 56.8175 + bin -1x,-3x: (30.000625 * 1.9) + (30.000625 * 1.9 * 0.0025) = 57.14369046875 + krak -1x,-3x: (30.03 * 1.9) + (30.03 * 1.9 * 0.0025) = 57.1996425 + 2.2 quote + bin,krak 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + bin 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667 + krak 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795 + bin -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.1663784375 + krak -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025) = 66.231165 + total_profit: + equations: + 1x, 3x : close_value - open_value + -1x,-3x: open_value - close_value + 2.1 quote + binance,kraken 1x: 62.8425 - 60.15 = 2.6925 + binance 3x: 62.84166667 - 60.15 = 2.69166667 + kraken 3x: 62.8025 - 60.15 = 2.6525 + binance -1x,-3x: 59.850 - 63.15881578125 = -3.308815781249997 + kraken -1x,-3x: 59.850 - 63.2206575 = -3.3706575 + 1.9 quote + binance,kraken 1x: 56.8575 - 60.15 = -3.2925 + binance 3x: 56.85666667 - 60.15 = -3.29333333 + kraken 3x: 56.8175 - 60.15 = -3.3325 + binance -1x,-3x: 59.850 - 57.14369046875 = 2.7063095312499996 + kraken -1x,-3x: 59.850 - 57.1996425 = 2.6503575 + 2.2 quote + binance,kraken 1x: 65.835 - 60.15 = 5.685 + binance 3x: 65.83416667 - 60.15 = 5.68416667 + kraken 3x: 65.795 - 60.15 = 5.645 + binance -1x,-3x: 59.850 - 66.1663784375 = -6.316378437499999 + kraken -1x,-3x: 59.850 - 66.231165 = -6.381165 + total_profit_ratio: + equations: + 1x, 3x : ((close_value/open_value) - 1) * leverage + -1x,-3x: (1 - (close_value/open_value)) * leverage + 2.1 quote + binance,kraken 1x: (62.8425 / 60.15) - 1 = 0.04476309226932673 + binance 3x: ((62.84166667 / 60.15) - 1)*3 = 0.13424771421446402 + kraken 3x: ((62.8025 / 60.15) - 1)*3 = 0.13229426433915248 + binance -1x: 1 - (63.15881578125 / 59.850) = -0.05528514254385963 + binance -3x: (1 - (63.15881578125 / 59.850))*3 = -0.1658554276315789 + kraken -1x: 1 - (63.2206575 / 59.850) = -0.05631842105263152 + kraken -3x: (1 - (63.2206575 / 59.850))*3 = -0.16895526315789455 + 1.9 quote + binance,kraken 1x: (56.8575 / 60.15) - 1 = -0.05473815461346632 + binance 3x: ((56.85666667 / 60.15) - 1)*3 = -0.16425602643391513 + kraken 3x: ((56.8175 / 60.15) - 1)*3 = -0.16620947630922667 + binance -1x: 1 - (57.14369046875 / 59.850) = 0.045218204365079395 + binance -3x: (1 - (57.14369046875 / 59.850))*3 = 0.13565461309523819 + kraken -1x: 1 - (57.1996425 / 59.850) = 0.04428333333333334 + kraken -3x: (1 - (57.1996425 / 59.850))*3 = 0.13285000000000002 + 2.2 quote + binance,kraken 1x: (65.835 / 60.15) - 1 = 0.0945137157107232 + binance 3x: ((65.83416667 / 60.15) - 1)*3 = 0.2834995845386534 + kraken 3x: ((65.795 / 60.15) - 1)*3 = 0.2815461346633419 + binance -1x: 1 - (66.1663784375 / 59.850) = -0.1055368159983292 + binance -3x: (1 - (66.1663784375 / 59.850))*3 = -0.3166104479949876 + kraken -1x: 1 - (66.231165 / 59.850) = -0.106619298245614 + kraken -3x: (1 - (66.231165 / 59.850))*3 = -0.319857894736842 + fee: 0.003, 1x + close_value: + 2.1 quote: (30.00 * 2.1) - (30.00 * 2.1 * 0.003) = 62.811 + 1.9 quote: (30.00 * 1.9) - (30.00 * 1.9 * 0.003) = 56.829 + 2.2 quote: (30.00 * 2.2) - (30.00 * 2.2 * 0.003) = 65.802 + total_profit + fee: 0.003, 1x + 2.1 quote: 62.811 - 60.15 = 2.6610000000000014 + 1.9 quote: 56.829 - 60.15 = -3.320999999999998 + 2.2 quote: 65.802 - 60.15 = 5.652000000000008 + total_profit_ratio + fee: 0.003, 1x + 2.1 quote: (62.811 / 60.15) - 1 = 0.04423940149625927 + 1.9 quote: (56.829 / 60.15) - 1 = -0.05521197007481293 + 2.2 quote: (65.802 / 60.15) - 1 = 0.09396508728179565 + """ trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + stake_amount=60.0, + amount=30.0, + 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', + exchange='binance' ) trade.open_order_id = 'something' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 + # 1x Leverage, long # Custom closing rate and regular fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00001234) == 0.00011753 - # Lower than open rate - assert trade.calc_profit(rate=0.00000123) == -0.00089086 + # Higher than open rate - 2.1 quote + assert trade.calc_profit(rate=2.1) == 2.6925 + # Lower than open rate - 1.9 quote + assert trade.calc_profit(rate=1.9) == round(-3.292499999999997, 8) - # Custom closing rate and custom fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00001234, fee=0.003) == 0.00011697 - # Lower than open rate - assert trade.calc_profit(rate=0.00000123, fee=0.003) == -0.00089092 + # fee 0.003 + # Higher than open rate - 2.1 quote + assert trade.calc_profit(rate=2.1, fee=0.003) == 2.661 + # Lower than open rate - 1.9 quote + assert trade.calc_profit(rate=1.9, fee=0.003) == round(-3.320999999999998, 8) - # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 - trade.update(limit_sell_order) - assert trade.calc_profit() == 0.00006217 + # Test when we apply a Sell order. Sell higher than open rate @ 2.2 + trade.update(limit_sell_order_usdt) + assert trade.calc_profit() == round(5.684999999999995, 8) # Test with a custom fee rate on the close trade - assert trade.calc_profit(fee=0.003) == 0.00006163 + assert trade.calc_profit(fee=0.003) == round(5.652000000000008, 8) + + trade.open_trade_value = 0.0 + trade.open_trade_value = trade._calc_open_trade_value() + + # 3x leverage, long ################################################### + trade.leverage = 3.0 + # Higher than open rate - 2.1 quote + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit(rate=2.1, fee=0.0025) == 2.69166667 + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit(rate=2.1, fee=0.0025) == 2.6525 + + # 1.9 quote + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit(rate=1.9, fee=0.0025) == -3.29333333 + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit(rate=1.9, fee=0.0025) == -3.3325 + + # 2.2 quote + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit(fee=0.0025) == 5.68416667 + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit(fee=0.0025) == 5.645 + + # 3x leverage, short ################################################### + trade.set_is_short(True) + # 2.1 quote - Higher than open rate + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit(rate=2.1, fee=0.0025) == round(-3.308815781249997, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit(rate=1.9, fee=0.0025) == round(2.7063095312499996, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit(fee=0.0025) == round(-6.316378437499999, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit(rate=2.1, fee=0.0025) == round(-3.308815781249997, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit(rate=1.9, fee=0.0025) == round(2.7063095312499996, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit(fee=0.0025) == round(-6.316378437499999, 8) + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit(fee=0.0025) == -6.381165 @pytest.mark.usefixtures("init_persistence") -def test_calc_profit_ratio(limit_buy_order, limit_sell_order, fee): +def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + stake_amount=60.0, + amount=30.0, + 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', + exchange='binance' ) trade.open_order_id = 'something' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 - # Get percent of profit with a custom rate (Higher than open rate) - assert trade.calc_profit_ratio(rate=0.00001234) == 0.11723875 + # 1x Leverage, long + # Custom closing rate and regular fee rate + # Higher than open rate - 2.1 quote + assert trade.calc_profit_ratio(rate=2.1) == round(0.04476309226932673, 8) + # Lower than open rate - 1.9 quote + assert trade.calc_profit_ratio(rate=1.9) == round(-0.05473815461346632, 8) - # Get percent of profit with a custom rate (Lower than open rate) - assert trade.calc_profit_ratio(rate=0.00000123) == -0.88863828 + # fee 0.003 + # Higher than open rate - 2.1 quote + assert trade.calc_profit_ratio(rate=2.1, fee=0.003) == round(0.04423940149625927, 8) + # Lower than open rate - 1.9 quote + assert trade.calc_profit_ratio(rate=1.9, fee=0.003) == round(-0.05521197007481293, 8) - # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 - trade.update(limit_sell_order) - assert trade.calc_profit_ratio() == 0.06201058 + # Test when we apply a Sell order. Sell higher than open rate @ 2.2 + trade.update(limit_sell_order_usdt) + assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) # Test with a custom fee rate on the close trade - assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 + assert trade.calc_profit_ratio(fee=0.003) == round(0.09396508728179565, 8) trade.open_trade_value = 0.0 assert trade.calc_profit_ratio(fee=0.003) == 0.0 + trade.open_trade_value = trade._calc_open_trade_value() + + # 3x leverage, long ################################################### + trade.leverage = 3.0 + # 2.1 quote - Higher than open rate + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit_ratio(rate=2.1) == round(0.13424771421446402, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio(rate=1.9) == round(-0.16425602643391513, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio() == round(0.2834995845386534, 8) + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit_ratio() == round(0.2815461346633419, 8) + + # 3x leverage, short ################################################### + trade.set_is_short(True) + # 2.1 quote - Higher than open rate + trade.interest_mode = InterestMode.HOURSPERDAY # binance + assert trade.calc_profit_ratio(rate=2.1) == round(-0.1658554276315789, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio(rate=1.9) == round(0.13565461309523819, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio() == round(-0.3166104479949876, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio(rate=2.1) == round(-0.05528514254385963, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio(rate=1.9) == round(0.045218204365079395, 8) + trade.interest_mode = InterestMode.HOURSPER4 # 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 + assert trade.calc_profit_ratio() == round(-0.1055368159983292, 8) + trade.interest_mode = InterestMode.HOURSPER4 # kraken + assert trade.calc_profit_ratio() == round(-0.106619298245614, 8) @pytest.mark.usefixtures("init_persistence") @@ -812,7 +1451,6 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert orders[1].order_id == 'stop_order_id222' assert orders[1].ft_order_side == 'stoploss' - # assert orders[0].is_short is False def test_migrate_mid_state(mocker, default_conf, fee, caplog): @@ -930,6 +1568,60 @@ def test_adjust_stop_loss(fee): assert trade.stop_loss_pct == -0.1 +def test_adjust_stop_loss_short(fee): + trade = Trade( + pair='ETH/BTC', + stake_amount=0.001, + amount=5, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='binance', + open_rate=1, + max_rate=1, + is_short=True, + interest_mode=InterestMode.HOURSPERDAY + ) + trade.adjust_stop_loss(trade.open_rate, 0.05, True) + assert trade.stop_loss == 1.05 + assert trade.stop_loss_pct == 0.05 + assert trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + # Get percent of profit with a lower rate + trade.adjust_stop_loss(1.04, 0.05) + assert trade.stop_loss == 1.05 + assert trade.stop_loss_pct == 0.05 + assert trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + # Get percent of profit with a custom rate (Higher than open rate) + trade.adjust_stop_loss(0.7, 0.1) + # If the price goes down to 0.7, with a trailing stop of 0.1, + # the new stoploss at 0.1 above 0.7 would be 0.7*0.1 higher + assert round(trade.stop_loss, 8) == 0.77 + assert trade.stop_loss_pct == 0.1 + assert trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + # current rate lower again ... should not change + trade.adjust_stop_loss(0.8, -0.1) + assert round(trade.stop_loss, 8) == 0.77 + assert trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + # current rate higher... should raise stoploss + trade.adjust_stop_loss(0.6, -0.1) + assert round(trade.stop_loss, 8) == 0.66 + assert trade.initial_stop_loss == 1.05 + 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 trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + assert trade.stop_loss_pct == 0.1 + trade.set_liquidation_price(0.63) + trade.adjust_stop_loss(0.59, -0.1) + assert trade.stop_loss == 0.63 + assert trade.liquidation_price == 0.63 + + def test_adjust_min_max_rates(fee): trade = Trade( pair='ETH/BTC', @@ -973,6 +1665,18 @@ def test_get_open(fee, use_db): Trade.use_db = True +@pytest.mark.usefixtures("init_persistence") +@pytest.mark.parametrize('use_db', [True, False]) +def test_get_open_lev(fee, use_db): + Trade.use_db = use_db + Trade.reset_trades() + + create_mock_trades_with_leverage(fee, use_db) + assert len(Trade.get_open_trades()) == 5 + + Trade.use_db = True + + @pytest.mark.usefixtures("init_persistence") def test_to_json(default_conf, fee): @@ -1174,6 +1878,66 @@ def test_stoploss_reinitialization(default_conf, fee): assert trade_adj.initial_stop_loss_pct == -0.04 +def test_stoploss_reinitialization_short(default_conf, fee): + init_db(default_conf['db_url']) + trade = Trade( + pair='ETH/BTC', + stake_amount=0.001, + fee_open=fee.return_value, + open_date=arrow.utcnow().shift(hours=-2).datetime, + amount=10, + fee_close=fee.return_value, + exchange='binance', + open_rate=1, + max_rate=1, + is_short=True, + leverage=3.0, + interest_mode=InterestMode.HOURSPERDAY + ) + trade.adjust_stop_loss(trade.open_rate, -0.05, True) + assert trade.stop_loss == 1.05 + assert trade.stop_loss_pct == 0.05 + assert trade.initial_stop_loss == 1.05 + assert trade.initial_stop_loss_pct == 0.05 + Trade.query.session.add(trade) + # Lower stoploss + Trade.stoploss_reinitialization(-0.06) + trades = Trade.get_open_trades() + assert len(trades) == 1 + trade_adj = trades[0] + assert trade_adj.stop_loss == 1.06 + assert trade_adj.stop_loss_pct == 0.06 + assert trade_adj.initial_stop_loss == 1.06 + assert trade_adj.initial_stop_loss_pct == 0.06 + # Raise stoploss + Trade.stoploss_reinitialization(-0.04) + trades = Trade.get_open_trades() + assert len(trades) == 1 + trade_adj = trades[0] + assert trade_adj.stop_loss == 1.04 + assert trade_adj.stop_loss_pct == 0.04 + assert trade_adj.initial_stop_loss == 1.04 + assert trade_adj.initial_stop_loss_pct == 0.04 + # Trailing stoploss + trade.adjust_stop_loss(0.98, -0.04) + assert trade_adj.stop_loss == 1.0192 + assert trade_adj.initial_stop_loss == 1.04 + Trade.stoploss_reinitialization(-0.04) + trades = Trade.get_open_trades() + assert len(trades) == 1 + trade_adj = trades[0] + # Stoploss should not change in this case. + assert trade_adj.stop_loss == 1.0192 + assert trade_adj.stop_loss_pct == 0.04 + 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_liquidation_price(1.0) + trade.adjust_stop_loss(0.97, -0.04) + assert trade_adj.stop_loss == 1.0 + assert trade_adj.stop_loss == 1.0 + + def test_update_fee(fee): trade = Trade( pair='ETH/BTC', @@ -1331,6 +2095,19 @@ def test_get_best_pair(fee): assert res[1] == 0.01 +@pytest.mark.usefixtures("init_persistence") +def test_get_best_pair_lev(fee): + + res = Trade.get_best_pair() + assert res is None + + create_mock_trades_with_leverage(fee) + res = Trade.get_best_pair() + assert len(res) == 2 + assert res[0] == 'DOGE/BTC' + assert res[1] == 0.1713156134055116 + + @pytest.mark.usefixtures("init_persistence") def test_update_order_from_ccxt(caplog): # Most basic order return (only has orderid)