Skip to content

Commit

Permalink
Merge pull request freqtrade#9298 from freqtrade/funding_fees
Browse files Browse the repository at this point in the history
improve Funding fee behavior
  • Loading branch information
xmatthias committed Oct 13, 2023
2 parents 2f07971 + 042e35e commit fa174a3
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 22 deletions.
6 changes: 2 additions & 4 deletions freqtrade/freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def update():
# TODO: This would be more efficient if scheduled in utc time, and performed at each
# TODO: funding interval, specified by funding_fee_times on the exchange classes
for time_slot in range(0, 24):
for minutes in [0, 15, 30, 45]:
for minutes in [1, 31]:
t = str(time(time_slot, minutes, 2))
self._schedule.every().day.at(t).do(update)
self.last_process: Optional[datetime] = None
Expand Down Expand Up @@ -199,6 +199,7 @@ def startup(self) -> None:
# Only update open orders on startup
# This will update the database after the initial migration
self.startup_update_open_orders()
self.update_funding_fees()

def process(self) -> None:
"""
Expand Down Expand Up @@ -378,9 +379,6 @@ def startup_update_open_orders(self):

logger.warning(f"Error updating Order {order.order_id} due to {e}")

if self.trading_mode == TradingMode.FUTURES:
self._schedule.run_pending()

def update_trades_without_assigned_fees(self) -> None:
"""
Update closed trades without close fees assigned.
Expand Down
10 changes: 6 additions & 4 deletions freqtrade/persistence/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def migrate_trades_and_orders_table(
# Futures Properties
interest_rate = get_column_def(cols, 'interest_rate', '0.0')
funding_fees = get_column_def(cols, 'funding_fees', '0.0')
funding_fee_running = get_column_def(cols, 'funding_fee_running', 'null')
max_stake_amount = get_column_def(cols, 'max_stake_amount', 'stake_amount')

# If ticker-interval existed use that, else null.
Expand Down Expand Up @@ -163,7 +164,7 @@ def migrate_trades_and_orders_table(
max_rate, min_rate, exit_reason, exit_order_status, strategy, enter_tag,
timeframe, open_trade_value, close_profit_abs,
trading_mode, leverage, liquidation_price, is_short,
interest_rate, funding_fees, realized_profit,
interest_rate, funding_fees, funding_fee_running, realized_profit,
amount_precision, price_precision, precision_mode, contract_size,
max_stake_amount
)
Expand Down Expand Up @@ -192,7 +193,8 @@ def migrate_trades_and_orders_table(
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
{trading_mode} trading_mode, {leverage} leverage, {liquidation_price} liquidation_price,
{is_short} is_short, {interest_rate} interest_rate,
{funding_fees} funding_fees, {realized_profit} realized_profit,
{funding_fees} funding_fees, {funding_fee_running} funding_fee_running,
{realized_profit} realized_profit,
{amount_precision} amount_precision, {price_precision} price_precision,
{precision_mode} precision_mode, {contract_size} contract_size,
{max_stake_amount} max_stake_amount
Expand Down Expand Up @@ -329,8 +331,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
# if ('orders' not in previous_tables
# or not has_column(cols_orders, 'funding_fee')):
migrating = False
# if not has_column(cols_trades, 'is_stop_loss_trailing'):
if not has_column(cols_orders, 'ft_cancel_reason'):
# if not has_column(cols_orders, 'ft_cancel_reason'):
if not has_column(cols_trades, 'funding_fee_running'):
migrating = True
logger.info(f"Running database migration for trades - "
f"backup: {table_back_name}, {order_table_bak_name}")
Expand Down
16 changes: 13 additions & 3 deletions freqtrade/persistence/trade_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ def close_bt_order(self, close_date: datetime, trade: 'LocalTrade'):
self.ft_is_open = False
# Assign funding fees to Order.
# Assumes backtesting will use date_last_filled_utc to calculate future funding fees.
self.funding_fee = trade.funding_fees
self.funding_fee = trade.funding_fee_running
trade.funding_fee_running = 0.0

if (self.ft_order_side == trade.entry_side and self.price):
trade.open_rate = self.price
Expand Down Expand Up @@ -393,6 +394,9 @@ class LocalTrade:

# Futures properties
funding_fees: Optional[float] = None
# Used to keep running funding fees - between the last filled order and now
# Shall not be used for calculations!
funding_fee_running: Optional[float] = None

@property
def stoploss_or_liquidation(self) -> float:
Expand Down Expand Up @@ -664,7 +668,9 @@ def set_funding_fees(self, funding_fee: float) -> None:
"""
if funding_fee is None:
return
self.funding_fees = funding_fee
self.funding_fee_running = funding_fee
prior_funding_fees = sum([o.funding_fee for o in self.orders if o.funding_fee])
self.funding_fees = prior_funding_fees + funding_fee

def __set_stop_loss(self, stop_loss: float, percent: float):
"""
Expand Down Expand Up @@ -747,7 +753,9 @@ def update_trade(self, order: Order, recalculating: bool = False) -> None:

logger.info(f'Updating trade (id={self.id}) ...')
if order.ft_order_side != 'stoploss':
order.funding_fee = self.funding_fees
order.funding_fee = self.funding_fee_running
# Reset running funding fees
self.funding_fee_running = 0.0

if order.ft_order_side == self.entry_side:
# Update open rate and actual amount
Expand Down Expand Up @@ -1489,6 +1497,8 @@ class Trade(ModelBase, LocalTrade):
# Futures properties
funding_fees: Mapped[Optional[float]] = mapped_column(
Float(), nullable=True, default=None) # type: ignore
funding_fee_running: Mapped[Optional[float]] = mapped_column(
Float(), nullable=True, default=None) # type: ignore

def __init__(self, **kwargs):
from_json = kwargs.pop('__FROM_JSON', None)
Expand Down
4 changes: 3 additions & 1 deletion tests/persistence/test_persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,15 +583,17 @@ def test_calc_open_close_trade_price(
oobj.update_from_ccxt_object(entry_order)
trade.update_trade(oobj)

trade.funding_fees = funding_fees
trade.funding_fee_running = funding_fees

oobj = Order.parse_from_ccxt_object(exit_order, 'ADA/USDT', trade.exit_side)
oobj._trade_live = trade
oobj.update_from_ccxt_object(exit_order)
trade.update_trade(oobj)

assert trade.is_open is False
# Funding fees transfer from funding_fee_running to funding_Fees
assert trade.funding_fees == funding_fees
assert trade.orders[-1].funding_fee == funding_fees

assert pytest.approx(trade._calc_open_trade_value(trade.amount, trade.open_rate)) == open_value
assert pytest.approx(trade.calc_close_trade_value(trade.close_rate)) == close_value
Expand Down
21 changes: 11 additions & 10 deletions tests/test_freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5917,16 +5917,17 @@ def test_get_valid_price(mocker, default_conf_usdt) -> None:
@pytest.mark.parametrize('trading_mode,calls,t1,t2', [
('spot', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
('margin', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
('futures', 31, "2021-09-01 00:00:02", "2021-09-01 08:00:01"),
('futures', 32, "2021-08-31 23:59:59", "2021-09-01 08:00:01"),
('futures', 32, "2021-09-01 00:00:02", "2021-09-01 08:00:02"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:02"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:03"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:04"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:05"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:06"),
('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:07"),
('futures', 33, "2021-08-31 23:59:58", "2021-09-01 08:00:07"),
('futures', 15, "2021-09-01 00:01:02", "2021-09-01 08:00:01"),
('futures', 16, "2021-09-01 00:00:02", "2021-09-01 08:00:01"),
('futures', 16, "2021-08-31 23:59:59", "2021-09-01 08:00:01"),
('futures', 16, "2021-09-01 00:00:02", "2021-09-01 08:00:02"),
('futures', 16, "2021-08-31 23:59:59", "2021-09-01 08:00:02"),
('futures', 16, "2021-08-31 23:59:59", "2021-09-01 08:00:03"),
('futures', 16, "2021-08-31 23:59:59", "2021-09-01 08:00:04"),
('futures', 17, "2021-08-31 23:59:59", "2021-09-01 08:01:05"),
('futures', 17, "2021-08-31 23:59:59", "2021-09-01 08:01:06"),
('futures', 17, "2021-08-31 23:59:59", "2021-09-01 08:01:07"),
('futures', 17, "2021-08-31 23:59:58", "2021-09-01 08:01:07"),
])
def test_update_funding_fees_schedule(mocker, default_conf, trading_mode, calls, time_machine,
t1, t2):
Expand Down

0 comments on commit fa174a3

Please sign in to comment.