Wrote all tests for shorting

This commit is contained in:
Sam Germain 2021-07-02 02:48:30 -06:00
parent f194673001
commit e4d4d1d1f1
5 changed files with 852 additions and 134 deletions

View File

@ -132,9 +132,6 @@ class Order(_DECL_BASE):
order_filled_date = Column(DateTime, nullable=True) order_filled_date = Column(DateTime, nullable=True)
order_update_date = Column(DateTime, nullable=True) order_update_date = Column(DateTime, nullable=True)
leverage = Column(Float, nullable=True, default=None)
is_short = Column(Boolean, nullable=True, default=False)
def __repr__(self): def __repr__(self):
return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, ' return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, '
f'side={self.side}, order_type={self.order_type}, status={self.status})') f'side={self.side}, order_type={self.order_type}, status={self.status})')

View File

@ -204,11 +204,34 @@ def create_mock_trades(fee, use_db: bool = True):
add_trade(trade) add_trade(trade)
trade = mock_trade_6(fee) trade = mock_trade_6(fee)
add_trade(trade) add_trade(trade)
# TODO: margin trades
# trade = short_trade(fee)
# add_trade(trade) def create_mock_trades_with_leverage(fee, use_db: bool = True):
# trade = leverage_trade(fee) """
# add_trade(trade) Create some fake trades ...
"""
def add_trade(trade):
if use_db:
Trade.query.session.add(trade)
else:
LocalTrade.add_bt_trade(trade)
# 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)
trade = short_trade(fee)
add_trade(trade)
trade = leverage_trade(fee)
add_trade(trade)
if use_db: if use_db:
Trade.query.session.flush() Trade.query.session.flush()
@ -2094,7 +2117,7 @@ def limit_short_order_open():
'cost': 0.00106733393, 'cost': 0.00106733393,
'remaining': 90.99181073, 'remaining': 90.99181073,
'status': 'open', 'status': 'open',
'is_short': True 'exchange': 'binance'
} }
@ -2111,7 +2134,8 @@ def limit_exit_short_order_open():
'amount': 90.99181073, 'amount': 90.99181073,
'filled': 0.0, 'filled': 0.0,
'remaining': 90.99181073, 'remaining': 90.99181073,
'status': 'open' 'status': 'open',
'exchange': 'binance'
} }
@ -2147,7 +2171,8 @@ def market_short_order():
'remaining': 0.0, 'remaining': 0.0,
'status': 'closed', 'status': 'closed',
'is_short': True, 'is_short': True,
'leverage': 3.0 # 'leverage': 3.0,
'exchange': 'kraken'
} }
@ -2164,5 +2189,96 @@ def market_exit_short_order():
'filled': 91.99181073, 'filled': 91.99181073,
'remaining': 0.0, 'remaining': 0.0,
'status': 'closed', 'status': 'closed',
'leverage': 3.0 # 'leverage': 3.0,
'exchange': 'kraken'
}
# leverage 3x
@pytest.fixture(scope='function')
def limit_leveraged_buy_order_open():
return {
'id': 'mocked_limit_buy',
'type': 'limit',
'side': 'buy',
'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().int_timestamp,
'price': 0.00001099,
'amount': 272.97543219,
'filled': 0.0,
'cost': 0.0029999999997681,
'remaining': 272.97543219,
'status': 'open',
'exchange': 'binance'
}
@pytest.fixture(scope='function')
def limit_leveraged_buy_order(limit_leveraged_buy_order_open):
order = deepcopy(limit_leveraged_buy_order_open)
order['status'] = 'closed'
order['filled'] = order['amount']
order['remaining'] = 0.0
return order
@pytest.fixture
def limit_leveraged_sell_order_open():
return {
'id': 'mocked_limit_sell',
'type': 'limit',
'side': 'sell',
'pair': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().int_timestamp,
'price': 0.00001173,
'amount': 272.97543219,
'filled': 0.0,
'remaining': 272.97543219,
'status': 'open',
'exchange': 'binance'
}
@pytest.fixture
def limit_leveraged_sell_order(limit_leveraged_sell_order_open):
order = deepcopy(limit_leveraged_sell_order_open)
order['remaining'] = 0.0
order['filled'] = order['amount']
order['status'] = 'closed'
return order
@pytest.fixture(scope='function')
def market_leveraged_buy_order():
return {
'id': 'mocked_market_buy',
'type': 'market',
'side': 'buy',
'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00004099,
'amount': 275.97543219,
'filled': 275.97543219,
'remaining': 0.0,
'status': 'closed',
'exchange': 'kraken'
}
@pytest.fixture
def market_leveraged_sell_order():
return {
'id': 'mocked_limit_sell',
'type': 'market',
'side': 'sell',
'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00004173,
'amount': 275.97543219,
'filled': 275.97543219,
'remaining': 0.0,
'status': 'closed',
'exchange': 'kraken'
} }

View File

@ -310,7 +310,7 @@ def mock_trade_6(fee):
def short_order(): def short_order():
return { return {
'id': '1235', 'id': '1236',
'symbol': 'ETC/BTC', 'symbol': 'ETC/BTC',
'status': 'closed', 'status': 'closed',
'side': 'sell', 'side': 'sell',
@ -319,14 +319,12 @@ def short_order():
'amount': 123.0, 'amount': 123.0,
'filled': 123.0, 'filled': 123.0,
'remaining': 0.0, 'remaining': 0.0,
'leverage': 5.0,
'isShort': True
} }
def exit_short_order(): def exit_short_order():
return { return {
'id': '12366', 'id': '12367',
'symbol': 'ETC/BTC', 'symbol': 'ETC/BTC',
'status': 'closed', 'status': 'closed',
'side': 'buy', 'side': 'buy',
@ -335,36 +333,60 @@ def exit_short_order():
'amount': 123.0, 'amount': 123.0,
'filled': 123.0, 'filled': 123.0,
'remaining': 0.0, 'remaining': 0.0,
'leverage': 5.0,
'isShort': True
} }
def short_trade(fee): def short_trade(fee):
""" """
Closed trade... 10 minute short limit trade on binance
Short trade
fee: 0.25% base
interest_rate: 0.05% per day
open_rate: 0.123 base
close_rate: 0.128 base
amount: 123.0 crypto
stake_amount: 15.129 base
borrowed: 123.0 crypto
time-periods: 10 minutes(rounds up to 1/24 time-period of 1 day)
interest: borrowed * interest_rate * time-periods
= 123.0 * 0.0005 * 1/24 = 0.0025625 crypto
open_value: (amount * open_rate) - (amount * open_rate * fee)
= (123 * 0.123) - (123 * 0.123 * 0.0025)
= 15.091177499999999
amount_closed: amount + interest = 123 + 0.0025625 = 123.0025625
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (123.0025625 * 0.128) + (123.0025625 * 0.128 * 0.0025)
= 15.78368882
total_profit = open_value - close_value
= 15.091177499999999 - 15.78368882
= -0.6925113200000013
total_profit_percentage = total_profit / stake_amount
= -0.6925113200000013 / 15.129
= -0.04577376693766946
""" """
trade = Trade( trade = Trade(
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=15.129,
amount=123.0, amount=123.0,
amount_requested=123.0, amount_requested=123.0,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
open_rate=0.123, open_rate=0.123,
close_rate=0.128, # close_rate=0.128,
close_profit=0.025, # close_profit=-0.04577376693766946,
close_profit_abs=0.000584127, # close_profit_abs=-0.6925113200000013,
exchange='binance', exchange='binance',
is_open=False, is_open=True,
open_order_id='dry_run_exit_short_12345', open_order_id='dry_run_exit_short_12345',
strategy='DefaultStrategy', strategy='DefaultStrategy',
timeframe=5, timeframe=5,
sell_reason='sell_signal', # TODO-mg: Update to exit/close reason sell_reason='sell_signal', # TODO-mg: Update to exit/close reason
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), # close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
# borrowed= # borrowed=
isShort=True is_short=True
) )
o = Order.parse_from_ccxt_object(short_order(), 'ETC/BTC', 'sell') o = Order.parse_from_ccxt_object(short_order(), 'ETC/BTC', 'sell')
trade.orders.append(o) trade.orders.append(o)
@ -375,7 +397,7 @@ def short_trade(fee):
def leverage_order(): def leverage_order():
return { return {
'id': '1235', 'id': '1237',
'symbol': 'ETC/BTC', 'symbol': 'ETC/BTC',
'status': 'closed', 'status': 'closed',
'side': 'buy', 'side': 'buy',
@ -390,7 +412,7 @@ def leverage_order():
def leverage_order_sell(): def leverage_order_sell():
return { return {
'id': '12366', 'id': '12368',
'symbol': 'ETC/BTC', 'symbol': 'ETC/BTC',
'status': 'closed', 'status': 'closed',
'side': 'sell', 'side': 'sell',
@ -399,34 +421,60 @@ def leverage_order_sell():
'amount': 123.0, 'amount': 123.0,
'filled': 123.0, 'filled': 123.0,
'remaining': 0.0, 'remaining': 0.0,
'leverage': 5.0,
'isShort': True
} }
def leverage_trade(fee): def leverage_trade(fee):
""" """
Closed trade... 5 hour short limit trade on kraken
Short trade
fee: 0.25% base
interest_rate: 0.05% per day
open_rate: 0.123 base
close_rate: 0.128 base
amount: 123.0 crypto
amount_with_leverage: 615.0
stake_amount: 15.129 base
borrowed: 60.516 base
leverage: 5
time-periods: 5 hrs( 5/4 time-period of 4 hours)
interest: borrowed * interest_rate * time-periods
= 60.516 * 0.0005 * 1/24 = 0.0378225 base
open_value: (amount * open_rate) - (amount * open_rate * fee)
= (615.0 * 0.123) - (615.0 * 0.123 * 0.0025)
= 75.4558875
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (615.0 * 0.128) + (615.0 * 0.128 * 0.0025)
= 78.9168
total_profit = close_value - open_value - interest
= 78.9168 - 75.4558875 - 0.0378225
= 3.423089999999992
total_profit_percentage = total_profit / stake_amount
= 3.423089999999992 / 15.129
= 0.22626016260162551
""" """
trade = Trade( trade = Trade(
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=15.129,
amount=615.0, amount=123.0,
amount_requested=615.0, leverage=5,
amount_requested=123.0,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
open_rate=0.123, open_rate=0.123,
close_rate=0.128, close_rate=0.128,
close_profit=0.005, # TODO-mg: Would this be -0.005 or -0.025 close_profit=0.22626016260162551,
close_profit_abs=0.000584127, close_profit_abs=3.423089999999992,
exchange='binance', exchange='kraken',
is_open=False, is_open=False,
open_order_id='dry_run_leverage_sell_12345', open_order_id='dry_run_leverage_sell_12345',
strategy='DefaultStrategy', strategy='DefaultStrategy',
timeframe=5, timeframe=5,
sell_reason='sell_signal', # TODO-mg: Update to exit/close reason sell_reason='sell_signal', # TODO-mg: Update to exit/close reason
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300),
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), close_date=datetime.now(tz=timezone.utc),
# borrowed= # borrowed=
) )
o = Order.parse_from_ccxt_object(leverage_order(), 'ETC/BTC', 'sell') o = Order.parse_from_ccxt_object(leverage_order(), 'ETC/BTC', 'sell')

View File

@ -0,0 +1,616 @@
import logging
from datetime import datetime, timedelta, timezone
from pathlib import Path
from types import FunctionType
from unittest.mock import MagicMock
import arrow
import pytest
from math import isclose
from sqlalchemy import create_engine, inspect, text
from freqtrade import constants
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_with_leverage, log_has, log_has_re
@pytest.mark.usefixtures("init_persistence")
def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, ten_minutes_ago, 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
close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee)
= (272.97543219 * 0.00001173) - (272.97543219 * 0.00001173 * 0.0025)
= 0.003193996815039728
total_profit = close_value - open_value - interest
= 0.003193996815039728 - 0.0030074999997675204 - 4.166666666344583e-08
= 0.00018645514860554435
total_profit_percentage = total_profit / stake_amount
= 0.00018645514860554435 / 0.0009999999999226999
= 0.18645514861995735
"""
trade = Trade(
id=2,
pair='ETH/BTC',
stake_amount=0.0009999999999226999,
open_rate=0.01,
amount=5,
is_open=True,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
# borrowed=90.99181073,
interest_rate=0.0005,
exchange='binance'
)
# assert trade.open_order_id is None
assert trade.close_profit is None
assert trade.close_date is None
assert trade.borrowed is None
assert trade.is_short is None
# trade.open_order_id = 'something'
trade.update(limit_leveraged_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 trade.is_short is True
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_leveraged_sell_order)
# assert trade.open_order_id is None
assert trade.close_rate == 0.00001173
assert trade.close_profit == 0.18645514861995735
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(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, ten_minutes_ago, 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: 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 = 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)
= (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.0025)
= 0.011487663648325479
total_profit = close_value - open_value - interest
= 0.011487663648325479 - 0.01134051354788177 - 3.7707443218227e-06
= 0.0001433793561218866
total_profit_percentage = total_profit / stake_amount
= 0.0001433793561218866 / 0.0037707443218227
= 0.03802415223225211
"""
trade = Trade(
id=1,
pair='ETH/BTC',
stake_amount=0.0037707443218227,
amount=5,
open_rate=0.01,
is_open=True,
leverage=3,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=ten_minutes_ago,
interest_rate=0.0005,
exchange='kraken'
)
trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
assert trade.leverage == 3.0
assert trade.is_short == True
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(limit_leveraged_sell_order)
assert trade.open_order_id is None
assert trade.close_rate == 0.00004173
assert trade.close_profit == 0.03802415223225211
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_open_close_trade_price(limit_leveraged_buy_order, limit_leveraged_sell_order, five_hours_ago, 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)
= (272.97543219 * 0.00001173) - (272.97543219 * 0.00001173 * 0.0025)
= 0.003193996815039728
total_profit = close_value - open_value - interest
= 0.003193996815039728 - 0.0030074999997675204 - 2.0833333331722917e-07
= 0.00018628848193889054
total_profit_percentage = total_profit / stake_amount
= 0.00018628848193889054 / 0.0009999999999226999
= 0.18628848195329067
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0009999999999226999,
open_rate=0.01,
amount=5,
open_date=five_hours_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
interest_rate=0.0005
)
trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
assert trade._calc_open_trade_value() == 0.0030074999997675204
trade.update(limit_leveraged_sell_order)
# Will be slightly different due to slight changes in compilation time, and the fact that interest depends on time
assert round(trade.calc_close_trade_value(), 11) == round(0.003193996815039728, 11)
# Profit in BTC
assert round(trade.calc_profit(), 8) == round(0.18628848195329067, 8)
# Profit in percent
# assert round(trade.calc_profit_ratio(), 11) == round(0.05822425142973869, 11)
@pytest.mark.usefixtures("init_persistence")
def test_trade_close(fee, five_hours_ago):
"""
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 * time-periods
= 1 * 0.0005 * 5/4 = 0.000625 crypto
open_value: (amount * open_rate) + (amount * open_rate * fee)
= (15 * 0.1) + (15 * 0.1 * 0.0025)
= 1.50375
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (15 * 0.2) - (15 * 0.2 * 0.0025)
= 2.9925
total_profit = close_value - open_value - interest
= 2.9925 - 1.50375 - 0.000625
= 1.4881250000000001
total_profit_percentage = total_profit / stake_amount
= 1.4881250000000001 / 0.5
= 2.9762500000000003
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.1,
open_rate=0.01,
amount=5,
is_open=True,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=five_hours_ago,
exchange='kraken',
leverage=3.0,
interest_rate=0.0005
)
assert trade.close_profit is None
assert trade.close_date is None
assert trade.is_open is True
trade.close(0.02)
assert trade.is_open is False
assert trade.close_profit == round(2.9762500000000003, 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_exception(limit_leveraged_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,
borrowed=0.002
)
trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
assert trade.calc_close_trade_value() == 0.0
@pytest.mark.usefixtures("init_persistence")
def test_update_open_order(limit_leveraged_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,
borrowed=2.00,
exchange='binance',
)
assert trade.open_order_id is None
assert trade.close_profit is None
assert trade.close_date is None
limit_leveraged_buy_order['status'] = 'open'
trade.update(limit_leveraged_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(market_leveraged_buy_order, ten_minutes_ago, 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 = 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
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=5,
open_rate=0.00004099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
interest_rate=0.0005,
exchange='kraken',
leverage=3
)
trade.open_order_id = 'open_trade'
trade.update(market_leveraged_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_close_trade_price(market_leveraged_buy_order, market_leveraged_sell_order, ten_minutes_ago, 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 = 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)
= (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.0025) = 0.0033970229911415386
= (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.003) = 0.0033953202227249265
= (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.005) = 0.011458872511362258
"""
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=ten_minutes_ago,
interest_rate=0.0005,
is_short=True,
leverage=3.0,
exchange='kraken',
)
trade.open_order_id = 'close_trade'
trade.update(market_leveraged_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.0033970229911415386)
# 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.0033953202227249265)
# Test when we apply a Sell order, and ask price with a custom fee rate
trade.update(market_leveraged_sell_order)
assert isclose(trade.calc_close_trade_value(fee=0.005), 0.011458872511362258)
@pytest.mark.usefixtures("init_persistence")
def test_calc_profit(market_leveraged_buy_order, market_leveraged_sell_order, ten_minutes_ago, five_hours_ago, fee):
"""
# TODO: Update this one
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
time-periods: 10 minutes(rounds up to 1 time-period of 4hrs)
5 hours = 5/4
interest: borrowed * interest_rate * time-periods
= 0.0075414886436454 * 0.0005 * 1 = 3.7707443218227e-06 crypto
= 0.0075414886436454 * 0.00025 * 5/4 = 2.3567152011391876e-06 crypto
= 0.0075414886436454 * 0.0005 * 5/4 = 4.713430402278375e-06 crypto
= 0.0075414886436454 * 0.00025 * 1 = 1.88537216091135e-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)
(275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.0025) = 0.014793842426575873
(275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.0025) = 0.0012029976070736241
(275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.003) = 0.014786426966712927
(275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.003) = 0.0012023946007542888
total_profit = close_value - open_value
= 0.014793842426575873 - 0.01134051354788177 = 0.003453328878694104
= 0.0012029976070736241 - 0.01134051354788177 = -0.010137515940808145
= 0.014786426966712927 - 0.01134051354788177 = 0.0034459134188311574
= 0.0012023946007542888 - 0.01134051354788177 = -0.01013811894712748
total_profit_percentage = total_profit / stake_amount
0.003453328878694104/0.0037707443218227 = 0.9158215418394733
-0.010137515940808145/0.0037707443218227 = -2.6884654793852154
0.0034459134188311574/0.0037707443218227 = 0.9138549646255183
-0.01013811894712748/0.0037707443218227 = -2.6886253964381557
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0038388182617629,
amount=5,
open_rate=0.00004099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='kraken',
leverage=3.0,
interest_rate=0.0005
)
trade.open_order_id = 'something'
trade.update(market_leveraged_buy_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.003453328878694104, 8)
assert trade.calc_profit_ratio(
rate=0.00004374, interest_rate=0.0005) == round(0.9158215418394733, 8)
# Lower than open rate
trade.open_date = five_hours_ago
assert trade.calc_profit(
rate=0.00000437, interest_rate=0.00025) == round(-0.010137515940808145, 8)
assert trade.calc_profit_ratio(
rate=0.00000437, interest_rate=0.00025) == round(-2.6884654793852154, 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.0034459134188311574, 8)
assert trade.calc_profit_ratio(rate=0.00004374, fee=0.003,
interest_rate=0.0005) == round(0.9138549646255183, 8)
# Lower than open rate
trade.open_date = ten_minutes_ago
assert trade.calc_profit(rate=0.00000437, fee=0.003,
interest_rate=0.00025) == round(-0.01013811894712748, 8)
assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003,
interest_rate=0.00025) == round(-2.6886253964381557, 8)
# Test when we apply a Sell order. Sell higher than open rate @ 0.00001173
trade.update(market_leveraged_sell_order)
assert trade.calc_profit() == round(0.0001433793561218866, 8)
assert trade.calc_profit_ratio() == round(0.03802415223225211, 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
@pytest.mark.usefixtures("init_persistence")
def test_interest_kraken(market_leveraged_buy_order, ten_minutes_ago, five_hours_ago, fee):
"""
Market trade on Kraken at 3x and 8x 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:
91.99181073 * leverage(3) = 275.97543219 crypto
91.99181073 * leverage(5) = 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 * time-periods
= 0.0075414886436454 * 0.0005 * 1 = 3.7707443218227e-06 base
= 0.0075414886436454 * 0.00025 * 5/4 = 2.3567152011391876e-06 base
= 0.0150829772872908 * 0.0005 * 5/4 = 9.42686080455675e-06 base
= 0.0150829772872908 * 0.00025 * 1 = 3.7707443218227e-06 base
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0037707443218227,
amount=91.99181073,
open_rate=0.00001099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='kraken',
leverage=3.0,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 8)) == 3.7707443218227e-06
trade.open_date = five_hours_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)
) == 2.3567152011391876e-06 # TODO: Fails with 0.08624233
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0037707443218227,
amount=91.99181073,
open_rate=0.00001099,
open_date=five_hours_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='kraken',
is_short=True,
leverage=5.0,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 8)
) == 9.42686080455675e-06 # TODO: Fails with 0.28747445
trade.open_date = ten_minutes_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 3.7707443218227e-06
@pytest.mark.usefixtures("init_persistence")
def test_interest_binance(market_leveraged_buy_order, ten_minutes_ago, five_hours_ago, fee):
"""
Market trade on Kraken at 3x and 8x 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:
91.99181073 * leverage(3) = 275.97543219 crypto
91.99181073 * leverage(5) = 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/24
interest: borrowed * interest_rate * time-periods
= 0.0075414886436454 * 0.0005 * 1/24 = 1.571143467426125e-07 base
= 0.0075414886436454 * 0.00025 * 5/24 = 3.9278586685653125e-07 base
= 0.0150829772872908 * 0.0005 * 5/24 = 1.571143467426125e-06 base
= 0.0150829772872908 * 0.00025 * 1/24 = 1.571143467426125e-07 base
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=275.97543219,
open_rate=0.00001099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=True,
borrowed=275.97543219,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 8)) == 1.571143467426125e-07
trade.open_date = five_hours_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)
) == 3.9278586685653125e-07
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=459.95905365,
open_rate=0.00001099,
open_date=five_hours_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=True,
borrowed=459.95905365,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 8)) == 1.571143467426125e-06
trade.open_date = ten_minutes_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 1.571143467426125e-07

View File

@ -10,7 +10,7 @@ from sqlalchemy import create_engine, inspect, text
from freqtrade import constants from freqtrade import constants
from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db 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_with_leverage, log_has, log_has_re
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -43,9 +43,6 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten
= (0.0010646656050132426 - 0.0010025208853391716) / 0.0010673339398629 = (0.0010646656050132426 - 0.0010025208853391716) / 0.0010673339398629
= 0.05822425142973869 = 0.05822425142973869
#Old
= 1-(0.0010025208853391716/0.0010646656050132426)
= 0.05837017687191848
""" """
trade = Trade( trade = Trade(
id=2, id=2,
@ -295,7 +292,7 @@ def test_calc_close_trade_price_exception(limit_short_order, fee):
exchange='binance', exchange='binance',
interest_rate=0.0005, interest_rate=0.0005,
is_short=True, is_short=True,
leverage=3.0 borrowed=15
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_short_order) trade.update(limit_short_order)
@ -636,40 +633,41 @@ def test_adjust_stop_loss(fee):
assert trade.initial_stop_loss_pct == 0.05 assert trade.initial_stop_loss_pct == 0.05
# Get percent of profit with a custom rate (Higher than open rate) # Get percent of profit with a custom rate (Higher than open rate)
trade.adjust_stop_loss(0.7, 0.1) trade.adjust_stop_loss(0.7, 0.1)
# assert round(trade.stop_loss, 8) == 1.17 #TODO-mg: What is this test? assert round(trade.stop_loss, 8) == 1.17 # TODO-mg: What is this test?
assert trade.stop_loss_pct == 0.1 assert trade.stop_loss_pct == 0.1
assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss == 1.05
assert trade.initial_stop_loss_pct == 0.05 assert trade.initial_stop_loss_pct == 0.05
# current rate lower again ... should not change # current rate lower again ... should not change
trade.adjust_stop_loss(0.8, -0.1) trade.adjust_stop_loss(0.8, -0.1)
# assert round(trade.stop_loss, 8) == 1.17 #TODO-mg: What is this test? assert round(trade.stop_loss, 8) == 1.17 # TODO-mg: What is this test?
assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss == 1.05
assert trade.initial_stop_loss_pct == 0.05 assert trade.initial_stop_loss_pct == 0.05
# current rate higher... should raise stoploss # current rate higher... should raise stoploss
trade.adjust_stop_loss(0.6, -0.1) trade.adjust_stop_loss(0.6, -0.1)
# assert round(trade.stop_loss, 8) == 1.26 #TODO-mg: What is this test? assert round(trade.stop_loss, 8) == 1.26 # TODO-mg: What is this test?
assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss == 1.05
assert trade.initial_stop_loss_pct == 0.05 assert trade.initial_stop_loss_pct == 0.05
# Initial is true but stop_loss set - so doesn't do anything # Initial is true but stop_loss set - so doesn't do anything
trade.adjust_stop_loss(0.3, -0.1, True) trade.adjust_stop_loss(0.3, -0.1, True)
# assert round(trade.stop_loss, 8) == 1.26 #TODO-mg: What is this test? assert round(trade.stop_loss, 8) == 1.26 # TODO-mg: What is this test?
assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss == 1.05
assert trade.initial_stop_loss_pct == 0.05 assert trade.initial_stop_loss_pct == 0.05
assert trade.stop_loss_pct == 0.1 assert trade.stop_loss_pct == 0.1
# TODO-mg: Do a test with a trade that has a liquidation price # TODO-mg: Do a test with a trade that has a liquidation price
# TODO: I don't know how to do this test, but it should be tested for shorts
# @pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
# @pytest.mark.parametrize('use_db', [True, False]) @pytest.mark.parametrize('use_db', [True, False])
# def test_get_open(fee, use_db): def test_get_open(fee, use_db):
# Trade.use_db = use_db Trade.use_db = use_db
# Trade.reset_trades() Trade.reset_trades()
# create_mock_trades(fee, use_db) create_mock_trades_with_leverage(fee, use_db)
# assert len(Trade.get_open_trades()) == 4 assert len(Trade.get_open_trades()) == 5
# Trade.use_db = True Trade.use_db = True
def test_stoploss_reinitialization(default_conf, fee): def test_stoploss_reinitialization(default_conf, fee):
# TODO-mg: I don't understand this at all, I was just going in the opposite direction as the matching function form test_persistance.py
init_db(default_conf['db_url']) init_db(default_conf['db_url'])
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
@ -721,83 +719,26 @@ def test_stoploss_reinitialization(default_conf, fee):
assert trade_adj.initial_stop_loss == 1.04 assert trade_adj.initial_stop_loss == 1.04
assert trade_adj.initial_stop_loss_pct == 0.04 assert trade_adj.initial_stop_loss_pct == 0.04
# @pytest.mark.usefixtures("init_persistence")
# @pytest.mark.parametrize('use_db', [True, False])
# def test_total_open_trades_stakes(fee, use_db):
# Trade.use_db = use_db
# Trade.reset_trades()
# res = Trade.total_open_trades_stakes()
# assert res == 0
# create_mock_trades(fee, use_db)
# res = Trade.total_open_trades_stakes()
# assert res == 0.004
# Trade.use_db = True
# @pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
# def test_get_overall_performance(fee): @pytest.mark.parametrize('use_db', [True, False])
# create_mock_trades(fee) def test_total_open_trades_stakes(fee, use_db):
# res = Trade.get_overall_performance() Trade.use_db = use_db
# assert len(res) == 2 Trade.reset_trades()
# assert 'pair' in res[0] res = Trade.total_open_trades_stakes()
# assert 'profit' in res[0] assert res == 0
# assert 'count' in 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(fee):
# res = Trade.get_best_pair()
# assert res is None
# create_mock_trades(fee)
# res = Trade.get_best_pair()
# assert len(res) == 2
# assert res[0] == 'XRP/BTC'
# assert res[1] == 0.01
# @pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
# def test_update_order_from_ccxt(caplog): def test_get_best_pair(fee):
# # Most basic order return (only has orderid) res = Trade.get_best_pair()
# o = Order.parse_from_ccxt_object({'id': '1234'}, 'ETH/BTC', 'buy') assert res is None
# assert isinstance(o, Order) create_mock_trades_with_leverage(fee)
# assert o.ft_pair == 'ETH/BTC' res = Trade.get_best_pair()
# assert o.ft_order_side == 'buy' assert len(res) == 2
# assert o.order_id == '1234' assert res[0] == 'ETC/BTC'
# assert o.ft_is_open assert res[1] == 0.22626016260162551
# ccxt_order = {
# 'id': '1234',
# 'side': 'buy',
# 'symbol': 'ETH/BTC',
# 'type': 'limit',
# 'price': 1234.5,
# 'amount': 20.0,
# 'filled': 9,
# 'remaining': 11,
# 'status': 'open',
# 'timestamp': 1599394315123
# }
# o = Order.parse_from_ccxt_object(ccxt_order, 'ETH/BTC', 'buy')
# assert isinstance(o, Order)
# assert o.ft_pair == 'ETH/BTC'
# assert o.ft_order_side == 'buy'
# assert o.order_id == '1234'
# assert o.order_type == 'limit'
# assert o.price == 1234.5
# assert o.filled == 9
# assert o.remaining == 11
# assert o.order_date is not None
# assert o.ft_is_open
# assert o.order_filled_date is None
# # Order has been closed
# ccxt_order.update({'filled': 20.0, 'remaining': 0.0, 'status': 'closed'})
# o.update_from_ccxt_object(ccxt_order)
# assert o.filled == 20.0
# assert o.remaining == 0.0
# assert not o.ft_is_open
# assert o.order_filled_date is not None
# ccxt_order.update({'id': 'somethingelse'})
# with pytest.raises(DependencyException, match=r"Order-id's don't match"):
# o.update_from_ccxt_object(ccxt_order)
# message = "aaaa is not a valid response object."
# assert not log_has(message, caplog)
# Order.update_orders([o], 'aaaa')
# assert log_has(message, caplog)
# # Call regular update - shouldn't fail.
# Order.update_orders([o], {'id': '1234'})