Skip to content

Commit d613c73

Browse files
Hiksangclaude
andcommitted
fix: use actual funding from exchange API instead of estimate
- Funding: fetch real payments via getFundingPayments() filtered by entry time - Fees: only count entry fees (exit not yet paid) - Fallback to estimate if API call fails Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eab035b commit d613c73

1 file changed

Lines changed: 23 additions & 15 deletions

File tree

src/commands/arb/index.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -432,24 +432,32 @@ export function registerArbManageCommands(
432432
const perpNotional = shortPos.size * shortPos.markPrice;
433433
const spotValueUsd = spotAmount * shortPos.markPrice; // approx using perp mark price
434434

435-
// Funding income: spot has 0 funding, perp short receives when rate > 0
436-
let estimatedFundingIncome = 0;
437-
if (holdDurationMs && hourlyRate !== 0) {
438-
const holdHours = holdDurationMs / (1000 * 60 * 60);
439-
// Short position: pays when rate < 0, receives when rate > 0
440-
estimatedFundingIncome = Math.abs(hourlyRate) * perpNotional * holdHours;
441-
if (hourlyRate < 0) estimatedFundingIncome = -estimatedFundingIncome;
442-
}
443-
// Add persisted accumulated funding
444-
if (persisted?.accumulatedFunding) {
445-
estimatedFundingIncome += persisted.accumulatedFunding;
435+
// Funding income: fetch actual payments from exchange API
436+
let actualFundingIncome = 0;
437+
try {
438+
const perpAdapter = await getAdapterForExchange(shortPos.exchange);
439+
const payments = await perpAdapter.getFundingPayments(200);
440+
const entryTime = persisted ? new Date(persisted.entryTime).getTime() : (holdDurationMs ? Date.now() - holdDurationMs : 0);
441+
for (const p of payments) {
442+
if (p.symbol.toUpperCase().includes(perpSymbol) && p.time >= entryTime) {
443+
actualFundingIncome += Number(p.payment);
444+
}
445+
}
446+
} catch {
447+
// Fallback to estimate if API fails
448+
if (holdDurationMs && hourlyRate !== 0) {
449+
const holdHours = holdDurationMs / (1000 * 60 * 60);
450+
actualFundingIncome = Math.abs(hourlyRate) * perpNotional * holdHours;
451+
if (hourlyRate < 0) actualFundingIncome = -actualFundingIncome;
452+
}
446453
}
454+
const estimatedFundingIncome = actualFundingIncome;
447455

448-
// Fees: spot buy + spot sell + perp short entry + perp buy to close
456+
// Fees: entry only (exit not yet paid)
449457
const { getTakerFee } = await import("../../constants.js");
450458
const spotFee = getTakerFee(spotEx);
451459
const perpFee = getTakerFee(shortPos.exchange);
452-
const totalFees = (spotValueUsd * spotFee * 2) + (perpNotional * perpFee * 2);
460+
const entryFees = (spotValueUsd * spotFee) + (perpNotional * perpFee);
453461

454462
const direction = hourlyRate >= 0 ? "long-spot-short-perp" as const : "sell-spot-long-perp" as const;
455463
const dailyFundingEstimate = Math.abs(hourlyRate) * perpNotional * 24;
@@ -474,8 +482,8 @@ export function registerArbManageCommands(
474482
holdDuration,
475483
holdDurationMs,
476484
estimatedFundingIncome,
477-
estimatedFees: totalFees,
478-
netPnl: (spotValueUsd - shortPos.entryPrice * spotAmount) + shortPos.unrealizedPnl + estimatedFundingIncome - totalFees,
485+
estimatedFees: entryFees,
486+
netPnl: (spotValueUsd - shortPos.entryPrice * spotAmount) + shortPos.unrealizedPnl + estimatedFundingIncome - entryFees,
479487
dailyFundingEstimate,
480488
});
481489
}

0 commit comments

Comments
 (0)