Refresh dailyPnL on a fixed hourly interval instead of throttling

- 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 <noreply@anthropic.com>
This commit is contained in:
Senofy
2026-03-12 20:41:56 -05:00
co-authored by Claude Sonnet 4.6
parent 92b929f4ce
commit d0065ab047
2 changed files with 9 additions and 20 deletions
-14
View File
@@ -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 {
+9 -6
View File
@@ -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<typeof setInterval> | null = null;
/** Last error per account name from fetchDaysTraded() */
public lastFetchErrors: Record<string, string> = {};
/** 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<void> {
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}` };