diff --git a/app/api/state/route.ts b/app/api/state/route.ts index 684d840..53a2266 100644 --- a/app/api/state/route.ts +++ b/app/api/state/route.ts @@ -50,14 +50,15 @@ export async function GET() { allFundTxns ); const equityProfit = cash.amount - cfg.account_size; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.min_day_pnl, effective.minTradingDays, equityProfit); + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.min_day_pnl, effective.minTradingDays, equityProfit); dailyTarget = target; // Condition 1: profit target already exceeded (target=0), still need days → any activity counts // Condition 2: target > 0 → must have made at least the computed daily target - targetHit = - // If we are just flipping take any activity as target hit - (target.amount === 0 && Math.abs(cash.realizedPnL) > 0 && client.daysTraded[acc.id] <= effective.minTradingDays) || - (cash.realizedPnL >= target.amount); + if (target) { + targetHit = + (target.amount === 0 && Math.abs(cash.realizedPnL) > 0 && client.daysTraded[acc.id] <= effective.minTradingDays) || + (cash.realizedPnL >= target.amount); + } } return { diff --git a/lib/auto-trade.ts b/lib/auto-trade.ts index a0994ac..62677a3 100644 --- a/lib/auto-trade.ts +++ b/lib/auto-trade.ts @@ -200,7 +200,8 @@ export async function runTrade(action: 'Buy' | 'Sell' | 'Auto', symbol: string) // Use the same target formula as the dashboard — skip if $0 (challenge complete) const equityProfit = cash.amount - cfg.accountSize; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + if (!target) continue; // skip accounts without a valid balance // Allow through if it's an MNQ extra-day trade: no min day P&L, profit done, days still needed const isMnqExtraDay = cfg.minDayPnL <= 0 @@ -266,7 +267,8 @@ export async function runTrade(action: 'Buy' | 'Sell' | 'Auto', symbol: string) } const equityProfit = cash.amount - cfg.accountSize; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + if (!target) throw new Error(`${acc.name}: invalid balance, cannot compute target`); const rawContracts = Math.max(1, Math.ceil(target.amount / 1000)); const contracts = cfg.maxPositionSize > 0 ? Math.min(rawContracts, cfg.maxPositionSize) : rawContracts; @@ -444,8 +446,8 @@ export async function copyTrade() { ); const equityProfit = cash.amount - cfg.accountSize; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); - if (target.amount <= 0) continue; + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + if (!target || target.amount <= 0) continue; eligible.push({ firmName: firm.name, client, acc, contract, firmConfig, cash, dailyPnL, daysTraded }); } @@ -471,7 +473,8 @@ export async function copyTrade() { ); const equityProfit = cash.amount - cfg.accountSize; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + if (!target) throw new Error(`${acc.name}: invalid balance, cannot compute target`); const rawContracts = Math.max(1, Math.ceil(target.amount / 1000)); const contracts = cfg.maxPositionSize > 0 ? Math.min(rawContracts, cfg.maxPositionSize) : rawContracts; @@ -559,7 +562,8 @@ function hasRemainingConfiguredAccounts(): boolean { ); const equityProfit = cash.amount - cfg.accountSize; - const target = computeDailyTarget(effective.profitTarget, effective.consistency, totalProfit, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + const target = computeDailyTarget(effective.profitTarget, effective.consistency, dailyPnL, cfg.minDayPnL, effective.minTradingDays, equityProfit); + if (!target) continue; // no valid balance → skip this account const isMnqExtraDay = cfg.minDayPnL <= 0 && effective.minTradingDays > daysTraded && totalProfit >= effective.profitTarget; diff --git a/lib/reporter.ts b/lib/reporter.ts index 7c0a325..6c132ab 100644 --- a/lib/reporter.ts +++ b/lib/reporter.ts @@ -50,11 +50,11 @@ function collectFirmStats() { const cfg = getAccountConfig(acc.name, f.accounts); if (cfg) { const dailyPnL = client.dailyPnL[acc.id] ?? []; - const totalProfit = dailyPnL.reduce((sum: number, d: { pnl: number }) => sum + d.pnl, 0); - const target = computeDailyTarget(cfg.profit_target, cfg.consistency, totalProfit, dailyPnL, cfg.min_day_pnl, cfg.min_trading_days); - const targetHit = - (target.amount === 0 && Math.abs(cash.realizedPnL) > 0 && (client.daysTraded[acc.id] ?? 0) <= cfg.min_trading_days) || - (cash.realizedPnL >= target.amount); + const equityProfit = cash.amount - cfg.account_size; + const target = computeDailyTarget(cfg.profit_target, cfg.consistency, dailyPnL, cfg.min_day_pnl, cfg.min_trading_days, equityProfit); + const targetHit = !target ? false : + ((target.amount === 0 && Math.abs(cash.realizedPnL) > 0 && (client.daysTraded[acc.id] ?? 0) <= cfg.min_trading_days) || + (cash.realizedPnL >= target.amount)); if (targetHit || cash.realizedPnL !== 0) { accountsTraded++; diff --git a/lib/trading-logic.ts b/lib/trading-logic.ts index d8205c9..892334e 100644 --- a/lib/trading-logic.ts +++ b/lib/trading-logic.ts @@ -80,16 +80,20 @@ export const POINT_VALUES: { [symbol: string]: number } = { export function computeDailyTarget( profitTarget: number, consistency: number, - totalProfit: number, dailyPnL: { date: string; pnl: number }[], - minDayPnL: number = 0, // 0 = no minimum per day - minTradingDays: number = 0, // 0 = no minimum trading days - equityProfit?: number // amount − accountSize; used for profitTarget comparison. Defaults to totalProfit. -): { amount: number; path: 'first_day' | 'normal_day' | 'reduced_day' } { + minDayPnL: number, // 0 = no minimum per day + minTradingDays: number, // 0 = no minimum trading days + equityProfit: number | null | undefined // amount − accountSize. Null/undefined → skip (return null). 0 is valid. +): { amount: number; path: 'first_day' | 'normal_day' | 'reduced_day' } | null { + // Don't run if we don't have a real account balance to compute against — 0 is valid. + if (typeof equityProfit !== 'number' || !Number.isFinite(equityProfit)) { + return null; + } + const qualifyingDays = minDayPnL === 0 ? dailyPnL : dailyPnL.filter((d) => d.pnl >= minDayPnL); const daysTraded = qualifyingDays.length; const effectiveMinDay = Math.max(0, minDayPnL); - const currentProfit = equityProfit ?? totalProfit; + const currentProfit = equityProfit; const remaining = profitTarget - currentProfit; // 1. Profit target already met — coast on min-day if mandatory days remain, else nothing to do.