From 07e195545b421d834a7aa14a23134dc5c1e67d3d Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:10:42 +0200 Subject: [PATCH 1/2] fix: include long-running Gains positions in HL funding projection --- src/app/api/fee-compare/route.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index d6d53301..1ba083d7 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -1130,10 +1130,12 @@ function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): P const slices: PositionSlice[] = []; for (const { open, close } of byId.values()) { if (!open) continue; - const openMs = new Date(open.date).getTime(); - if (openMs < cutoffMs) continue; - // Still-open positions use now as close time (same as reconstructHlPositions) + const rawOpenMs = new Date(open.date).getTime(); const closeMs = close ? new Date(close.date).getTime() : now; + // Skip positions that closed before the analysis window + if (closeMs < cutoffMs) continue; + // Cap openMs to the window start so long-running positions aren't skipped + const openMs = Math.max(rawOpenMs, cutoffMs); slices.push({ coin: open.pair.split("/")[0], notionalUsd: open.size * open.leverage, From 524bcdfc15621dfa7d0460bc261e8d8421cb83d8 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:13:21 +0200 Subject: [PATCH 2/2] fix: use earliest POSSIZEINCREASE as anchor for positions opened before fetch window --- src/app/api/fee-compare/route.ts | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index 1ba083d7..11b7236e 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -1115,33 +1115,40 @@ function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): P const INCREASE_ACTIONS = new Set(["TradePosSizeIncrease"]); const CLOSE_ACTIONS = new Set(["TradeClosedMarket", "TradeClosedTP", "TradeClosedSL", "TradeClosedLIQ"]); - const byId = new Map(); + const byId = new Map(); for (const t of trades) { if (!byId.has(t.id)) byId.set(t.id, {}); const e = byId.get(t.id)!; if (OPEN_ACTIONS.has(t.action)) e.open = t; - else if (INCREASE_ACTIONS.has(t.action) && e.open) { - e.open = { ...e.open, size: t.size, leverage: t.leverage }; + else if (INCREASE_ACTIONS.has(t.action)) { + if (e.open) e.open = { ...e.open, size: t.size, leverage: t.leverage }; + // Track increases for positions opened before the window (no open event in data) + else if (!e.lastIncrease || new Date(t.date).getTime() < new Date(e.lastIncrease.date).getTime()) { + e.lastIncrease = t; + } } else if (CLOSE_ACTIONS.has(t.action)) e.close = t; } const now = Date.now(); const slices: PositionSlice[] = []; - for (const { open, close } of byId.values()) { - if (!open) continue; - const rawOpenMs = new Date(open.date).getTime(); + for (const { open, close, lastIncrease } of byId.values()) { + // Use open event if available; fall back to earliest increase in the window + // for positions opened before the fetch window (open event not in data). + const anchor = open ?? lastIncrease; + if (!anchor) continue; + const rawOpenMs = new Date(anchor.date).getTime(); const closeMs = close ? new Date(close.date).getTime() : now; // Skip positions that closed before the analysis window if (closeMs < cutoffMs) continue; - // Cap openMs to the window start so long-running positions aren't skipped + // Cap openMs to the window start so long-running positions aren't missed const openMs = Math.max(rawOpenMs, cutoffMs); slices.push({ - coin: open.pair.split("/")[0], - notionalUsd: open.size * open.leverage, + coin: anchor.pair.split("/")[0], + notionalUsd: anchor.size * anchor.leverage, openMs, closeMs, - isLong: open.buy !== false, + isLong: anchor.buy !== false, }); }