Fix min-day target for 0% consistency accounts + daysTraded consistency

trading-logic: when baseAmount=0 (consistency=0%), target cappedByFuture
directly instead of collapsing to minDayPnL. For a $4000 target with $150
min-day and 5 days, day 1 now correctly targets $3400 ($4000 - 4×$150)
then $150 for each remaining mandatory day.

tradovate-class: make fetchDaysTraded() public so auto-trade can call it
immediately after a trade exits. Fix daysTraded to count only positive-P&L
days from the FIFO results, consistent with computeDailyTarget's
positiveDays.length — previously counted all raw fill dates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Senofy
2026-03-09 17:33:55 -05:00
co-authored by Claude Sonnet 4.6
parent 5cba4ef175
commit 68075059d0
2 changed files with 11 additions and 5 deletions
+5 -1
View File
@@ -79,7 +79,11 @@ export function computeDailyTarget(
// Floor: must make at least minDayPnL today (or whatever is left if less) // Floor: must make at least minDayPnL today (or whatever is left if less)
const floor = Math.min(effectiveMinDay, remaining); const floor = Math.min(effectiveMinDay, remaining);
const amount = Math.max(floor, Math.min(baseAmount, cappedByFuture)); // When consistency = 0% (baseAmount = 0), there's no consistency-based upper bound —
// target cappedByFuture directly (make as much as possible today, reserve future days).
// When consistency > 0%, treat baseAmount as the consistency cap.
const consistencyCapped = baseAmount > 0 ? Math.min(baseAmount, cappedByFuture) : cappedByFuture;
const amount = Math.max(floor, consistencyCapped);
return { amount: Math.round(amount * 100) / 100, path }; return { amount: Math.round(amount * 100) / 100, path };
} }
+6 -4
View File
@@ -311,7 +311,7 @@ export class TradovateClient {
this.ws.send('user/syncrequest\n3\n\n{"splitResponses":false}'); this.ws.send('user/syncrequest\n3\n\n{"splitResponses":false}');
} }
private async fetchDaysTraded(): Promise<void> { public async fetchDaysTraded(): Promise<void> {
if (!this.accessInfo?.accessToken) return; if (!this.accessInfo?.accessToken) return;
this.fetchDaysComplete = false; this.fetchDaysComplete = false;
@@ -378,8 +378,6 @@ export class TradovateClient {
commission: number; commission: number;
}; };
const fills: Fill[] = JSON.parse(raw); const fills: Fill[] = JSON.parse(raw);
const uniqueDays = new Set(fills.map(f => f._tradeDate));
this.daysTraded[account.id] = uniqueDays.size;
// POINT_VALUES imported from trading-logic.ts // POINT_VALUES imported from trading-logic.ts
@@ -428,9 +426,13 @@ export class TradovateClient {
} }
} }
this.dailyPnL[account.id] = Object.entries(dailyMap) const entries = Object.entries(dailyMap)
.map(([date, pnl]) => ({ date, pnl: Math.round(pnl * 100) / 100 })) .map(([date, pnl]) => ({ date, pnl: Math.round(pnl * 100) / 100 }))
.sort((a, b) => a.date.localeCompare(b.date)); .sort((a, b) => a.date.localeCompare(b.date));
this.dailyPnL[account.id] = entries;
// Count only positive-P&L days — consistent with computeDailyTarget's positiveDays
this.daysTraded[account.id] = entries.filter(d => d.pnl > 0).length;
} catch (err) { } catch (err) {
const msg = err instanceof Error ? `${err.message}` : String(err); const msg = err instanceof Error ? `${err.message}` : String(err);
console.error(`[fetchDaysTraded] ${account.name}:`, msg); console.error(`[fetchDaysTraded] ${account.name}:`, msg);