@@ -5297,6 +5297,91 @@ def test_update_funding_fees_error(mocker, default_conf, caplog):
52975297 log_has ("Could not update funding fees for open trades." , caplog )
52985298
52995299
5300+ def test_execute_entry_funding_fees_dca (mocker , default_conf_usdt , fee ) -> None :
5301+ """
5302+ On a position adjustment (DCA), funding fees must be calculated on the existing
5303+ position size (trade.amount) - not on the combined existing + newly added amount,
5304+ as the newly added amount hasn't accrued any funding fees yet.
5305+ """
5306+ patch_wallet (mocker , free = 10000 )
5307+ default_conf_usdt .update (
5308+ {
5309+ "position_adjustment_enable" : True ,
5310+ "trading_mode" : "futures" ,
5311+ "margin_mode" : "isolated" ,
5312+ "dry_run" : False ,
5313+ "stake_amount" : 10.0 ,
5314+ "dry_run_wallet" : 1000.0 ,
5315+ }
5316+ )
5317+ bid = 11
5318+ stake_amount = 10
5319+ get_funding_fees = MagicMock (return_value = 0 )
5320+ mocker .patch .multiple (
5321+ EXMS ,
5322+ get_rate = MagicMock (return_value = bid ),
5323+ fetch_ticker = MagicMock (return_value = {"bid" : 10 , "ask" : 12 , "last" : 11 }),
5324+ get_min_pair_stake_amount = MagicMock (return_value = 1 ),
5325+ get_fee = fee ,
5326+ get_funding_fees = get_funding_fees ,
5327+ get_maintenance_ratio_and_amt = MagicMock (return_value = (0.01 , 0.01 )),
5328+ get_max_leverage = MagicMock (return_value = 10 ),
5329+ )
5330+ freqtrade = get_patched_freqtradebot (mocker , default_conf_usdt )
5331+ pair = "ETH/USDT"
5332+
5333+ # Initial buy - creates a trade with amount == 30
5334+ closed_buy_order = {
5335+ "pair" : pair ,
5336+ "ft_pair" : pair ,
5337+ "ft_order_side" : "buy" ,
5338+ "side" : "buy" ,
5339+ "type" : "limit" ,
5340+ "status" : "closed" ,
5341+ "price" : bid ,
5342+ "average" : bid ,
5343+ "cost" : bid * 30 ,
5344+ "amount" : 30 ,
5345+ "filled" : 30 ,
5346+ "ft_is_open" : False ,
5347+ "id" : "650" ,
5348+ "order_id" : "650" ,
5349+ }
5350+ mocker .patch (f"{ EXMS } .create_order" , return_value = closed_buy_order )
5351+ mocker .patch (f"{ EXMS } .fetch_order_or_stoploss_order" , return_value = closed_buy_order )
5352+ assert freqtrade .execute_entry (pair , stake_amount )
5353+
5354+ trade = Trade .session .scalars (select (Trade )).first ()
5355+ assert trade
5356+ assert trade .amount == 30
5357+
5358+ # Position adjustment (DCA) - adds 12 to the position
5359+ get_funding_fees .reset_mock ()
5360+ closed_dca_order = {
5361+ "pair" : pair ,
5362+ "ft_pair" : pair ,
5363+ "ft_order_side" : "buy" ,
5364+ "side" : "buy" ,
5365+ "type" : "limit" ,
5366+ "status" : "closed" ,
5367+ "price" : 9 ,
5368+ "average" : 9 ,
5369+ "cost" : 108 ,
5370+ "amount" : 12 ,
5371+ "filled" : 12 ,
5372+ "ft_is_open" : False ,
5373+ "id" : "651" ,
5374+ "order_id" : "651" ,
5375+ }
5376+ mocker .patch (f"{ EXMS } .create_order" , return_value = closed_dca_order )
5377+ mocker .patch (f"{ EXMS } .fetch_order_or_stoploss_order" , return_value = closed_dca_order )
5378+ assert freqtrade .execute_entry (pair , stake_amount , trade = trade )
5379+
5380+ # Funding fees must be calculated on the existing amount (30), not 30 + 12
5381+ assert get_funding_fees .call_count == 1
5382+ assert get_funding_fees .call_args [1 ]["amount" ] == 30
5383+
5384+
53005385def test_position_adjust (mocker , default_conf_usdt , fee ) -> None :
53015386 patch_RPCManager (mocker )
53025387 patch_exchange (mocker )
0 commit comments