merge with feat/short interest changes
This commit is contained in:
@@ -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
|
||||
|
@@ -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")
|
@@ -1,2 +1,3 @@
|
||||
# flake8: noqa: F401
|
||||
from freqtrade.leverage.interest import interest
|
||||
from freqtrade.leverage.liquidation_price import liquidation_price
|
||||
|
42
freqtrade/leverage/interest.py
Normal file
42
freqtrade/leverage/interest.py
Normal 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")
|
@@ -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}
|
||||
"""))
|
||||
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user