From d0065ab0474a1f825239c6b290a198a4fe360317 Mon Sep 17 00:00:00 2001 From: Senofy <63175905+Senofy@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:41:56 -0500 Subject: [PATCH] Refresh dailyPnL on a fixed hourly interval instead of throttling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the reactive throttle with a proactive setInterval in requestSync that fires fetchDaysTraded exactly once per hour per client - Remove post-fill fetchDaysTraded calls from auto-trade.ts — no longer needed and were causing bursts of report API requests on simultaneous fills - Guard against duplicate intervals if requestSync fires more than once Co-Authored-By: Claude Sonnet 4.6 --- lib/auto-trade.ts | 14 -------------- lib/tradovate-class.ts | 15 +++++++++------ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/lib/auto-trade.ts b/lib/auto-trade.ts index 0f1be4b..8a31288 100644 --- a/lib/auto-trade.ts +++ b/lib/auto-trade.ts @@ -180,11 +180,6 @@ export async function runTrade(action: 'Buy' | 'Sell' | 'Random', symbol: string const exitAction: 'Buy' | 'Sell' = resolvedAction === 'Buy' ? 'Sell' : 'Buy'; const exitFill = await client.sendOrder(acc.id, mnqContract.name, 1, exitAction, 'Market'); - // Refresh daily P&L immediately after the extra-day round-trip completes - client.fetchDaysTraded().catch((err) => - console.error('[auto-trade] post-fill fetchDaysTraded error:', err) - ); - console.log(`[auto-trade] ${acc.name} (${item.firmName}) extra-day: ${resolvedAction} 1xMNQ @ ${fill.price} | exited @ ${exitFill.price} (market)`); return { @@ -226,15 +221,6 @@ export async function runTrade(action: 'Buy' | 'Sell' | 'Random', symbol: string const exitAction: 'Buy' | 'Sell' = resolvedAction === 'Buy' ? 'Sell' : 'Buy'; const exitOrder = await client.placeOrderNoWait(acc.id, contract.name, contracts, exitAction, 'Limit', exitPrice); - // Refresh daily P&L as soon as the exit limit order fills - if (exitOrder.orderId != null) { - client.onFill(exitOrder.orderId, () => { - client.fetchDaysTraded().catch((err) => - console.error('[auto-trade] post-fill fetchDaysTraded error:', err) - ); - }); - } - console.log(`[auto-trade] ${acc.name} (${item.firmName}) ${resolvedAction} ${contracts}x${resolvedSymbol} @ ${fill.price} | target $${target.amount} [${target.path}] (+$${totalCommission.toFixed(2)} comm) | exit @ ${exitPrice} (orderId=${exitOrder.orderId})`); return { diff --git a/lib/tradovate-class.ts b/lib/tradovate-class.ts index aaaecf3..ca21f4e 100644 --- a/lib/tradovate-class.ts +++ b/lib/tradovate-class.ts @@ -34,8 +34,8 @@ export class TradovateClient { /** True once fetchDaysTraded() has finished its last full run */ public fetchDaysComplete = false; - /** Timestamp (ms) of the last successful fetchDaysTraded run — throttled to once per hour */ - private lastDaysFetch = 0; + /** NodeJS.Timeout handle for the hourly dailyPnL refresh */ + private daysFetchInterval: ReturnType | null = null; /** Last error per account name from fetchDaysTraded() */ public lastFetchErrors: Record = {}; /** Raw reports API response sample per account (first 100 chars) for debugging */ @@ -290,6 +290,13 @@ export class TradovateClient { this.fetchDaysTraded(); + // Refresh daily P&L once per hour — avoids hammering the reports API + if (this.daysFetchInterval) clearInterval(this.daysFetchInterval); + this.daysFetchInterval = setInterval( + () => this.fetchDaysTraded().catch((err) => console.error('[hourly fetchDaysTraded]', err)), + 60 * 60 * 1_000 + ); + if (this.products.length > 0) { this.syncComplete = true; this.callbackOnSyncRequest(); @@ -315,10 +322,6 @@ export class TradovateClient { public async fetchDaysTraded(): Promise { if (!this.accessInfo?.accessToken) return; - // Throttle to at most once per hour to avoid rate-limiting the reports API - const ONE_HOUR = 60 * 60 * 1_000; - if (Date.now() - this.lastDaysFetch < ONE_HOUR) return; - this.lastDaysFetch = Date.now(); this.fetchDaysComplete = false; const authHeaders = { Authorization: `Bearer ${this.accessInfo.accessToken}` };