Updated tests to new persistence

This commit is contained in:
Sam Germain 2021-07-04 00:11:59 -06:00
parent 75b2c9ca1b
commit 9ddb6981dd
10 changed files with 874 additions and 973 deletions

View File

@ -3,6 +3,7 @@ import logging
from typing import Dict from typing import Dict
import ccxt import ccxt
from decimal import Decimal
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
OperationalException, TemporaryError) OperationalException, TemporaryError)
@ -89,3 +90,12 @@ class Binance(Exchange):
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
@staticmethod
def calculate_interest(borrowed: Decimal, hours: Decimal, interest_rate: Decimal) -> Decimal:
# Rate is per day but accrued hourly or something
# binance: https://www.binance.com/en-AU/support/faq/360030157812
one = Decimal(1)
twenty_four = Decimal(24)
# TODO-mg: Is hours rounded?
return borrowed * interest_rate * max(hours, one)/twenty_four

View File

@ -3,6 +3,7 @@ import logging
from typing import Any, Dict from typing import Any, Dict
import ccxt import ccxt
from decimal import Decimal
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
OperationalException, TemporaryError) OperationalException, TemporaryError)
@ -124,3 +125,11 @@ class Kraken(Exchange):
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
@staticmethod
def calculate_interest(borrowed: Decimal, hours: Decimal, interest_rate: Decimal) -> Decimal:
four = Decimal(4.0)
# https://support.kraken.com/hc/en-us/articles/206161568-What-are-the-fees-for-margin-trading-
opening_fee = borrowed * interest_rate
roll_over_fee = borrowed * interest_rate * max(0, (hours-four)/four)
return opening_fee + roll_over_fee

View File

@ -49,9 +49,6 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
strategy = get_column_def(cols, 'strategy', 'null') strategy = get_column_def(cols, 'strategy', 'null')
leverage = get_column_def(cols, 'leverage', 'null') leverage = get_column_def(cols, 'leverage', 'null')
borrowed = get_column_def(cols, 'borrowed', '0.0')
borrowed_currency = get_column_def(cols, 'borrowed_currency', 'null')
collateral_currency = get_column_def(cols, 'collateral_currency', 'null')
interest_rate = get_column_def(cols, 'interest_rate', '0.0') interest_rate = get_column_def(cols, 'interest_rate', '0.0')
liquidation_price = get_column_def(cols, 'liquidation_price', 'null') liquidation_price = get_column_def(cols, 'liquidation_price', 'null')
is_short = get_column_def(cols, 'is_short', 'False') is_short = get_column_def(cols, 'is_short', 'False')
@ -91,8 +88,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
stoploss_order_id, stoploss_last_update, stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy, max_rate, min_rate, sell_reason, sell_order_status, strategy,
timeframe, open_trade_value, close_profit_abs, timeframe, open_trade_value, close_profit_abs,
leverage, borrowed, borrowed_currency, collateral_currency, interest_rate, leverage, interest_rate, liquidation_price, is_short
liquidation_price, is_short
) )
select id, lower(exchange), select id, lower(exchange),
case case
@ -116,14 +112,11 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
{sell_order_status} sell_order_status, {sell_order_status} sell_order_status,
{strategy} strategy, {timeframe} timeframe, {strategy} strategy, {timeframe} timeframe,
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
{leverage} leverage, {borrowed} borrowed, {borrowed_currency} borrowed_currency, {leverage} leverage, {interest_rate} interest_rate,
{collateral_currency} collateral_currency, {interest_rate} interest_rate,
{liquidation_price} liquidation_price, {is_short} is_short {liquidation_price} liquidation_price, {is_short} is_short
from {table_back_name} from {table_back_name}
""")) """))
# TODO: Does leverage go in here?
def migrate_open_orders_to_trades(engine): def migrate_open_orders_to_trades(engine):
with engine.begin() as connection: with engine.begin() as connection:

View File

@ -132,7 +132,11 @@ 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})')
@ -226,7 +230,6 @@ class LocalTrade():
fee_close_currency: str = '' fee_close_currency: str = ''
open_rate: float = 0.0 open_rate: float = 0.0
open_rate_requested: Optional[float] = None open_rate_requested: Optional[float] = None
# open_trade_value - calculated via _calc_open_trade_value # open_trade_value - calculated via _calc_open_trade_value
open_trade_value: float = 0.0 open_trade_value: float = 0.0
close_rate: Optional[float] = None close_rate: Optional[float] = None
@ -261,61 +264,23 @@ class LocalTrade():
timeframe: Optional[int] = None timeframe: Optional[int] = None
# Margin trading properties # Margin trading properties
borrowed_currency: str = None
collateral_currency: str = None
interest_rate: float = 0.0 interest_rate: float = 0.0
liquidation_price: float = None liquidation_price: float = None
is_short: bool = False is_short: bool = False
borrowed: float = 0.0
leverage: float = None leverage: float = None
# @property @property
# def base_currency(self) -> str: def has_no_leverage(self) -> bool:
# if not self.pair: return (self.leverage == 1.0 and not self.is_short) or self.leverage is None
# raise OperationalException('LocalTrade.pair must be assigned')
# return self.pair.split("/")[1]
# TODO: @samgermain: Amount should be persisted "as is". @property
# I've partially reverted this (this killed most of your tests) def borrowed(self) -> float:
# but leave this here as i'm not sure where you intended to use this. if self.has_no_leverage:
# @property return 0.0
# def amount(self) -> float: elif not self.is_short:
# if self._leverage is not None: return self.stake_amount * (self.leverage-1)
# return self._amount * self.leverage else:
# else: return self.amount
# return self._amount
# @amount.setter
# def amount(self, value):
# self._amount = value
# @property
# def borrowed(self) -> float:
# if self._leverage is not None:
# if self.is_short:
# # If shorting the full amount must be borrowed
# return self._amount * self._leverage
# else:
# # If not shorting, then the trader already owns a bit
# return self._amount * (self._leverage-1)
# else:
# return self._borrowed
# @borrowed.setter
# def borrowed(self, value):
# self._borrowed = value
# self._leverage = None
# @property
# def leverage(self) -> float:
# return self._leverage
# @leverage.setter
# def leverage(self, value):
# self._leverage = value
# self._borrowed = None
# End of margin trading properties
@property @property
def open_date_utc(self): def open_date_utc(self):
@ -326,13 +291,8 @@ class LocalTrade():
return self.close_date.replace(tzinfo=timezone.utc) return self.close_date.replace(tzinfo=timezone.utc)
def __init__(self, **kwargs): def __init__(self, **kwargs):
if kwargs.get('leverage') and kwargs.get('borrowed'):
# TODO-mg: should I raise an error?
raise OperationalException('Cannot pass both borrowed and leverage to Trade')
for key in kwargs: for key in kwargs:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
if not self.is_short:
self.is_short = False
self.recalc_open_trade_value() self.recalc_open_trade_value()
def __repr__(self): def __repr__(self):
@ -404,9 +364,6 @@ class LocalTrade():
'max_rate': self.max_rate, 'max_rate': self.max_rate,
'leverage': self.leverage, 'leverage': self.leverage,
'borrowed': self.borrowed,
'borrowed_currency': self.borrowed_currency,
'collateral_currency': self.collateral_currency,
'interest_rate': self.interest_rate, 'interest_rate': self.interest_rate,
'liquidation_price': self.liquidation_price, 'liquidation_price': self.liquidation_price,
'is_short': self.is_short, 'is_short': self.is_short,
@ -473,7 +430,7 @@ class LocalTrade():
# evaluate if the stop loss needs to be updated # evaluate if the stop loss needs to be updated
else: else:
# stop losses only walk up, never down!, #TODO: But adding more to a margin account would create a lower liquidation price, decreasing the minimum stoploss # stop losses only walk up, never down!, #But adding more to a margin account would create a lower liquidation price, decreasing the minimum stoploss
if (new_loss > self.stop_loss and not self.is_short) or (new_loss < self.stop_loss and self.is_short): if (new_loss > self.stop_loss and not self.is_short) or (new_loss < self.stop_loss and self.is_short):
logger.debug(f"{self.pair} - Adjusting stoploss...") logger.debug(f"{self.pair} - Adjusting stoploss...")
self._set_new_stoploss(new_loss, stoploss) self._set_new_stoploss(new_loss, stoploss)
@ -510,13 +467,8 @@ class LocalTrade():
""" """
order_type = order['type'] order_type = order['type']
if ('leverage' in order and 'borrowed' in order):
raise OperationalException(
'Pass only one of Leverage or Borrowed to the order in update trade')
if 'is_short' in order and order['side'] == 'sell': if 'is_short' in order and order['side'] == 'sell':
# Only set's is_short on opening trades, ignores non-shorts # Only set's is_short on opening trades, ignores non-shorts
# TODO-mg: I don't like this, but it might be the only way
self.is_short = order['is_short'] self.is_short = order['is_short']
# Ignore open and cancelled orders # Ignore open and cancelled orders
@ -527,15 +479,10 @@ class LocalTrade():
if order_type in ('market', 'limit') and self.is_opening_trade(order['side']): if order_type in ('market', 'limit') and self.is_opening_trade(order['side']):
# Update open rate and actual amount # Update open rate and actual amount
self.open_rate = float(safe_value_fallback(order, 'average', 'price')) self.open_rate = float(safe_value_fallback(order, 'average', 'price'))
self.amount = float(safe_value_fallback(order, 'filled', 'amount')) self.amount = float(safe_value_fallback(order, 'filled', 'amount'))
if 'leverage' in order:
if 'borrowed' in order:
self.borrowed = order['borrowed']
elif 'leverage' in order:
self.leverage = order['leverage'] self.leverage = order['leverage']
self.recalc_open_trade_value() self.recalc_open_trade_value()
if self.is_open: if self.is_open:
payment = "SELL" if self.is_short else "BUY" payment = "SELL" if self.is_short else "BUY"
@ -544,7 +491,8 @@ class LocalTrade():
elif order_type in ('market', 'limit') and self.is_closing_trade(order['side']): elif order_type in ('market', 'limit') and self.is_closing_trade(order['side']):
if self.is_open: if self.is_open:
payment = "BUY" if self.is_short else "SELL" payment = "BUY" if self.is_short else "SELL"
# TODO: On Shorts technically your buying a little bit more than the amount because it's the ammount plus the interest # TODO-mg: On Shorts technically your buying a little bit more than the amount because it's the ammount plus the interest
# But this wll only print the original
logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.') logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.')
self.close(safe_value_fallback(order, 'average', 'price')) # TODO: Double check this self.close(safe_value_fallback(order, 'average', 'price')) # TODO: Double check this
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'): elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
@ -632,17 +580,16 @@ class LocalTrade():
: param interest_rate: interest_charge for borrowing this coin(optional). : param interest_rate: interest_charge for borrowing this coin(optional).
If interest_rate is not set self.interest_rate will be used If interest_rate is not set self.interest_rate will be used
""" """
# TODO-mg: Need to set other conditions because sometimes self.open_date is not defined, but why would it ever not be set
zero = Decimal(0.0) zero = Decimal(0.0)
if not (self.borrowed): # If nothing was borrowed
if (self.leverage == 1.0 and not self.is_short) or not self.leverage:
return zero return zero
open_date = self.open_date.replace(tzinfo=None) open_date = self.open_date.replace(tzinfo=None)
now = datetime.utcnow() now = (self.close_date or datetime.utcnow()).replace(tzinfo=None)
# sec_per_day = Decimal(86400)
sec_per_hour = Decimal(3600) sec_per_hour = Decimal(3600)
total_seconds = Decimal((now - open_date).total_seconds()) total_seconds = Decimal((now - open_date).total_seconds())
# days = total_seconds/sec_per_day or zero
hours = total_seconds/sec_per_hour or zero hours = total_seconds/sec_per_hour or zero
rate = Decimal(interest_rate or self.interest_rate) rate = Decimal(interest_rate or self.interest_rate)
@ -654,7 +601,7 @@ class LocalTrade():
if self.exchange == 'binance': if self.exchange == 'binance':
# Rate is per day but accrued hourly or something # Rate is per day but accrued hourly or something
# binance: https://www.binance.com/en-AU/support/faq/360030157812 # binance: https://www.binance.com/en-AU/support/faq/360030157812
return borrowed * rate * max(hours, one)/twenty_four # TODO-mg: Is hours rounded? return borrowed * rate * max(hours, one)/twenty_four
elif self.exchange == 'kraken': elif self.exchange == 'kraken':
# https://support.kraken.com/hc/en-us/articles/206161568-What-are-the-fees-for-margin-trading- # https://support.kraken.com/hc/en-us/articles/206161568-What-are-the-fees-for-margin-trading-
opening_fee = borrowed * rate opening_fee = borrowed * rate
@ -746,16 +693,15 @@ class LocalTrade():
if (self.is_short and close_trade_value == 0.0) or (not self.is_short and self.open_trade_value == 0.0): if (self.is_short and close_trade_value == 0.0) or (not self.is_short and self.open_trade_value == 0.0):
return 0.0 return 0.0
else: else:
if self.borrowed: # TODO: This is only needed so that previous tests that included dummy stake_amounts don't fail. Undate those tests and get rid of this else if self.has_no_leverage:
# TODO: This is only needed so that previous tests that included dummy stake_amounts don't fail. Undate those tests and get rid of this else
profit_ratio = (close_trade_value/self.open_trade_value) - 1
else:
if self.is_short: if self.is_short:
profit_ratio = ((self.open_trade_value - close_trade_value) / self.stake_amount) profit_ratio = ((self.open_trade_value - close_trade_value) / self.stake_amount)
else: else:
profit_ratio = ((close_trade_value - self.open_trade_value) / self.stake_amount) profit_ratio = ((close_trade_value - self.open_trade_value) / self.stake_amount)
else: # TODO: This is only needed so that previous tests that included dummy stake_amounts don't fail. Undate those tests and get rid of this else
if self.is_short:
profit_ratio = 1 - (close_trade_value/self.open_trade_value)
else:
profit_ratio = (close_trade_value/self.open_trade_value) - 1
return float(f"{profit_ratio:.8f}") return float(f"{profit_ratio:.8f}")
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]: def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
@ -907,14 +853,10 @@ class Trade(_DECL_BASE, LocalTrade):
timeframe = Column(Integer, nullable=True) timeframe = Column(Integer, nullable=True)
# Margin trading properties # Margin trading properties
leverage = Column(Float, nullable=True) # TODO: can this be nullable, or should it default to 1? (must also be changed in migrations eventually) leverage = Column(Float, nullable=True)
borrowed = Column(Float, nullable=False, default=0.0)
interest_rate = Column(Float, nullable=False, default=0.0) interest_rate = Column(Float, nullable=False, default=0.0)
liquidation_price = Column(Float, nullable=True) liquidation_price = Column(Float, nullable=True)
is_short = Column(Boolean, nullable=False, default=False) is_short = Column(Boolean, nullable=False, default=False)
# TODO: Bottom 2 might not be needed
borrowed_currency = Column(Float, nullable=True)
collateral_currency = Column(String(25), nullable=True)
# End of margin trading properties # End of margin trading properties
def __init__(self, **kwargs): def __init__(self, **kwargs):

View File

@ -7,10 +7,12 @@ from datetime import datetime, timedelta
from functools import reduce from functools import reduce
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock, Mock, PropertyMock from unittest.mock import MagicMock, Mock, PropertyMock
import arrow import arrow
import numpy as np import numpy as np
import pytest import pytest
from telegram import Chat, Message, Update from telegram import Chat, Message, Update
from freqtrade import constants from freqtrade import constants
from freqtrade.commands import Arguments from freqtrade.commands import Arguments
from freqtrade.data.converter import ohlcv_to_dataframe from freqtrade.data.converter import ohlcv_to_dataframe
@ -23,7 +25,11 @@ from freqtrade.resolvers import ExchangeResolver
from freqtrade.worker import Worker from freqtrade.worker import Worker
from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4, from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4,
mock_trade_5, mock_trade_6, short_trade, leverage_trade) mock_trade_5, mock_trade_6, short_trade, leverage_trade)
logging.getLogger('').setLevel(logging.INFO) logging.getLogger('').setLevel(logging.INFO)
# Do not mask numpy errors as warnings that no one read, raise the exсeption # Do not mask numpy errors as warnings that no one read, raise the exсeption
np.seterr(all='raise') np.seterr(all='raise')
@ -63,6 +69,7 @@ def get_args(args):
def get_mock_coro(return_value): def get_mock_coro(return_value):
async def mock_coro(*args, **kwargs): async def mock_coro(*args, **kwargs):
return return_value return return_value
return Mock(wraps=mock_coro) return Mock(wraps=mock_coro)
@ -85,6 +92,7 @@ def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> No
if mock_markets: if mock_markets:
mocker.patch('freqtrade.exchange.Exchange.markets', mocker.patch('freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=get_markets())) PropertyMock(return_value=get_markets()))
if api_mock: if api_mock:
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
else: else:
@ -118,6 +126,7 @@ def patch_edge(mocker) -> None:
# "LTC/BTC", # "LTC/BTC",
# "XRP/BTC", # "XRP/BTC",
# "NEO/BTC" # "NEO/BTC"
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={ return_value={
'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25), 'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25),
@ -131,6 +140,7 @@ def get_patched_edge(mocker, config) -> Edge:
patch_edge(mocker) patch_edge(mocker)
edge = Edge(config) edge = Edge(config)
return edge return edge
# Functions for recurrent object patching # Functions for recurrent object patching
@ -191,6 +201,7 @@ def create_mock_trades(fee, use_db: bool = True):
Trade.query.session.add(trade) Trade.query.session.add(trade)
else: else:
LocalTrade.add_bt_trade(trade) LocalTrade.add_bt_trade(trade)
# Simulate dry_run entries # Simulate dry_run entries
trade = mock_trade_1(fee) trade = mock_trade_1(fee)
add_trade(trade) add_trade(trade)
@ -220,14 +231,19 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
add_trade(trade) add_trade(trade)
trade = mock_trade_2(fee) trade = mock_trade_2(fee)
add_trade(trade) add_trade(trade)
trade = mock_trade_3(fee) trade = mock_trade_3(fee)
add_trade(trade) add_trade(trade)
trade = mock_trade_4(fee) trade = mock_trade_4(fee)
add_trade(trade) add_trade(trade)
trade = mock_trade_5(fee) trade = mock_trade_5(fee)
add_trade(trade) add_trade(trade)
trade = mock_trade_6(fee) trade = mock_trade_6(fee)
add_trade(trade) add_trade(trade)
trade = short_trade(fee) trade = short_trade(fee)
add_trade(trade) add_trade(trade)
trade = leverage_trade(fee) trade = leverage_trade(fee)
@ -243,6 +259,7 @@ def patch_coingekko(mocker) -> None:
:param mocker: mocker to patch coingekko class :param mocker: mocker to patch coingekko class
:return: None :return: None
""" """
tickermock = MagicMock(return_value={'bitcoin': {'usd': 12345.0}, 'ethereum': {'usd': 12345.0}}) tickermock = MagicMock(return_value={'bitcoin': {'usd': 12345.0}, 'ethereum': {'usd': 12345.0}})
listmock = MagicMock(return_value=[{'id': 'bitcoin', 'name': 'Bitcoin', 'symbol': 'btc', listmock = MagicMock(return_value=[{'id': 'bitcoin', 'name': 'Bitcoin', 'symbol': 'btc',
'website_slug': 'bitcoin'}, 'website_slug': 'bitcoin'},
@ -253,13 +270,13 @@ def patch_coingekko(mocker) -> None:
'freqtrade.rpc.fiat_convert.CoinGeckoAPI', 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=tickermock, get_price=tickermock,
get_coins_list=listmock, get_coins_list=listmock,
) )
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def init_persistence(default_conf): def init_persistence(default_conf):
init_db(default_conf['db_url'], default_conf['dry_run']) init_db(default_conf['db_url'], default_conf['dry_run'])
# TODO-mg: trade with leverage and/or borrowed?
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@ -924,7 +941,8 @@ def limit_sell_order_old():
@pytest.fixture @pytest.fixture
def limit_buy_order_old_partial(): def limit_buy_order_old_partial():
return {'id': 'mocked_limit_buy_old_partial', return {
'id': 'mocked_limit_buy_old_partial',
'type': 'limit', 'type': 'limit',
'side': 'buy', 'side': 'buy',
'symbol': 'ETH/BTC', 'symbol': 'ETH/BTC',
@ -950,6 +968,7 @@ def limit_buy_order_canceled_empty(request):
# Indirect fixture # Indirect fixture
# Documentation: # Documentation:
# https://docs.pytest.org/en/latest/example/parametrize.html#apply-indirect-on-particular-arguments # https://docs.pytest.org/en/latest/example/parametrize.html#apply-indirect-on-particular-arguments
exchange_name = request.param exchange_name = request.param
if exchange_name == 'ftx': if exchange_name == 'ftx':
return { return {
@ -1739,6 +1758,7 @@ def edge_conf(default_conf):
"max_trade_duration_minute": 1440, "max_trade_duration_minute": 1440,
"remove_pumps": False "remove_pumps": False
} }
return conf return conf
@ -1776,7 +1796,6 @@ def rpc_balance():
'used': 0.0 'used': 0.0
}, },
} }
# TODO-mg: Add shorts and leverage?
@pytest.fixture @pytest.fixture
@ -1796,9 +1815,12 @@ def import_fails() -> None:
if name in ["filelock", 'systemd.journal', 'uvloop']: if name in ["filelock", 'systemd.journal', 'uvloop']:
raise ImportError(f"No module named '{name}'") raise ImportError(f"No module named '{name}'")
return realimport(name, *args, **kwargs) return realimport(name, *args, **kwargs)
builtins.__import__ = mockedimport builtins.__import__ = mockedimport
# Run test - then cleanup # Run test - then cleanup
yield yield
# restore previous importfunction # restore previous importfunction
builtins.__import__ = realimport builtins.__import__ = realimport
@ -2083,6 +2105,7 @@ def saved_hyperopt_results():
'is_best': False 'is_best': False
} }
] ]
for res in hyperopt_res: for res in hyperopt_res:
res['results_metrics']['holding_avg_s'] = res['results_metrics']['holding_avg' res['results_metrics']['holding_avg_s'] = res['results_metrics']['holding_avg'
].total_seconds() ].total_seconds()
@ -2091,16 +2114,6 @@ def saved_hyperopt_results():
# * Margin Tests # * Margin Tests
@pytest.fixture
def ten_minutes_ago():
return datetime.utcnow() - timedelta(hours=0, minutes=10)
@pytest.fixture
def five_hours_ago():
return datetime.utcnow() - timedelta(hours=5, minutes=0)
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def limit_short_order_open(): def limit_short_order_open():
return { return {
@ -2112,12 +2125,12 @@ def limit_short_order_open():
'timestamp': arrow.utcnow().int_timestamp, 'timestamp': arrow.utcnow().int_timestamp,
'price': 0.00001173, 'price': 0.00001173,
'amount': 90.99181073, 'amount': 90.99181073,
'borrowed': 90.99181073, 'leverage': 1.0,
'filled': 0.0, 'filled': 0.0,
'cost': 0.00106733393, 'cost': 0.00106733393,
'remaining': 90.99181073, 'remaining': 90.99181073,
'status': 'open', 'status': 'open',
'exchange': 'binance' 'is_short': True
} }
@ -2131,11 +2144,10 @@ def limit_exit_short_order_open():
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().int_timestamp, 'timestamp': arrow.utcnow().int_timestamp,
'price': 0.00001099, 'price': 0.00001099,
'amount': 90.99181073, 'amount': 90.99370639272354,
'filled': 0.0, 'filled': 0.0,
'remaining': 90.99181073, 'remaining': 90.99370639272354,
'status': 'open', 'status': 'open'
'exchange': 'binance'
} }
@ -2166,13 +2178,12 @@ def market_short_order():
'symbol': 'mocked', 'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'price': 0.00004173, 'price': 0.00004173,
'amount': 91.99181073, 'amount': 275.97543219,
'filled': 91.99181073, 'filled': 275.97543219,
'remaining': 0.0, 'remaining': 0.0,
'status': 'closed', 'status': 'closed',
'is_short': True, 'is_short': True,
# 'leverage': 3.0, 'leverage': 3.0
'exchange': 'kraken'
} }
@ -2185,12 +2196,11 @@ def market_exit_short_order():
'symbol': 'mocked', 'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'price': 0.00004099, 'price': 0.00004099,
'amount': 91.99181073, 'amount': 276.113419906095,
'filled': 91.99181073, 'filled': 276.113419906095,
'remaining': 0.0, 'remaining': 0.0,
'status': 'closed', 'status': 'closed',
# 'leverage': 3.0, 'leverage': 3.0
'exchange': 'kraken'
} }
@ -2207,8 +2217,9 @@ def limit_leveraged_buy_order_open():
'price': 0.00001099, 'price': 0.00001099,
'amount': 272.97543219, 'amount': 272.97543219,
'filled': 0.0, 'filled': 0.0,
'cost': 0.0029999999997681, 'cost': 0.0009999999999226999,
'remaining': 272.97543219, 'remaining': 272.97543219,
'leverage': 3.0,
'status': 'open', 'status': 'open',
'exchange': 'binance' 'exchange': 'binance'
} }
@ -2236,6 +2247,7 @@ def limit_leveraged_sell_order_open():
'amount': 272.97543219, 'amount': 272.97543219,
'filled': 0.0, 'filled': 0.0,
'remaining': 272.97543219, 'remaining': 272.97543219,
'leverage': 3.0,
'status': 'open', 'status': 'open',
'exchange': 'binance' 'exchange': 'binance'
} }

View File

@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone
from freqtrade.persistence.models import Order, Trade from freqtrade.persistence.models import Order, Trade
MOCK_TRADE_COUNT = 6 # TODO-mg: Increase for short and leverage MOCK_TRADE_COUNT = 6
def mock_order_1(): def mock_order_1():
@ -433,8 +433,7 @@ def leverage_trade(fee):
interest_rate: 0.05% per day interest_rate: 0.05% per day
open_rate: 0.123 base open_rate: 0.123 base
close_rate: 0.128 base close_rate: 0.128 base
amount: 123.0 crypto amount: 615 crypto
amount_with_leverage: 615.0
stake_amount: 15.129 base stake_amount: 15.129 base
borrowed: 60.516 base borrowed: 60.516 base
leverage: 5 leverage: 5

View File

@ -109,9 +109,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'exchange': 'binance', 'exchange': 'binance',
'leverage': None, 'leverage': None,
'borrowed': 0.0,
'borrowed_currency': None,
'collateral_currency': None,
'interest_rate': 0.0, 'interest_rate': 0.0,
'liquidation_price': None, 'liquidation_price': None,
'is_short': False, 'is_short': False,
@ -183,9 +180,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'exchange': 'binance', 'exchange': 'binance',
'leverage': None, 'leverage': None,
'borrowed': 0.0,
'borrowed_currency': None,
'collateral_currency': None,
'interest_rate': 0.0, 'interest_rate': 0.0,
'liquidation_price': None, 'liquidation_price': None,
'is_short': False, 'is_short': False,

View File

@ -105,27 +105,6 @@ def test_is_opening_closing_trade(fee):
assert trade.is_closing_trade('sell') == False assert trade.is_closing_trade('sell') == False
@pytest.mark.usefixtures("init_persistence")
def test_amount(limit_buy_order, limit_sell_order, fee, caplog):
trade = Trade(
id=2,
pair='ETH/BTC',
stake_amount=0.001,
open_rate=0.01,
amount=5,
is_open=True,
open_date=arrow.utcnow().datetime,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=False
)
assert trade.amount == 5
trade.leverage = 3
assert trade.amount == 15
assert trade._amount == 5
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog): def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog):
""" """
@ -728,6 +707,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
FOREIGN KEY(ft_trade_id) REFERENCES trades (id) FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
) )
""")) """))
connection.execute(text(""" connection.execute(text("""
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status, insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
symbol, order_type, side, price, amount, filled, remaining, cost, order_date, symbol, order_type, side, price, amount, filled, remaining, cost, order_date,
@ -978,9 +958,6 @@ def test_to_json(default_conf, fee):
'exchange': 'binance', 'exchange': 'binance',
'leverage': None, 'leverage': None,
'borrowed': None,
'borrowed_currency': None,
'collateral_currency': None,
'interest_rate': None, 'interest_rate': None,
'liquidation_price': None, 'liquidation_price': None,
'is_short': None, 'is_short': None,
@ -1051,9 +1028,6 @@ def test_to_json(default_conf, fee):
'exchange': 'binance', 'exchange': 'binance',
'leverage': None, 'leverage': None,
'borrowed': None,
'borrowed_currency': None,
'collateral_currency': None,
'interest_rate': None, 'interest_rate': None,
'liquidation_price': None, 'liquidation_price': None,
'is_short': None, 'is_short': None,
@ -1189,42 +1163,6 @@ def test_fee_updated(fee):
assert not trade.fee_updated('asfd') assert not trade.fee_updated('asfd')
@pytest.mark.usefixtures("init_persistence")
def test_update_leverage(fee, ten_minutes_ago):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=5,
open_rate=0.00001099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=True,
interest_rate=0.0005
)
trade.leverage = 3.0
assert trade.borrowed == 15.0
assert trade.amount == 15.0
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=5,
open_rate=0.00001099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=False,
interest_rate=0.0005
)
trade.leverage = 5.0
assert trade.borrowed == 20.0
assert trade.amount == 25.0
@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_total_open_trades_stakes(fee, use_db): def test_total_open_trades_stakes(fee, use_db):

View File

@ -14,7 +14,358 @@ from tests.conftest import create_mock_trades_with_leverage, log_has, log_has_re
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, ten_minutes_ago, caplog): def test_interest_kraken(market_leveraged_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 * 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=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
)
# The trades that last 10 minutes do not need to be rounded because they round up to 4 hours on kraken so we can predict the correct value
assert float(trade.calculate_interest()) == 3.7707443218227e-06
trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
# The trades that last for 5 hours have to be rounded because the length of time that the test takes will vary every time it runs, so we can't predict the exact value
assert float(round(trade.calculate_interest(interest_rate=0.00025), 11)
) == round(2.3567152011391876e-06, 11)
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0037707443218227,
amount=459.95905365,
open_rate=0.00001099,
open_date=datetime.utcnow() - timedelta(hours=5, minutes=0),
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='kraken',
leverage=5.0,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 11)
) == round(9.42686080455675e-06, 11)
trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10)
assert float(trade.calculate_interest(interest_rate=0.00025)) == 3.7707443218227e-06
@pytest.mark.usefixtures("init_persistence")
def test_interest_binance(market_leveraged_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
)
# The trades that last 10 minutes do not always need to be rounded because they round up to 4 hours on kraken so we can predict the correct value
assert round(float(trade.calculate_interest()), 22) == round(4.166666666344583e-08, 22)
trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
# The trades that last for 5 hours have to be rounded because the length of time that the test takes will vary every time it runs, so we can't predict the exact value
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=5, minutes=0),
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
leverage=5.0,
interest_rate=0.0005
)
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(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,
leverage=3.0,
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, 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=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
)
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_open_close_trade_price(limit_leveraged_buy_order, limit_leveraged_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
total_profit = close_value - open_value
= 0.003193788481706411 - 0.0030074999997675204
= 0.00018628848193889044
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=datetime.utcnow() - timedelta(hours=5, minutes=0),
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.00300749999976752
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.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.18628848195329067, 8)
@pytest.mark.usefixtures("init_persistence")
def test_trade_close(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 * 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) - interest
= (15 * 0.2) - (15 * 0.2 * 0.0025) - 0.000625
= 2.9918750000000003
total_profit = close_value - open_value
= 2.9918750000000003 - 1.50375
= 1.4881250000000001
total_profit_percentage = total_profit / stake_amount
= 1.4881250000000001 / 0.5
= 2.9762500000000003
"""
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=5, minutes=0),
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.2)
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(market_leveraged_buy_order, market_leveraged_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(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) - interest
= (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.0025) - 3.7707443218227e-06 = 0.003393252246819716
= (275.97543219 * 0.00001234) - (275.97543219 * 0.00001234 * 0.003) - 3.7707443218227e-06 = 0.003391549478403104
= (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.005) - 3.7707443218227e-06 = 0.011455101767040435
"""
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',
)
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.003393252246819716)
# 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.003391549478403104)
# 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.011455101767040435)
@pytest.mark.usefixtures("init_persistence")
def test_update_limit_order(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, caplog):
""" """
10 minute leveraged limit trade on binance at 3x leverage 10 minute leveraged limit trade on binance at 3x leverage
@ -50,18 +401,17 @@ def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_ord
open_rate=0.01, open_rate=0.01,
amount=5, amount=5,
is_open=True, is_open=True,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
# borrowed=90.99181073, leverage=3.0,
interest_rate=0.0005, interest_rate=0.0005,
exchange='binance' exchange='binance'
) )
# assert trade.open_order_id is None # assert trade.open_order_id is None
assert trade.close_profit is None assert trade.close_profit is None
assert trade.close_date 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.open_order_id = 'something'
trade.update(limit_leveraged_buy_order) trade.update(limit_leveraged_buy_order)
# assert trade.open_order_id is None # assert trade.open_order_id is None
@ -69,7 +419,6 @@ def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_ord
assert trade.close_profit is None assert trade.close_profit is None
assert trade.close_date is None assert trade.close_date is None
assert trade.borrowed == 0.0019999999998453998 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, " 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=.*\).", r"pair=ETH/BTC, amount=272.97543219, open_rate=0.00001099, open_since=.*\).",
caplog) caplog)
@ -78,7 +427,7 @@ def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_ord
trade.update(limit_leveraged_sell_order) trade.update(limit_leveraged_sell_order)
# assert trade.open_order_id is None # assert trade.open_order_id is None
assert trade.close_rate == 0.00001173 assert trade.close_rate == 0.00001173
assert trade.close_profit == 0.18645514861995735 assert trade.close_profit == round(0.18645514861995735, 8)
assert trade.close_date is not None assert trade.close_date is not None
assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, " 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=.*\).", r"pair=ETH/BTC, amount=272.97543219, open_rate=0.00001099, open_since=.*\).",
@ -86,7 +435,7 @@ def test_update_with_binance(limit_leveraged_buy_order, limit_leveraged_sell_ord
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_update_market_order(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, ten_minutes_ago, caplog): def test_update_market_order(market_leveraged_buy_order, market_leveraged_sell_order, fee, caplog):
""" """
10 minute leveraged market trade on Kraken at 3x leverage 10 minute leveraged market trade on Kraken at 3x leverage
Short trade Short trade
@ -94,7 +443,7 @@ def test_update_market_order(limit_leveraged_buy_order, limit_leveraged_sell_ord
interest_rate: 0.05% per 4 hrs interest_rate: 0.05% per 4 hrs
open_rate: 0.00004099 base open_rate: 0.00004099 base
close_rate: 0.00004173 base close_rate: 0.00004173 base
amount: 91.99181073 * leverage(3) = 275.97543219 crypto amount: = 275.97543219 crypto
stake_amount: 0.0037707443218227 stake_amount: 0.0037707443218227
borrowed: 0.0075414886436454 base borrowed: 0.0075414886436454 base
time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) time-periods: 10 minutes(rounds up to 1 time-period of 4hrs)
@ -118,19 +467,18 @@ def test_update_market_order(limit_leveraged_buy_order, limit_leveraged_sell_ord
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.0037707443218227, stake_amount=0.0037707443218227,
amount=5, amount=5,
open_rate=0.01, open_rate=0.00004099,
is_open=True, is_open=True,
leverage=3, leverage=3,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
interest_rate=0.0005, interest_rate=0.0005,
exchange='kraken' exchange='kraken'
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order) trade.update(market_leveraged_buy_order)
assert trade.leverage == 3.0 assert trade.leverage == 3.0
assert trade.is_short == True
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 0.00004099 assert trade.open_rate == 0.00004099
assert trade.close_profit is None assert trade.close_profit is None
@ -144,10 +492,10 @@ def test_update_market_order(limit_leveraged_buy_order, limit_leveraged_sell_ord
caplog.clear() caplog.clear()
trade.is_open = True trade.is_open = True
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_leveraged_sell_order) trade.update(market_leveraged_sell_order)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.close_rate == 0.00004173 assert trade.close_rate == 0.00004173
assert trade.close_profit == 0.03802415223225211 assert trade.close_profit == round(0.03802415223225211, 8)
assert trade.close_date is not None assert trade.close_date is not None
# TODO: The amount should maybe be the opening amount + the interest # TODO: The amount should maybe be the opening amount + the interest
# TODO: Uncomment the next assert and make it work. # TODO: Uncomment the next assert and make it work.
@ -157,116 +505,6 @@ def test_update_market_order(limit_leveraged_buy_order, limit_leveraged_sell_ord
caplog) 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") @pytest.mark.usefixtures("init_persistence")
def test_calc_close_trade_price_exception(limit_leveraged_buy_order, fee): def test_calc_close_trade_price_exception(limit_leveraged_buy_order, fee):
trade = Trade( trade = Trade(
@ -278,7 +516,7 @@ def test_calc_close_trade_price_exception(limit_leveraged_buy_order, fee):
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='binance', exchange='binance',
interest_rate=0.0005, interest_rate=0.0005,
borrowed=0.002 leverage=3.0
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order) trade.update(limit_leveraged_buy_order)
@ -286,118 +524,7 @@ def test_calc_close_trade_price_exception(limit_leveraged_buy_order, fee):
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_update_open_order(limit_leveraged_buy_order): def test_calc_profit(market_leveraged_buy_order, market_leveraged_sell_order, fee):
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 # TODO: Update this one
Leveraged trade on Kraken at 3x leverage Leveraged trade on Kraken at 3x leverage
@ -418,29 +545,29 @@ def test_calc_profit(market_leveraged_buy_order, market_leveraged_sell_order, te
= 0.0075414886436454 * 0.00025 * 1 = 1.88537216091135e-06 crypto = 0.0075414886436454 * 0.00025 * 1 = 1.88537216091135e-06 crypto
open_value: (amount * open_rate) + (amount * open_rate * fee) open_value: (amount * open_rate) + (amount * open_rate * fee)
= (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) = 0.01134051354788177 = (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) = 0.01134051354788177
close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest
(275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.0025) = 0.014793842426575873 (275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.0025) - 3.7707443218227e-06 = 0.01479007168225405
(275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.0025) = 0.0012029976070736241 (275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.0025) - 2.3567152011391876e-06 = 0.001200640891872485
(275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.003) = 0.014786426966712927 (275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.003) - 4.713430402278375e-06 = 0.014781713536310649
(275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.003) = 0.0012023946007542888 (275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.003) - 1.88537216091135e-06 = 0.0012005092285933775
total_profit = close_value - open_value total_profit = close_value - open_value
= 0.014793842426575873 - 0.01134051354788177 = 0.003453328878694104 = 0.01479007168225405 - 0.01134051354788177 = 0.003449558134372281
= 0.0012029976070736241 - 0.01134051354788177 = -0.010137515940808145 = 0.001200640891872485 - 0.01134051354788177 = -0.010139872656009285
= 0.014786426966712927 - 0.01134051354788177 = 0.0034459134188311574 = 0.014781713536310649 - 0.01134051354788177 = 0.0034411999884288794
= 0.0012023946007542888 - 0.01134051354788177 = -0.01013811894712748 = 0.0012005092285933775 - 0.01134051354788177 = -0.010140004319288392
total_profit_percentage = total_profit / stake_amount total_profit_percentage = total_profit / stake_amount
0.003453328878694104/0.0037707443218227 = 0.9158215418394733 0.003449558134372281/0.0037707443218227 = 0.9148215418394732
-0.010137515940808145/0.0037707443218227 = -2.6884654793852154 -0.010139872656009285/0.0037707443218227 = -2.6890904793852157
0.0034459134188311574/0.0037707443218227 = 0.9138549646255183 0.0034411999884288794/0.0037707443218227 = 0.9126049646255184
-0.01013811894712748/0.0037707443218227 = -2.6886253964381557 -0.010140004319288392/0.0037707443218227 = -2.6891253964381554
""" """
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.0038388182617629, stake_amount=0.0037707443218227,
amount=5, amount=5,
open_rate=0.00004099, open_rate=0.00004099,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='kraken', exchange='kraken',
@ -452,31 +579,31 @@ def test_calc_profit(market_leveraged_buy_order, market_leveraged_sell_order, te
# Custom closing rate and regular fee rate # Custom closing rate and regular fee rate
# Higher than open rate # Higher than open rate
assert trade.calc_profit(rate=0.00004374, interest_rate=0.0005) == round( assert trade.calc_profit(rate=0.00005374, interest_rate=0.0005) == round(
0.003453328878694104, 8) 0.003449558134372281, 8)
assert trade.calc_profit_ratio( assert trade.calc_profit_ratio(
rate=0.00004374, interest_rate=0.0005) == round(0.9158215418394733, 8) rate=0.00005374, interest_rate=0.0005) == round(0.9148215418394732, 8)
# Lower than open rate # Lower than open rate
trade.open_date = five_hours_ago trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
assert trade.calc_profit( assert trade.calc_profit(
rate=0.00000437, interest_rate=0.00025) == round(-0.010137515940808145, 8) rate=0.00000437, interest_rate=0.00025) == round(-0.010139872656009285, 8)
assert trade.calc_profit_ratio( assert trade.calc_profit_ratio(
rate=0.00000437, interest_rate=0.00025) == round(-2.6884654793852154, 8) rate=0.00000437, interest_rate=0.00025) == round(-2.6890904793852157, 8)
# Custom closing rate and custom fee rate # Custom closing rate and custom fee rate
# Higher than open rate # Higher than open rate
assert trade.calc_profit(rate=0.00004374, fee=0.003, assert trade.calc_profit(rate=0.00005374, fee=0.003,
interest_rate=0.0005) == round(0.0034459134188311574, 8) interest_rate=0.0005) == round(0.0034411999884288794, 8)
assert trade.calc_profit_ratio(rate=0.00004374, fee=0.003, assert trade.calc_profit_ratio(rate=0.00005374, fee=0.003,
interest_rate=0.0005) == round(0.9138549646255183, 8) interest_rate=0.0005) == round(0.9126049646255184, 8)
# Lower than open rate # Lower than open rate
trade.open_date = ten_minutes_ago trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10)
assert trade.calc_profit(rate=0.00000437, fee=0.003, assert trade.calc_profit(rate=0.00000437, fee=0.003,
interest_rate=0.00025) == round(-0.01013811894712748, 8) interest_rate=0.00025) == round(-0.010140004319288392, 8)
assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003, assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003,
interest_rate=0.00025) == round(-2.6886253964381557, 8) interest_rate=0.00025) == round(-2.6891253964381554, 8)
# Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173
trade.update(market_leveraged_sell_order) trade.update(market_leveraged_sell_order)
@ -486,131 +613,3 @@ def test_calc_profit(market_leveraged_buy_order, market_leveraged_sell_order, te
# Test with a custom fee rate on the close trade # 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) == 0.00006163
# assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 # 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

@ -14,7 +14,357 @@ from tests.conftest import create_mock_trades_with_leverage, log_has, log_has_re
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten_minutes_ago, caplog): def test_interest_kraken(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 * time-periods
= 275.97543219 * 0.0005 * 1 = 0.137987716095 crypto
= 275.97543219 * 0.00025 * 5/4 = 0.086242322559375 crypto
= 459.95905365 * 0.0005 * 5/4 = 0.28747440853125 crypto
= 459.95905365 * 0.00025 * 1 = 0.1149897634125 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
)
assert float(round(trade.calculate_interest(), 8)) == round(0.137987716095, 8)
trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)
) == round(0.086242322559375, 8)
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=459.95905365,
open_rate=0.00001099,
open_date=datetime.utcnow() - timedelta(hours=5, minutes=0),
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)) == round(0.28747440853125, 8)
trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10)
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)
) == round(0.1149897634125, 8)
@ pytest.mark.usefixtures("init_persistence")
def test_interest_binance(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
)
assert float(round(trade.calculate_interest(), 8)) == 0.00574949
trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
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=5, minutes=0),
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
is_short=True,
leverage=5.0,
interest_rate=0.0005
)
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(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',
)
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(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',
)
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(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
)
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(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
time-periods: 10 minutes(rounds up to 1 time-period of 4hrs)
interest: borrowed * interest_rate * time-periods
= 275.97543219 * 0.0005 * 1 = 0.137987716095 crypto
amount_closed: amount + interest = 275.97543219 + 0.137987716095 = 276.113419906095
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (276.113419906095 * 0.00001234) + (276.113419906095 * 0.00001234 * 0.0025)
= 0.01134618380465571
"""
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',
)
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.003415757700645315)
# 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.0034174613204461354)
# 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.011374478527360586)
@ pytest.mark.usefixtures("init_persistence")
def test_calc_open_close_trade_price(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
total_profit = open_value - close_value
= 0.0010646656050132426 - 0.001002604427005832
= 0.00006206117800741065
total_profit_percentage = (close_value - open_value) / stake_amount
= (0.0010646656050132426 - 0.0010025208853391716)/0.0010673339398629
= 0.05822425142973869
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0010673339398629,
open_rate=0.01,
amount=5,
open_date=datetime.utcnow() - timedelta(hours=5, minutes=0),
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='binance',
interest_rate=0.0005
)
trade.open_order_id = 'something'
trade.update(limit_short_order)
assert trade._calc_open_trade_value() == 0.0010646656050132426
trade.update(limit_exit_short_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.001002604427005832, 11)
# Profit in BTC
assert round(trade.calc_profit(), 8) == round(0.00006206117800741065, 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 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 * 5/4 = 0.009375 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.009375
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (15.009375 * 0.01) + (15.009375 * 0.01 * 0.0025)
= 0.150468984375
total_profit = open_value - close_value
= 0.29925 - 0.150468984375
= 0.148781015625
total_profit_percentage = total_profit / stake_amount
= 0.148781015625 / 0.1
= 1.4878101562500001
"""
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=5, minutes=0),
exchange='kraken',
is_short=True,
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.01)
assert trade.is_open is False
assert trade.close_profit == round(1.4878101562500001, 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(limit_short_order, limit_exit_short_order, fee, caplog):
""" """
10 minute short limit trade on binance 10 minute short limit trade on binance
@ -40,7 +390,7 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten
= 0.0010646656050132426 - 0.0010025208853391716 = 0.0010646656050132426 - 0.0010025208853391716
= 0.00006214471967407108 = 0.00006214471967407108
total_profit_percentage = (close_value - open_value) / stake_amount total_profit_percentage = (close_value - open_value) / stake_amount
= (0.0010646656050132426 - 0.0010025208853391716) / 0.0010673339398629 = 0.00006214471967407108 / 0.0010673339398629
= 0.05822425142973869 = 0.05822425142973869
""" """
@ -51,7 +401,7 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten
open_rate=0.01, open_rate=0.01,
amount=5, amount=5,
is_open=True, is_open=True,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
# borrowed=90.99181073, # borrowed=90.99181073,
@ -61,7 +411,7 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten
# assert trade.open_order_id is None # assert trade.open_order_id is None
assert trade.close_profit is None assert trade.close_profit is None
assert trade.close_date is None assert trade.close_date is None
assert trade.borrowed is None assert trade.borrowed == 0.0
assert trade.is_short is None assert trade.is_short is None
# trade.open_order_id = 'something' # trade.open_order_id = 'something'
trade.update(limit_short_order) trade.update(limit_short_order)
@ -91,7 +441,6 @@ def test_update_market_order(
market_short_order, market_short_order,
market_exit_short_order, market_exit_short_order,
fee, fee,
ten_minutes_ago,
caplog caplog
): ):
""" """
@ -101,7 +450,7 @@ def test_update_market_order(
interest_rate: 0.05% per 4 hrs interest_rate: 0.05% per 4 hrs
open_rate: 0.00004173 base open_rate: 0.00004173 base
close_rate: 0.00004099 base close_rate: 0.00004099 base
amount: 91.99181073 * leverage(3) = 275.97543219 crypto amount: = 275.97543219 crypto
stake_amount: 0.0038388182617629 stake_amount: 0.0038388182617629
borrowed: 275.97543219 crypto borrowed: 275.97543219 crypto
time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) time-periods: 10 minutes(rounds up to 1 time-period of 4hrs)
@ -130,7 +479,8 @@ def test_update_market_order(
is_open=True, is_open=True,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
leverage=3.0,
interest_rate=0.0005, interest_rate=0.0005,
exchange='kraken' exchange='kraken'
) )
@ -165,232 +515,7 @@ def test_update_market_order(
@ pytest.mark.usefixtures("init_persistence") @ pytest.mark.usefixtures("init_persistence")
def test_calc_open_close_trade_price(limit_short_order, limit_exit_short_order, five_hours_ago, fee): def test_calc_profit(market_short_order, market_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
total_profit = open_value - close_value
= 0.0010646656050132426 - 0.001002604427005832
= 0.00006206117800741065
total_profit_percentage = (close_value - open_value) / stake_amount
= (0.0010646656050132426 - 0.0010025208853391716)/0.0010673339398629
= 0.05822425142973869
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.0010673339398629,
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_short_order)
assert trade._calc_open_trade_value() == 0.0010646656050132426
trade.update(limit_exit_short_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.001002604427005832, 11)
# Profit in BTC
assert round(trade.calc_profit(), 8) == round(0.00006206117800741065, 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):
"""
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: 5 * 3 = 15 crypto
borrowed: 15 crypto
time-periods: 5 hours = 5/4
interest: borrowed * interest_rate * time-periods
= 15 * 0.0005 * 5/4 = 0.009375 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.009375
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (15.009375 * 0.01) + (15.009375 * 0.01 * 0.0025)
= 0.150468984375
total_profit = open_value - close_value
= 0.29925 - 0.150468984375
= 0.148781015625
total_profit_percentage = total_profit / stake_amount
= 0.148781015625 / 0.1
= 1.4878101562500001
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.1,
open_rate=0.02,
amount=5,
is_open=True,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=five_hours_ago,
exchange='kraken',
is_short=True,
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.01)
assert trade.is_open is False
assert trade.close_profit == round(1.4878101562500001, 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_short_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,
is_short=True,
borrowed=15
)
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_update_open_order(limit_short_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,
is_short=True,
exchange='binance',
)
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_open_trade_value(market_short_order, ten_minutes_ago, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=5,
open_rate=0.00004173,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
interest_rate=0.0005,
is_short=True,
leverage=3.0,
exchange='kraken',
)
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_calc_close_trade_price(market_short_order, market_exit_short_order, ten_minutes_ago, 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: 91.99181073 * leverage(3) = 275.97543219 crypto
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 * 1 = 0.137987716095 crypto
amount_closed: amount + interest = 275.97543219 + 0.137987716095 = 276.113419906095
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
= (276.113419906095 * 0.00001234) + (276.113419906095 * 0.00001234 * 0.0025)
= 0.01134618380465571
"""
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_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.003415757700645315)
# 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.0034174613204461354)
# 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.011374478527360586)
@pytest.mark.usefixtures("init_persistence")
def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ago, five_hours_ago, fee):
""" """
Market trade on Kraken at 3x leverage Market trade on Kraken at 3x leverage
Short trade Short trade
@ -399,7 +524,7 @@ def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ag
open_rate: 0.00004173 base open_rate: 0.00004173 base
close_rate: 0.00004099 base close_rate: 0.00004099 base
stake_amount: 0.0038388182617629 stake_amount: 0.0038388182617629
amount: 91.99181073 * leverage(3) = 275.97543219 crypto amount: = 275.97543219 crypto
borrowed: 275.97543219 crypto borrowed: 275.97543219 crypto
time-periods: 10 minutes(rounds up to 1 time-period of 4hrs) time-periods: 10 minutes(rounds up to 1 time-period of 4hrs)
5 hours = 5/4 5 hours = 5/4
@ -438,7 +563,7 @@ def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ag
stake_amount=0.0038388182617629, stake_amount=0.0038388182617629,
amount=5, amount=5,
open_rate=0.00001099, open_rate=0.00001099,
open_date=ten_minutes_ago, open_date=datetime.utcnow() - timedelta(hours=0, minutes=10),
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='kraken', exchange='kraken',
@ -456,7 +581,7 @@ def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ag
rate=0.00004374, interest_rate=0.0005) == round(-0.16143779115744006, 8) rate=0.00004374, interest_rate=0.0005) == round(-0.16143779115744006, 8)
# Lower than open rate # Lower than open rate
trade.open_date = five_hours_ago trade.open_date = datetime.utcnow() - timedelta(hours=5, minutes=0)
assert trade.calc_profit(rate=0.00000437, interest_rate=0.00025) == round(0.01027826, 8) assert trade.calc_profit(rate=0.00000437, interest_rate=0.00025) == round(0.01027826, 8)
assert trade.calc_profit_ratio( assert trade.calc_profit_ratio(
rate=0.00000437, interest_rate=0.00025) == round(2.677453699564163, 8) rate=0.00000437, interest_rate=0.00025) == round(2.677453699564163, 8)
@ -469,7 +594,7 @@ def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ag
interest_rate=0.0005) == round(-0.16340506919482353, 8) interest_rate=0.0005) == round(-0.16340506919482353, 8)
# Lower than open rate # Lower than open rate
trade.open_date = ten_minutes_ago trade.open_date = datetime.utcnow() - timedelta(hours=0, minutes=10)
assert trade.calc_profit(rate=0.00000437, fee=0.003, assert trade.calc_profit(rate=0.00000437, fee=0.003,
interest_rate=0.00025) == round(0.01027773, 8) interest_rate=0.00025) == round(0.01027773, 8)
assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003, assert trade.calc_profit_ratio(rate=0.00000437, fee=0.003,
@ -485,129 +610,6 @@ def test_calc_profit(market_short_order, market_exit_short_order, ten_minutes_ag
# assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 # assert trade.calc_profit_ratio(fee=0.003) == 0.06147824
@pytest.mark.usefixtures("init_persistence")
def test_interest_kraken(market_short_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.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 time-period of 4hrs)
5 hours = 5/4
interest: borrowed * interest_rate * time-periods
= 275.97543219 * 0.0005 * 1 = 0.137987716095 crypto
= 275.97543219 * 0.00025 * 5/4 = 0.086242322559375 crypto
= 459.95905365 * 0.0005 * 5/4 = 0.28747440853125 crypto
= 459.95905365 * 0.00025 * 1 = 0.1149897634125 crypto
"""
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
amount=91.99181073,
open_rate=0.00001099,
open_date=ten_minutes_ago,
fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='kraken',
is_short=True,
leverage=3.0,
interest_rate=0.0005
)
assert float(round(trade.calculate_interest(), 8)) == 0.13798772
trade.open_date = five_hours_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)
) == 0.08624232 # TODO: Fails with 0.08624233
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
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)) == 0.28747441 # TODO: Fails with 0.28747445
trade.open_date = ten_minutes_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 0.11498976
@pytest.mark.usefixtures("init_persistence")
def test_interest_binance(market_short_order, ten_minutes_ago, five_hours_ago, 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=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)) == 0.00574949
trade.open_date = five_hours_ago
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=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)) == 0.04791240
trade.open_date = ten_minutes_ago
assert float(round(trade.calculate_interest(interest_rate=0.00025), 8)) == 0.00479124
def test_adjust_stop_loss(fee): def test_adjust_stop_loss(fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
@ -653,6 +655,8 @@ def test_adjust_stop_loss(fee):
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
trade.liquidation_price == 1.03
# 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
@ -679,7 +683,8 @@ def test_stoploss_reinitialization(default_conf, fee):
exchange='binance', exchange='binance',
open_rate=1, open_rate=1,
max_rate=1, max_rate=1,
is_short=True is_short=True,
leverage=3.0,
) )
trade.adjust_stop_loss(trade.open_rate, -0.05, True) trade.adjust_stop_loss(trade.open_rate, -0.05, True)
assert trade.stop_loss == 1.05 assert trade.stop_loss == 1.05