147147 @test cf. amount ≈ - expected_fee atol= 1e-6
148148end
149149
150- @testitem " Margin spot short accrues borrow fee and interest " begin
150+ @testitem " Margin spot short accrues borrow fee and lend on free cash only " begin
151151 using Test, Fastback, Dates
152152
153153 base_currency= CashSpec (:USD )
195195 advance_time! (acc, dt1; accrue_interest= true , accrue_borrow_fees= true )
196196
197197 yearfrac = Dates. value (Dates. Millisecond (dt1 - dt0)) / (1000 * 60 * 60 * 24 * 365.0 )
198- expected_interest = bal_before * 0.02 * yearfrac
198+ short_proceeds = abs (qty) * price * inst. multiplier
199+ expected_interest = (bal_before - short_proceeds) * 0.02 * yearfrac
199200 expected_fee = abs (qty) * price * inst. multiplier * inst. short_borrow_rate * yearfrac
200201 expected_delta = expected_interest - expected_fee
201202
@@ -216,6 +217,124 @@ end
216217 @test fee_cf. amount ≈ - expected_fee atol= 1e-8
217218end
218219
220+ @testitem " IBKR short excludes proceeds from lend base and applies rebate" begin
221+ using Test, Fastback, Dates
222+
223+ benchmark_by_cash = Dict (:USD => StepSchedule ([(DateTime (2026 , 1 , 1 ), 0.03 )]))
224+ base_currency= CashSpec (:USD )
225+ acc = Account (
226+ ;
227+ broker= IBKRProFixedBroker (
228+ benchmark_by_cash= benchmark_by_cash,
229+ borrow_spread= 0.015 ,
230+ lend_spread= 0.005 ,
231+ credit_no_interest_balance= 0.0 ,
232+ short_proceeds_exclusion= 1.0 ,
233+ short_proceeds_rebate_spread= 0.01 ,
234+ ),
235+ funding= AccountFunding. Margined,
236+ base_currency= base_currency,
237+ )
238+ deposit! (acc, :USD , 10_100.0 )
239+
240+ inst = register_instrument! (acc, Instrument (
241+ Symbol (" SPOTIBKR/USD" ),
242+ :SPOTIBKR ,
243+ :USD ;
244+ contract_kind= ContractKind. Spot,
245+ settlement= SettlementStyle. PrincipalExchange,
246+ margin_requirement= MarginRequirement. PercentNotional,
247+ margin_init_long= 0.5 ,
248+ margin_maint_long= 0.25 ,
249+ margin_init_short= 0.5 ,
250+ margin_maint_short= 0.25 ,
251+ ))
252+
253+ dt0 = DateTime (2026 , 1 , 1 )
254+ price = 100.0
255+ qty = - 200.0
256+ trade = fill_order! (acc, Order (oid! (acc), inst, dt0, price, qty); dt= dt0, fill_price= price, bid= price, ask= price, last= price)
257+ @test trade isa Trade
258+
259+ usd = cash_asset (acc, :USD )
260+ bal_before = cash_balance (acc, usd)
261+ eq_before = equity (acc, usd)
262+ @test bal_before ≈ 30_099.0 atol= 1e-8
263+ @test eq_before ≈ 10_099.0 atol= 1e-8
264+
265+ accrue_interest! (acc, dt0)
266+ @test isempty (acc. cashflows)
267+
268+ dt1 = dt0 + Day (1 )
269+ accrue_interest! (acc, dt1)
270+
271+ yearfrac = Dates. value (Dates. Millisecond (dt1 - dt0)) / (1000 * 60 * 60 * 24 * 365.0 )
272+ short_proceeds = abs (qty) * price * inst. multiplier
273+ free_cash = bal_before - short_proceeds
274+ expected_lend = free_cash * (0.03 - 0.005 ) * yearfrac
275+ expected_rebate = short_proceeds * (0.03 - 0.01 ) * yearfrac
276+ expected_interest = expected_lend + expected_rebate
277+
278+ @test cash_balance (acc, usd) ≈ bal_before + expected_interest atol= 1e-8
279+ @test equity (acc, usd) ≈ eq_before + expected_interest atol= 1e-8
280+ @test length (acc. cashflows) == 1
281+ cf = only (acc. cashflows)
282+ @test cf. kind == CashflowKind. LendInterest
283+ @test cf. cash_index == usd. index
284+ @test cf. amount ≈ expected_interest atol= 1e-8
285+ end
286+
287+ @testitem " Flat fee broker can include short proceeds in lend base" begin
288+ using Test, Fastback, Dates
289+
290+ base_currency= CashSpec (:USD )
291+ acc = Account (
292+ ;
293+ broker= FlatFeeBroker (
294+ lend_by_cash= Dict (:USD => 0.02 ),
295+ short_proceeds_exclusion_by_cash= Dict (:USD => 0.0 ),
296+ ),
297+ funding= AccountFunding. Margined,
298+ base_currency= base_currency,
299+ )
300+ deposit! (acc, :USD , 10_000.0 )
301+
302+ inst = register_instrument! (acc, Instrument (
303+ Symbol (" SPOTINCL/USD" ),
304+ :SPOTINCL ,
305+ :USD ;
306+ contract_kind= ContractKind. Spot,
307+ settlement= SettlementStyle. PrincipalExchange,
308+ margin_requirement= MarginRequirement. PercentNotional,
309+ margin_init_long= 0.5 ,
310+ margin_maint_long= 0.25 ,
311+ margin_init_short= 0.5 ,
312+ margin_maint_short= 0.25 ,
313+ ))
314+
315+ dt0 = DateTime (2026 , 1 , 1 )
316+ price = 100.0
317+ qty = - 200.0
318+ trade = fill_order! (acc, Order (oid! (acc), inst, dt0, price, qty); dt= dt0, fill_price= price, bid= price, ask= price, last= price)
319+ @test trade isa Trade
320+
321+ usd = cash_asset (acc, :USD )
322+ bal_before = cash_balance (acc, usd)
323+ @test bal_before ≈ 30_000.0 atol= 1e-8
324+
325+ accrue_interest! (acc, dt0)
326+ dt1 = dt0 + Day (1 )
327+ accrue_interest! (acc, dt1)
328+
329+ yearfrac = Dates. value (Dates. Millisecond (dt1 - dt0)) / (1000 * 60 * 60 * 24 * 365.0 )
330+ expected_interest = bal_before * 0.02 * yearfrac
331+
332+ @test cash_balance (acc, usd) ≈ bal_before + expected_interest atol= 1e-8
333+ @test equity (acc, usd) ≈ 10_000.0 + expected_interest atol= 1e-8
334+ @test length (acc. cashflows) == 1
335+ @test only (acc. cashflows). amount ≈ expected_interest atol= 1e-8
336+ end
337+
219338@testitem " Fully funded account applies margin checks to spot" begin
220339 using Test, Fastback, Dates
221340
0 commit comments