Updated tests to new persistence
This commit is contained in:
parent
e0d42d2eb7
commit
78708b27f2
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
|
||||||
|
@ -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)
|
||||||
@ -86,12 +436,11 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, ten
|
|||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@ pytest.mark.usefixtures("init_persistence")
|
||||||
def test_update_market_order(
|
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'
|
||||||
)
|
)
|
||||||
@ -164,233 +514,8 @@ def test_update_market_order(
|
|||||||
# caplog)
|
# caplog)
|
||||||
|
|
||||||
|
|
||||||
@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,11 +655,13 @@ 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
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@ pytest.mark.usefixtures("init_persistence")
|
||||||
@pytest.mark.parametrize('use_db', [True, False])
|
@ pytest.mark.parametrize('use_db', [True, False])
|
||||||
def test_get_open(fee, use_db):
|
def test_get_open(fee, use_db):
|
||||||
Trade.use_db = use_db
|
Trade.use_db = use_db
|
||||||
Trade.reset_trades()
|
Trade.reset_trades()
|
||||||
@ -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
|
||||||
@ -720,8 +725,8 @@ def test_stoploss_reinitialization(default_conf, fee):
|
|||||||
assert trade_adj.initial_stop_loss_pct == 0.04
|
assert trade_adj.initial_stop_loss_pct == 0.04
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@ pytest.mark.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):
|
||||||
Trade.use_db = use_db
|
Trade.use_db = use_db
|
||||||
Trade.reset_trades()
|
Trade.reset_trades()
|
||||||
@ -733,7 +738,7 @@ def test_total_open_trades_stakes(fee, use_db):
|
|||||||
Trade.use_db = True
|
Trade.use_db = True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@ pytest.mark.usefixtures("init_persistence")
|
||||||
def test_get_best_pair(fee):
|
def test_get_best_pair(fee):
|
||||||
res = Trade.get_best_pair()
|
res = Trade.get_best_pair()
|
||||||
assert res is None
|
assert res is None
|
||||||
|
Loading…
Reference in New Issue
Block a user