merge with feat/short interest changes

This commit is contained in:
Sam Germain
2021-08-11 01:55:31 -06:00
9 changed files with 172 additions and 136 deletions

View File

@@ -1,7 +1,6 @@
# flake8: noqa: F401
from freqtrade.enums.backteststate import BacktestState
from freqtrade.enums.collateral import Collateral
from freqtrade.enums.interestmode import InterestMode
from freqtrade.enums.rpcmessagetype import RPCMessageType
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
from freqtrade.enums.selltype import SellType

View File

@@ -1,28 +0,0 @@
from decimal import Decimal
from enum import Enum
from math import ceil
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, borrowed: Decimal, rate: Decimal, hours: Decimal):
if self.name == "HOURSPERDAY":
return borrowed * rate * ceil(hours)/twenty_four
elif self.name == "HOURSPER4":
# Rounded based on https://kraken-fees-calculator.github.io/
return borrowed * rate * (1+ceil(hours/four))
else:
raise OperationalException("Leverage not available on this exchange with freqtrade")

View File

@@ -1,2 +1,3 @@
# flake8: noqa: F401
from freqtrade.leverage.interest import interest
from freqtrade.leverage.liquidation_price import liquidation_price

View File

@@ -0,0 +1,42 @@
from decimal import Decimal
from math import ceil
from freqtrade.exceptions import OperationalException
one = Decimal(1.0)
four = Decimal(4.0)
twenty_four = Decimal(24.0)
def interest(
exchange_name: str,
borrowed: Decimal,
rate: Decimal,
hours: Decimal
) -> Decimal:
"""
Equation to calculate interest on margin trades
:param exchange_name: The exchanged being trading on
:param borrowed: The amount of currency being borrowed
:param rate: The rate of interest
:param hours: The time in hours that the currency has been borrowed for
Raises:
OperationalException: Raised if freqtrade does
not support margin trading for this exchange
Returns: The amount of interest owed (currency matches borrowed)
"""
exchange_name = exchange_name.lower()
if exchange_name == "binance":
return borrowed * rate * ceil(hours)/twenty_four
elif exchange_name == "kraken":
# Rounded based on https://kraken-fees-calculator.github.io/
return borrowed * rate * (one+ceil(hours/four))
elif exchange_name == "ftx":
# TODO-lev: Add FTX interest formula
raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade")
else:
raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade")

View File

@@ -54,7 +54,6 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
isolated_liq = get_column_def(cols, 'isolated_liq', 'null')
# sqlite does not support literals for booleans
is_short = get_column_def(cols, 'is_short', '0')
interest_mode = get_column_def(cols, 'interest_mode', 'null')
# If ticker-interval existed use that, else null.
if has_column(cols, 'ticker_interval'):
timeframe = get_column_def(cols, 'timeframe', 'ticker_interval')
@@ -66,7 +65,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
close_profit_abs = get_column_def(
cols, 'close_profit_abs',
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_value}")
# TODO-mg: update to exit order status
# TODO-lev: update to exit order status
sell_order_status = get_column_def(cols, 'sell_order_status', 'null')
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
@@ -92,7 +91,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag,
timeframe, open_trade_value, close_profit_abs,
leverage, interest_rate, isolated_liq, is_short, interest_mode
leverage, interest_rate, isolated_liq, is_short
)
select id, lower(exchange), pair,
is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost,
@@ -110,8 +109,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
{strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe,
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
{leverage} leverage, {interest_rate} interest_rate,
{isolated_liq} isolated_liq, {is_short} is_short,
{interest_mode} interest_mode
{isolated_liq} isolated_liq, {is_short} is_short
from {table_back_name}
"""))

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, Enum, Float, ForeignKey, Integer, String,
from sqlalchemy import (Boolean, Column, DateTime, 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
@@ -14,9 +14,9 @@ from sqlalchemy.pool import StaticPool
from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.enums import InterestMode, SellType
from freqtrade.enums import SellType
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.leverage import liquidation_price
from freqtrade.leverage import interest, liquidation_price
from freqtrade.misc import safe_value_fallback
from freqtrade.persistence.migrations import check_migrate
@@ -265,12 +265,13 @@ class LocalTrade():
buy_tag: Optional[str] = None
timeframe: Optional[int] = None
# Leverage trading properties
is_short: bool = False
isolated_liq: Optional[float] = None
leverage: float = 1.0
# Margin trading properties
interest_rate: float = 0.0
isolated_liq: Optional[float] = None
is_short: bool = False
leverage: float = 1.0
interest_mode: InterestMode = InterestMode.NONE
@property
def has_no_leverage(self) -> bool:
@@ -480,12 +481,12 @@ class LocalTrade():
if self.is_short:
new_loss = float(current_price * (1 + abs(stoploss)))
# If trading on margin, don't set the stoploss below the liquidation price
# If trading with leverage, don't set the stoploss below the liquidation price
if self.isolated_liq:
new_loss = min(self.isolated_liq, new_loss)
else:
new_loss = float(current_price * (1 - abs(stoploss)))
# If trading on margin, don't set the stoploss below the liquidation price
# If trading with leverage, don't set the stoploss below the liquidation price
if self.isolated_liq:
new_loss = max(self.isolated_liq, new_loss)
@@ -506,7 +507,8 @@ class LocalTrade():
lower_stop = 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,
# TODO-lev
# ? But adding more to a leveraged trade would create a lower liquidation price,
# ? decreasing the minimum stoploss
if (higher_stop and not self.is_short) or (lower_stop and self.is_short):
logger.debug(f"{self.pair} - Adjusting stoploss...")
@@ -554,10 +556,11 @@ class LocalTrade():
elif order_type in ('market', 'limit') and self.exit_side == order['side']:
if self.is_open:
payment = "BUY" if self.is_short else "SELL"
# TODO-mg: On shorts, you buy a little bit more than the amount (amount + interest)
# TODO-lev: 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-mg: Double check this
# TODO-lev: Double check this
self.close(safe_value_fallback(order, 'average', 'price'))
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
@@ -659,7 +662,7 @@ class LocalTrade():
rate = Decimal(interest_rate or self.interest_rate)
borrowed = Decimal(self.borrowed)
return self.interest_mode(borrowed=borrowed, rate=rate, hours=hours)
return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours)
def calc_close_trade_value(self, rate: Optional[float] = None,
fee: Optional[float] = None,
@@ -892,19 +895,19 @@ class Trade(_DECL_BASE, LocalTrade):
max_rate = Column(Float, nullable=True, default=0.0)
# Lowest price reached
min_rate = Column(Float, nullable=True)
sell_reason = Column(String(100), nullable=True) # TODO-mg: Change to close_reason
sell_order_status = Column(String(100), nullable=True) # TODO-mg: Change to close_order_status
sell_reason = Column(String(100), nullable=True) # TODO-lev: Change to close_reason
sell_order_status = Column(String(100), nullable=True) # TODO-lev: Change to close_order_status
strategy = Column(String(100), nullable=True)
buy_tag = Column(String(100), nullable=True)
timeframe = Column(Integer, nullable=True)
# Margin trading properties
# Leverage trading properties
leverage = Column(Float, nullable=True, default=1.0)
interest_rate = Column(Float, nullable=False, default=0.0)
isolated_liq = Column(Float, nullable=True)
is_short = Column(Boolean, nullable=False, default=False)
interest_mode = Column(Enum(InterestMode), nullable=True)
# End of margin trading properties
isolated_liq = Column(Float, nullable=True)
# Margin Trading Properties
interest_rate = Column(Float, nullable=False, default=0.0)
def __init__(self, **kwargs):
super().__init__(**kwargs)