mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
New funding fee methods
This commit is contained in:
parent
b854350e8d
commit
d6d5bae2a1
|
@ -20,7 +20,6 @@ from freqtrade.enums import RPCMessageType, SellType, State, TradingMode
|
|||
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
|
||||
InvalidOrderException, PricingError)
|
||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||
from freqtrade.leverage.funding_fee import FundingFee
|
||||
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
|
||||
from freqtrade.mixins import LoggingMixin
|
||||
from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db
|
||||
|
@ -103,10 +102,10 @@ class FreqtradeBot(LoggingMixin):
|
|||
self._sell_lock = Lock()
|
||||
LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe))
|
||||
|
||||
self.trading_mode = self.config['trading_mode']
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
self.funding_fee = FundingFee()
|
||||
self.funding_fee.start()
|
||||
if 'trading_mode' in self.config:
|
||||
self.trading_mode = self.config['trading_mode']
|
||||
else:
|
||||
self.trading_mode = TradingMode.SPOT
|
||||
|
||||
def notify_status(self, msg: str) -> None:
|
||||
"""
|
||||
|
@ -243,9 +242,10 @@ class FreqtradeBot(LoggingMixin):
|
|||
open_trades = len(Trade.get_open_trades())
|
||||
return max(0, self.config['max_open_trades'] - open_trades)
|
||||
|
||||
def get_funding_fees():
|
||||
def add_funding_fees(self, trade: Trade):
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
return
|
||||
funding_fees = self.exchange.get_funding_fees(trade.pair, trade.open_date)
|
||||
trade.funding_fees = funding_fees
|
||||
|
||||
def update_open_orders(self):
|
||||
"""
|
||||
|
@ -262,6 +262,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
try:
|
||||
fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair,
|
||||
order.ft_order_side == 'stoploss')
|
||||
|
||||
self.update_trade_state(order.trade, order.order_id, fo)
|
||||
|
||||
except ExchangeError as e:
|
||||
|
@ -568,10 +569,6 @@ class FreqtradeBot(LoggingMixin):
|
|||
amount = safe_value_fallback(order, 'filled', 'amount')
|
||||
buy_limit_filled_price = safe_value_fallback(order, 'average', 'price')
|
||||
|
||||
funding_fee = (self.funding_fee.initial_funding_fee(amount)
|
||||
if self.trading_mode == TradingMode.FUTURES
|
||||
else None)
|
||||
|
||||
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
||||
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
||||
trade = Trade(
|
||||
|
@ -590,14 +587,10 @@ class FreqtradeBot(LoggingMixin):
|
|||
strategy=self.strategy.get_strategy_name(),
|
||||
buy_tag=buy_tag,
|
||||
timeframe=timeframe_to_minutes(self.config['timeframe']),
|
||||
funding_fee=funding_fee,
|
||||
trading_mode=self.trading_mode
|
||||
)
|
||||
trade.orders.append(order_obj)
|
||||
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
self.funding_fee.add_new_trade(trade)
|
||||
|
||||
# Update fees if order is closed
|
||||
if order_status == 'closed':
|
||||
self.update_trade_state(trade, order_id, order)
|
||||
|
|
|
@ -61,8 +61,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||
interest_rate = get_column_def(cols, 'interest_rate', '0.0')
|
||||
|
||||
# Futures properties
|
||||
funding_fee = get_column_def(cols, 'funding_fee', '0.0')
|
||||
last_funding_adjustment = get_column_def(cols, 'last_funding_adjustment', 'null')
|
||||
funding_fees = get_column_def(cols, 'funding_fees', '0.0')
|
||||
|
||||
# If ticker-interval existed use that, else null.
|
||||
if has_column(cols, 'ticker_interval'):
|
||||
|
@ -102,7 +101,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||
max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag,
|
||||
timeframe, open_trade_value, close_profit_abs,
|
||||
trading_mode, leverage, isolated_liq, is_short,
|
||||
interest_rate, funding_fee, last_funding_adjustment
|
||||
interest_rate, funding_fees
|
||||
)
|
||||
select id, lower(exchange), pair,
|
||||
is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost,
|
||||
|
@ -121,7 +120,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
|
||||
{trading_mode} trading_mode, {leverage} leverage, {isolated_liq} isolated_liq,
|
||||
{is_short} is_short, {interest_rate} interest_rate,
|
||||
{funding_fee} funding_fee, {last_funding_adjustment} last_funding_adjustment
|
||||
{funding_fees} funding_fees
|
||||
from {table_back_name}
|
||||
"""))
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
This module contains the class to persist trades into SQLite
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
@ -16,7 +16,7 @@ from sqlalchemy.sql.schema import UniqueConstraint
|
|||
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
|
||||
from freqtrade.enums import SellType, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.leverage.interest import interest
|
||||
from freqtrade.leverage import interest
|
||||
from freqtrade.misc import safe_value_fallback
|
||||
from freqtrade.persistence.migrations import check_migrate
|
||||
|
||||
|
@ -57,7 +57,7 @@ def init_db(db_url: str, clean_open_orders: bool = False) -> None:
|
|||
f"is no valid database URL! (See {_SQL_DOCS_URL})")
|
||||
|
||||
# https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
|
||||
# Scoped sessions proxy reque sts to the appropriate thread-local session.
|
||||
# Scoped sessions proxy requests to the appropriate thread-local session.
|
||||
# We should use the scoped_session object - not a seperately initialized version
|
||||
Trade._session = scoped_session(sessionmaker(bind=engine, autoflush=True))
|
||||
Trade.query = Trade._session.query_property()
|
||||
|
@ -93,12 +93,6 @@ def clean_dry_run_db() -> None:
|
|||
Trade.commit()
|
||||
|
||||
|
||||
def hour_rounder(t):
|
||||
# Rounds to nearest hour by adding a timedelta hour if minute >= 30
|
||||
return (
|
||||
t.replace(second=0, microsecond=0, minute=0, hour=t.hour) + timedelta(hours=t.minute//30))
|
||||
|
||||
|
||||
class Order(_DECL_BASE):
|
||||
"""
|
||||
Order database model
|
||||
|
@ -282,8 +276,7 @@ class LocalTrade():
|
|||
interest_rate: float = 0.0
|
||||
|
||||
# Futures properties
|
||||
funding_fee: Optional[float] = None
|
||||
last_funding_adjustment: Optional[datetime] = None
|
||||
funding_fees: Optional[float] = None
|
||||
|
||||
@property
|
||||
def has_no_leverage(self) -> bool:
|
||||
|
@ -451,9 +444,7 @@ class LocalTrade():
|
|||
'isolated_liq': self.isolated_liq,
|
||||
'is_short': self.is_short,
|
||||
'trading_mode': self.trading_mode,
|
||||
'funding_fee': self.funding_fee,
|
||||
'last_funding_adjustment': (self.last_funding_adjustment.strftime(DATETIME_PRINT_FORMAT)
|
||||
if self.last_funding_adjustment else None),
|
||||
'funding_fees': self.funding_fees,
|
||||
'open_order_id': self.open_order_id,
|
||||
}
|
||||
|
||||
|
@ -531,10 +522,6 @@ class LocalTrade():
|
|||
f"Trailing stoploss saved us: "
|
||||
f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.")
|
||||
|
||||
def adjust_funding_fee(self, adjustment):
|
||||
self.funding_fee = self.funding_fee + adjustment
|
||||
self.last_funding_adjustment = datetime.utcnow()
|
||||
|
||||
def update(self, order: Dict) -> None:
|
||||
"""
|
||||
Updates this entity with amount and actual open/close rates.
|
||||
|
@ -673,7 +660,6 @@ class LocalTrade():
|
|||
rate = Decimal(interest_rate or self.interest_rate)
|
||||
borrowed = Decimal(self.borrowed)
|
||||
|
||||
# TODO-lev: Pass trading mode to interest maybe
|
||||
return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours)
|
||||
|
||||
def _calc_base_close(self, amount: Decimal, rate: Optional[float] = None,
|
||||
|
@ -721,11 +707,8 @@ class LocalTrade():
|
|||
return float(self._calc_base_close(amount, rate, fee) - total_interest)
|
||||
|
||||
elif (trading_mode == TradingMode.FUTURES):
|
||||
funding_fee = self.funding_fee or 0.0
|
||||
if self.is_short:
|
||||
return float(self._calc_base_close(amount, rate, fee)) + funding_fee
|
||||
else:
|
||||
return float(self._calc_base_close(amount, rate, fee)) - funding_fee
|
||||
funding_fees = self.funding_fees or 0.0
|
||||
return float(self._calc_base_close(amount, rate, fee)) + funding_fees
|
||||
else:
|
||||
raise OperationalException(
|
||||
f"{self.trading_mode.value} trading is not yet available using freqtrade")
|
||||
|
@ -938,16 +921,16 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||
|
||||
trading_mode = Column(Enum(TradingMode))
|
||||
|
||||
# Leverage trading properties
|
||||
leverage = Column(Float, nullable=True, default=1.0)
|
||||
isolated_liq = Column(Float, nullable=True)
|
||||
is_short = Column(Boolean, nullable=False, default=False)
|
||||
isolated_liq = Column(Float, nullable=True)
|
||||
|
||||
# Margin properties
|
||||
# Margin Trading Properties
|
||||
interest_rate = Column(Float, nullable=False, default=0.0)
|
||||
|
||||
# Futures properties
|
||||
funding_fee = Column(Float, nullable=True, default=None)
|
||||
last_funding_adjustment = Column(DateTime, nullable=True)
|
||||
funding_fees = Column(Float, nullable=True, default=None)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
@ -3,7 +3,7 @@ from math import isclose
|
|||
|
||||
import pytest
|
||||
|
||||
from freqtrade.leverage.interest import interest
|
||||
from freqtrade.leverage import interest
|
||||
|
||||
|
||||
ten_mins = Decimal(1/6)
|
||||
|
|
|
@ -8,7 +8,7 @@ import pytest
|
|||
from numpy import isnan
|
||||
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import State
|
||||
from freqtrade.enums import State, TradingMode
|
||||
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.persistence.pairlock_middleware import PairLocks
|
||||
|
@ -112,6 +112,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||
'interest_rate': 0.0,
|
||||
'isolated_liq': None,
|
||||
'is_short': False,
|
||||
'funding_fees': None,
|
||||
'trading_mode': TradingMode.SPOT
|
||||
}
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
|
@ -183,6 +185,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||
'interest_rate': 0.0,
|
||||
'isolated_liq': None,
|
||||
'is_short': False,
|
||||
'funding_fees': None,
|
||||
'trading_mode': TradingMode.SPOT
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1724,8 +1724,7 @@ def test_to_json(default_conf, fee):
|
|||
'isolated_liq': None,
|
||||
'is_short': None,
|
||||
'trading_mode': None,
|
||||
'funding_fee': None,
|
||||
'last_funding_adjustment': None
|
||||
'funding_fees': None,
|
||||
}
|
||||
|
||||
# Simulate dry_run entries
|
||||
|
@ -1798,8 +1797,7 @@ def test_to_json(default_conf, fee):
|
|||
'isolated_liq': None,
|
||||
'is_short': None,
|
||||
'trading_mode': None,
|
||||
'funding_fee': None,
|
||||
'last_funding_adjustment': None
|
||||
'funding_fees': None,
|
||||
}
|
||||
|
||||
|
||||
|
@ -2219,7 +2217,6 @@ def test_Trade_object_idem():
|
|||
'get_open_trades_without_assigned_fees',
|
||||
'get_open_order_trades',
|
||||
'get_trades',
|
||||
'last_funding_adjustment'
|
||||
)
|
||||
|
||||
# Parent (LocalTrade) should have the same attributes
|
||||
|
|
Loading…
Reference in New Issue
Block a user