diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 56f0c3d7e..b6ab24529 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -132,7 +132,7 @@ class FreqtradeBot(LoggingMixin): # 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 @@ -199,6 +199,7 @@ class FreqtradeBot(LoggingMixin): # 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: """ @@ -378,9 +379,6 @@ class FreqtradeBot(LoggingMixin): 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. diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 717a13f90..bb6c04922 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -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. @@ -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 ) @@ -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 @@ -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}") diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index d29c73040..9a24556bc 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -246,7 +246,8 @@ class Order(ModelBase): 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 @@ -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: @@ -664,7 +668,9 @@ class LocalTrade: """ 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): """ @@ -747,7 +753,9 @@ class LocalTrade: 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 @@ -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) diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index 2a2a53fce..541106ba5 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -583,7 +583,7 @@ 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 @@ -591,7 +591,9 @@ def test_calc_open_close_trade_price( 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 diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 790e29179..40d77ce6c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -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):