All persistence margin tests pass

Flake8 compliant, passed mypy, ran isort .
This commit is contained in:
Sam Germain 2021-07-07 21:14:08 -06:00
parent a368dfa7b5
commit 7f75c978a0
7 changed files with 135 additions and 115 deletions

View File

@ -1,16 +1,20 @@
from enum import Enum, auto
from decimal import Decimal
from enum import Enum
from freqtrade.exceptions import OperationalException
one = Decimal(1.0)
four = Decimal(4.0)
twenty_four = Decimal(24.0)
class InterestMode(Enum):
"""Equations to calculate interest"""
HOURSPERDAY = "HOURSPERDAY"
HOURSPER4 = "HOURSPER4" # Hours per 4 hour segment
NONE = "NONE"
def __call__(self, *args, **kwargs):
@ -21,4 +25,4 @@ class InterestMode(Enum):
elif self.name == "HOURSPER4":
return borrowed * rate * (1 + max(0, (hours-four)/four))
else:
raise OperationalException(f"Leverage not available on this exchange with freqtrade")
raise OperationalException("Leverage not available on this exchange with freqtrade")

View File

@ -6,7 +6,7 @@ from datetime import datetime, timezone
from decimal import Decimal
from typing import Any, Dict, List, Optional
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Integer, String,
from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String,
create_engine, desc, func, inspect)
from sqlalchemy.exc import NoSuchModuleError
from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session, sessionmaker
@ -159,7 +159,7 @@ class Order(_DECL_BASE):
self.remaining = order.get('remaining', self.remaining)
self.cost = order.get('cost', self.cost)
self.leverage = order.get('leverage', self.leverage)
# TODO-mg: liquidation price? is_short?
# TODO-mg: is_short?
if 'timestamp' in order and order['timestamp'] is not None:
self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc)
@ -269,7 +269,7 @@ class LocalTrade():
liquidation_price: Optional[float] = None
is_short: bool = False
leverage: float = 1.0
interest_mode: Optional[InterestMode] = None
interest_mode: InterestMode = InterestMode.NONE
@property
def has_no_leverage(self) -> bool:
@ -299,8 +299,9 @@ class LocalTrade():
self.recalc_open_trade_value()
def set_stop_loss_helper(self, stop_loss: Optional[float], liquidation_price: Optional[float]):
# Stoploss would be better as a computed variable, but that messes up the database so it might not be possible
# TODO-mg: What should be done about initial_stop_loss
# Stoploss would be better as a computed variable,
# but that messes up the database so it might not be possible
if liquidation_price is not None:
if stop_loss is not None:
if self.is_short:
@ -312,6 +313,8 @@ class LocalTrade():
self.initial_stop_loss = liquidation_price
self.liquidation_price = liquidation_price
else:
# programmming error check: 1 of liqudication_price or stop_loss must be set
assert stop_loss is not None
if not self.stop_loss:
self.initial_stop_loss = stop_loss
self.stop_loss = stop_loss
@ -438,11 +441,13 @@ class LocalTrade():
if self.is_short:
new_loss = float(current_price * (1 + abs(stoploss)))
if self.liquidation_price: # If trading on margin, don't set the stoploss below the liquidation price
# If trading on margin, don't set the stoploss below the liquidation price
if self.liquidation_price:
new_loss = min(self.liquidation_price, new_loss)
else:
new_loss = float(current_price * (1 - abs(stoploss)))
if self.liquidation_price: # If trading on margin, don't set the stoploss below the liquidation price
# If trading on margin, don't set the stoploss below the liquidation price
if self.liquidation_price:
new_loss = max(self.liquidation_price, new_loss)
# no stop loss assigned yet
@ -457,8 +462,14 @@ class LocalTrade():
# evaluate if the stop loss needs to be updated
else:
# 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):
higherStop = new_loss > self.stop_loss
lowerStop = new_loss < self.stop_loss
# 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 (higherStop and not self.is_short) or (lowerStop and self.is_short):
logger.debug(f"{self.pair} - Adjusting stoploss...")
self._set_new_stoploss(new_loss, stoploss)
else:
@ -518,10 +529,10 @@ class LocalTrade():
elif order_type in ('market', 'limit') and self.is_closing_trade(order['side']):
if self.is_open:
payment = "BUY" if self.is_short else "SELL"
# 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
# TODO-mg: On shorts, you buy a little bit more than the amount (amount + interest)
# This wll only print the original amount
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-mg: Double check this
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss
@ -644,7 +655,7 @@ class LocalTrade():
if self.is_short:
amount = Decimal(self.amount) + Decimal(interest)
else:
# The interest does not need to be purchased on longs because the user already owns that currency in your wallet
# Currency already owned for longs, no need to purchase
amount = Decimal(self.amount)
close_trade = Decimal(amount) * Decimal(rate or self.close_rate) # type: ignore
@ -697,11 +708,12 @@ class LocalTrade():
fee=(fee or self.fee_close),
interest_rate=(interest_rate or self.interest_rate)
)
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
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
# TODO-mg: Use one profit_ratio calculation
profit_ratio = (close_trade_value/self.open_trade_value) - 1
else:
if self.is_short:
@ -864,7 +876,7 @@ class Trade(_DECL_BASE, LocalTrade):
interest_rate = Column(Float, nullable=False, default=0.0)
liquidation_price = Column(Float, nullable=True)
is_short = Column(Boolean, nullable=False, default=False)
interest_mode = Column(String(100), nullable=True)
interest_mode = Column(Enum(InterestMode), nullable=True)
# End of margin trading properties
def __init__(self, **kwargs):

View File

@ -23,8 +23,8 @@ from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import LocalTrade, Trade, init_db
from freqtrade.resolvers import ExchangeResolver
from freqtrade.worker import Worker
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)
from tests.conftest_trades import (leverage_trade, mock_trade_1, mock_trade_2, mock_trade_3,
mock_trade_4, mock_trade_5, mock_trade_6, short_trade)
logging.getLogger('').setLevel(logging.INFO)
@ -2209,7 +2209,7 @@ def market_exit_short_order():
# leverage 3x
@pytest.fixture(scope='function')
def limit_leveraged_buy_order_open():
def limit_lev_buy_order_open():
return {
'id': 'mocked_limit_buy',
'type': 'limit',
@ -2229,8 +2229,8 @@ def limit_leveraged_buy_order_open():
@pytest.fixture(scope='function')
def limit_leveraged_buy_order(limit_leveraged_buy_order_open):
order = deepcopy(limit_leveraged_buy_order_open)
def limit_lev_buy_order(limit_lev_buy_order_open):
order = deepcopy(limit_lev_buy_order_open)
order['status'] = 'closed'
order['filled'] = order['amount']
order['remaining'] = 0.0
@ -2238,7 +2238,7 @@ def limit_leveraged_buy_order(limit_leveraged_buy_order_open):
@pytest.fixture
def limit_leveraged_sell_order_open():
def limit_lev_sell_order_open():
return {
'id': 'mocked_limit_sell',
'type': 'limit',
@ -2257,8 +2257,8 @@ def limit_leveraged_sell_order_open():
@pytest.fixture
def limit_leveraged_sell_order(limit_leveraged_sell_order_open):
order = deepcopy(limit_leveraged_sell_order_open)
def limit_lev_sell_order(limit_lev_sell_order_open):
order = deepcopy(limit_lev_sell_order_open)
order['remaining'] = 0.0
order['filled'] = order['amount']
order['status'] = 'closed'
@ -2266,7 +2266,7 @@ def limit_leveraged_sell_order(limit_leveraged_sell_order_open):
@pytest.fixture(scope='function')
def market_leveraged_buy_order():
def market_lev_buy_order():
return {
'id': 'mocked_market_buy',
'type': 'market',
@ -2284,7 +2284,7 @@ def market_leveraged_buy_order():
@pytest.fixture
def market_leveraged_sell_order():
def market_lev_sell_order():
return {
'id': 'mocked_limit_sell',
'type': 'market',

View File

@ -79,10 +79,10 @@ def test_is_opening_closing_trade(fee):
is_short=False,
leverage=2.0
)
assert trade.is_opening_trade('buy') == True
assert trade.is_opening_trade('sell') == False
assert trade.is_closing_trade('buy') == False
assert trade.is_closing_trade('sell') == True
assert trade.is_opening_trade('buy') is True
assert trade.is_opening_trade('sell') is False
assert trade.is_closing_trade('buy') is False
assert trade.is_closing_trade('sell') is True
trade = Trade(
id=2,
@ -99,10 +99,10 @@ def test_is_opening_closing_trade(fee):
leverage=2.0
)
assert trade.is_opening_trade('buy') == False
assert trade.is_opening_trade('sell') == True
assert trade.is_closing_trade('buy') == True
assert trade.is_closing_trade('sell') == False
assert trade.is_opening_trade('buy') is False
assert trade.is_opening_trade('sell') is True
assert trade.is_closing_trade('buy') is True
assert trade.is_closing_trade('sell') is False
@pytest.mark.usefixtures("init_persistence")

View File

@ -1,21 +1,15 @@
import logging
from datetime import datetime, timedelta, timezone
from pathlib import Path
from types import FunctionType
from unittest.mock import MagicMock
import arrow
import pytest
from datetime import datetime, timedelta
from math import isclose
from sqlalchemy import create_engine, inspect, text
from freqtrade import constants
import pytest
from freqtrade.enums import InterestMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db
from tests.conftest import create_mock_trades_with_leverage, log_has, log_has_re
from freqtrade.persistence import Trade
from tests.conftest import log_has_re
@pytest.mark.usefixtures("init_persistence")
def test_interest_kraken_lev(market_leveraged_buy_order, fee):
def test_interest_kraken_lev(market_lev_buy_order, fee):
"""
Market trade on Kraken at 3x and 5x leverage
Short trade
@ -54,10 +48,10 @@ def test_interest_kraken_lev(market_leveraged_buy_order, fee):
interest_mode=InterestMode.HOURSPER4
)
# 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
# 10 minutes round up to 4 hours evenly on kraken so we can predict the exact 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
# All trade > 5 hours will vary slightly due to execution time and interest calculated
assert float(round(trade.calculate_interest(interest_rate=0.00025), 11)
) == round(2.3567152011391876e-06, 11)
@ -82,7 +76,7 @@ def test_interest_kraken_lev(market_leveraged_buy_order, fee):
@pytest.mark.usefixtures("init_persistence")
def test_interest_binance_lev(market_leveraged_buy_order, fee):
def test_interest_binance_lev(market_lev_buy_order, fee):
"""
Market trade on Kraken at 3x and 5x leverage
Short trade
@ -120,10 +114,10 @@ def test_interest_binance_lev(market_leveraged_buy_order, fee):
interest_rate=0.0005,
interest_mode=InterestMode.HOURSPERDAY
)
# 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
# 10 minutes round up to 4 hours evenly on kraken so we can predict the them more accurately
assert round(float(trade.calculate_interest()), 22) == round(4.166666666344583e-08, 22)
trade.open_date = datetime.utcnow() - timedelta(hours=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
# All trade > 5 hours will vary slightly due to execution time and interest calculated
assert float(round(trade.calculate_interest(interest_rate=0.00025), 14)
) == round(1.0416666665861459e-07, 14)
@ -148,7 +142,7 @@ def test_interest_binance_lev(market_leveraged_buy_order, fee):
@pytest.mark.usefixtures("init_persistence")
def test_update_open_order_lev(limit_leveraged_buy_order):
def test_update_open_order_lev(limit_lev_buy_order):
trade = Trade(
pair='ETH/BTC',
stake_amount=1.00,
@ -163,15 +157,15 @@ def test_update_open_order_lev(limit_leveraged_buy_order):
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)
limit_lev_buy_order['status'] = 'open'
trade.update(limit_lev_buy_order)
assert trade.open_order_id is None
assert trade.close_profit is None
assert trade.close_date is None
@pytest.mark.usefixtures("init_persistence")
def test_calc_open_trade_value_lev(market_leveraged_buy_order, fee):
def test_calc_open_trade_value_lev(market_lev_buy_order, fee):
"""
10 minute leveraged market trade on Kraken at 3x leverage
Short trade
@ -203,7 +197,7 @@ def test_calc_open_trade_value_lev(market_leveraged_buy_order, fee):
interest_mode=InterestMode.HOURSPER4
)
trade.open_order_id = 'open_trade'
trade.update(market_leveraged_buy_order) # Buy @ 0.00001099
trade.update(market_lev_buy_order) # Buy @ 0.00001099
# Get the open rate price with the standard fee rate
assert trade._calc_open_trade_value() == 0.01134051354788177
trade.fee_open = 0.003
@ -212,7 +206,7 @@ def test_calc_open_trade_value_lev(market_leveraged_buy_order, fee):
@pytest.mark.usefixtures("init_persistence")
def test_calc_open_close_trade_price_lev(limit_leveraged_buy_order, limit_leveraged_sell_order, fee):
def test_calc_open_close_trade_price_lev(limit_lev_buy_order, limit_lev_sell_order, fee):
"""
5 hour leveraged trade on Binance
@ -230,7 +224,9 @@ def test_calc_open_close_trade_price_lev(limit_leveraged_buy_order, limit_levera
= (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
= (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
@ -252,11 +248,11 @@ def test_calc_open_close_trade_price_lev(limit_leveraged_buy_order, limit_levera
interest_mode=InterestMode.HOURSPERDAY
)
trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
trade.update(limit_lev_buy_order)
assert trade._calc_open_trade_value() == 0.00300749999976752
trade.update(limit_leveraged_sell_order)
trade.update(limit_lev_sell_order)
# Will be slightly different due to slight changes in compilation time, and the fact that interest depends on time
# Is slightly different due to compilation time changes. Interest depends on time
assert round(trade.calc_close_trade_value(), 11) == round(0.003193788481706411, 11)
# Profit in BTC
assert round(trade.calc_profit(), 8) == round(0.00018628848193889054, 8)
@ -281,7 +277,7 @@ def test_trade_close_lev(fee):
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
close_value: (amount * close_rate) + (amount * close_rate * fee) - interest
= (15 * 0.2) - (15 * 0.2 * 0.0025) - 0.000625
= 2.9918750000000003
total_profit = close_value - open_value
@ -324,7 +320,7 @@ def test_trade_close_lev(fee):
@pytest.mark.usefixtures("init_persistence")
def test_calc_close_trade_price_lev(market_leveraged_buy_order, market_leveraged_sell_order, fee):
def test_calc_close_trade_price_lev(market_lev_buy_order, market_lev_sell_order, fee):
"""
10 minute leveraged market trade on Kraken at 3x leverage
Short trade
@ -337,15 +333,17 @@ def test_calc_close_trade_price_lev(market_leveraged_buy_order, market_leveraged
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
= 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
= (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',
@ -361,18 +359,18 @@ def test_calc_close_trade_price_lev(market_leveraged_buy_order, market_leveraged
interest_mode=InterestMode.HOURSPER4
)
trade.open_order_id = 'close_trade'
trade.update(market_leveraged_buy_order) # Buy @ 0.00001099
trade.update(market_lev_buy_order) # Buy @ 0.00001099
# Get the close rate price with a custom close rate and a regular fee rate
assert isclose(trade.calc_close_trade_value(rate=0.00001234), 0.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)
trade.update(market_lev_sell_order)
assert isclose(trade.calc_close_trade_value(fee=0.005), 0.011455101767040435)
@pytest.mark.usefixtures("init_persistence")
def test_update_limit_order_lev(limit_leveraged_buy_order, limit_leveraged_sell_order, fee, caplog):
def test_update_limit_order_lev(limit_lev_buy_order, limit_lev_sell_order, fee, caplog):
"""
10 minute leveraged limit trade on binance at 3x leverage
@ -420,7 +418,7 @@ def test_update_limit_order_lev(limit_leveraged_buy_order, limit_leveraged_sell_
assert trade.close_date is None
# trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
trade.update(limit_lev_buy_order)
# assert trade.open_order_id is None
assert trade.open_rate == 0.00001099
assert trade.close_profit is None
@ -431,7 +429,7 @@ def test_update_limit_order_lev(limit_leveraged_buy_order, limit_leveraged_sell_
caplog)
caplog.clear()
# trade.open_order_id = 'something'
trade.update(limit_leveraged_sell_order)
trade.update(limit_lev_sell_order)
# assert trade.open_order_id is None
assert trade.close_rate == 0.00001173
assert trade.close_profit == round(0.18645514861995735, 8)
@ -442,7 +440,7 @@ def test_update_limit_order_lev(limit_leveraged_buy_order, limit_leveraged_sell_
@pytest.mark.usefixtures("init_persistence")
def test_update_market_order_lev(market_leveraged_buy_order, market_leveraged_sell_order, fee, caplog):
def test_update_market_order_lev(market_lev_buy_order, market_lev_sell_order, fee, caplog):
"""
10 minute leveraged market trade on Kraken at 3x leverage
Short trade
@ -484,7 +482,7 @@ def test_update_market_order_lev(market_leveraged_buy_order, market_leveraged_se
interest_mode=InterestMode.HOURSPER4
)
trade.open_order_id = 'something'
trade.update(market_leveraged_buy_order)
trade.update(market_lev_buy_order)
assert trade.leverage == 3.0
assert trade.open_order_id is None
assert trade.open_rate == 0.00004099
@ -499,7 +497,7 @@ def test_update_market_order_lev(market_leveraged_buy_order, market_leveraged_se
caplog.clear()
trade.is_open = True
trade.open_order_id = 'something'
trade.update(market_leveraged_sell_order)
trade.update(market_lev_sell_order)
assert trade.open_order_id is None
assert trade.close_rate == 0.00004173
assert trade.close_profit == round(0.03802415223225211, 8)
@ -513,7 +511,7 @@ def test_update_market_order_lev(market_leveraged_buy_order, market_leveraged_se
@pytest.mark.usefixtures("init_persistence")
def test_calc_close_trade_price_exception_lev(limit_leveraged_buy_order, fee):
def test_calc_close_trade_price_exception_lev(limit_lev_buy_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
@ -527,14 +525,13 @@ def test_calc_close_trade_price_exception_lev(limit_leveraged_buy_order, fee):
interest_mode=InterestMode.HOURSPERDAY
)
trade.open_order_id = 'something'
trade.update(limit_leveraged_buy_order)
trade.update(limit_lev_buy_order)
assert trade.calc_close_trade_value() == 0.0
@pytest.mark.usefixtures("init_persistence")
def test_calc_profit_lev(market_leveraged_buy_order, market_leveraged_sell_order, fee):
def test_calc_profit_lev(market_lev_buy_order, market_lev_sell_order, fee):
"""
# TODO: Update this one
Leveraged trade on Kraken at 3x leverage
fee: 0.25% base or 0.3%
interest_rate: 0.05%, 0.25% per 4 hrs
@ -547,17 +544,22 @@ def test_calc_profit_lev(market_leveraged_buy_order, market_leveraged_sell_order
5 hours = 5/4
interest: borrowed * interest_rate * time-periods
= 0.0075414886436454 * 0.0005 * 1 = 3.7707443218227e-06 crypto
= 0.0075414886436454 * 0.00025 * 5/4 = 2.3567152011391876e-06 crypto
= 0.0075414886436454 * 0.0005 * 5/4 = 4.713430402278375e-06 crypto
= 0.0075414886436454 * 0.00025 * 1 = 1.88537216091135e-06 crypto
= 0.0075414886436454 * 0.0005 * 1 = 3.7707443218227e-06 crypto
= 0.0075414886436454 * 0.00025 * 5/4 = 2.3567152011391876e-06 crypto
= 0.0075414886436454 * 0.0005 * 5/4 = 4.713430402278375e-06 crypto
= 0.0075414886436454 * 0.00025 * 1 = 1.88537216091135e-06 crypto
open_value: (amount * open_rate) + (amount * open_rate * fee)
= (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025) = 0.01134051354788177
= (275.97543219 * 0.00004099) + (275.97543219 * 0.00004099 * 0.0025)
= 0.01134051354788177
close_value: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest
(275.97543219 * 0.00005374) - (275.97543219 * 0.00005374 * 0.0025) - 3.7707443218227e-06 = 0.01479007168225405
(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) - 4.713430402278375e-06 = 0.014781713536310649
(275.97543219 * 0.00000437) - (275.97543219 * 0.00000437 * 0.003) - 1.88537216091135e-06 = 0.0012005092285933775
(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) - 2.3567152011391876e-06
= 0.001200640891872485
(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) - 1.88537216091135e-06
= 0.0012005092285933775
total_profit = close_value - open_value
= 0.01479007168225405 - 0.01134051354788177 = 0.003449558134372281
= 0.001200640891872485 - 0.01134051354788177 = -0.010139872656009285
@ -584,7 +586,7 @@ def test_calc_profit_lev(market_leveraged_buy_order, market_leveraged_sell_order
interest_mode=InterestMode.HOURSPER4
)
trade.open_order_id = 'something'
trade.update(market_leveraged_buy_order) # Buy @ 0.00001099
trade.update(market_lev_buy_order) # Buy @ 0.00001099
# Custom closing rate and regular fee rate
# Higher than open rate
@ -615,7 +617,7 @@ def test_calc_profit_lev(market_leveraged_buy_order, market_leveraged_sell_order
interest_rate=0.00025) == round(-2.6891253964381554, 8)
# Test when we apply a Sell order. Sell higher than open rate @ 0.00001173
trade.update(market_leveraged_sell_order)
trade.update(market_lev_sell_order)
assert trade.calc_profit() == round(0.0001433793561218866, 8)
assert trade.calc_profit_ratio() == round(0.03802415223225211, 8)

View File

@ -1,17 +1,12 @@
import logging
from datetime import datetime, timedelta, timezone
from pathlib import Path
from types import FunctionType
from unittest.mock import MagicMock
from datetime import datetime, timedelta
from math import isclose
import arrow
import pytest
from math import isclose
from sqlalchemy import create_engine, inspect, text
from freqtrade import constants
from freqtrade.enums import InterestMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db
from tests.conftest import create_mock_trades_with_leverage, log_has, log_has_re
from freqtrade.persistence import Trade, init_db
from tests.conftest import create_mock_trades_with_leverage, log_has_re
@pytest.mark.usefixtures("init_persistence")
@ -302,11 +297,12 @@ def test_calc_open_close_trade_price_short(limit_short_order, limit_exit_short_o
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
# Is slightly different due to compilation time. Interest depends on time
assert round(trade.calc_close_trade_value(), 11) == round(0.001002604427005832, 11)
# Profit in BTC
assert round(trade.calc_profit(), 8) == round(0.00006206117800741065, 8)
# Profit in percent
# TODO-mg get this working
# assert round(trade.calc_profit_ratio(), 11) == round(0.05822425142973869, 11)
@ -499,7 +495,7 @@ def test_update_market_order_short(
trade.open_order_id = 'something'
trade.update(market_short_order)
assert trade.leverage == 3.0
assert trade.is_short == True
assert trade.is_short is True
assert trade.open_order_id is None
assert trade.open_rate == 0.00004173
assert trade.close_profit is None
@ -546,17 +542,22 @@ def test_calc_profit_short(market_short_order, market_exit_short_order, fee):
= 275.97543219 * 0.0005 * 5/4 = 0.17248464511875 crypto
= 275.97543219 * 0.00025 * 1 = 0.0689938580475 crypto
open_value: (amount * open_rate) - (amount * open_rate * fee)
= (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.0025) = 0.011487663648325479
= (275.97543219 * 0.00004173) - (275.97543219 * 0.00004173 * 0.0025)
= 0.011487663648325479
amount_closed: amount + interest
= 275.97543219 + 0.137987716095 = 276.113419906095
= 275.97543219 + 0.086242322559375 = 276.06167451255936
= 275.97543219 + 0.17248464511875 = 276.14791683511874
= 275.97543219 + 0.0689938580475 = 276.0444260480475
close_value: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
(276.113419906095 * 0.00004374) + (276.113419906095 * 0.00004374 * 0.0025) = 0.012107393989159325
(276.06167451255936 * 0.00000437) + (276.06167451255936 * 0.00000437 * 0.0025) = 0.0012094054914139338
(276.14791683511874 * 0.00004374) + (276.14791683511874 * 0.00004374 * 0.003) = 0.012114946012015198
(276.0444260480475 * 0.00000437) + (276.0444260480475 * 0.00000437 * 0.003) = 0.0012099330842554573
(276.113419906095 * 0.00004374) + (276.113419906095 * 0.00004374 * 0.0025)
= 0.012107393989159325
(276.06167451255936 * 0.00000437) + (276.06167451255936 * 0.00000437 * 0.0025)
= 0.0012094054914139338
(276.14791683511874 * 0.00004374) + (276.14791683511874 * 0.00004374 * 0.003)
= 0.012114946012015198
(276.0444260480475 * 0.00000437) + (276.0444260480475 * 0.00000437 * 0.003)
= 0.0012099330842554573
total_profit = open_value - close_value
= print(0.011487663648325479 - 0.012107393989159325) = -0.0006197303408338461
= print(0.011487663648325479 - 0.0012094054914139338) = 0.010278258156911545
@ -647,7 +648,8 @@ def test_adjust_stop_loss_short(fee):
assert trade.initial_stop_loss_pct == 0.05
# Get percent of profit with a custom rate (Higher than open rate)
trade.adjust_stop_loss(0.7, 0.1)
# If the price goes down to 0.7, with a trailing stop of 0.1, the new stoploss at 0.1 above 0.7 would be 0.7*0.1 higher
# If the price goes down to 0.7, with a trailing stop of 0.1,
# the new stoploss at 0.1 above 0.7 would be 0.7*0.1 higher
assert round(trade.stop_loss, 8) == 0.77
assert trade.stop_loss_pct == 0.1
assert trade.initial_stop_loss == 1.05