Report and badge Personal DLL days

A stop-enabled account whose day booked red via its own stop shows an
amber "Personal DLL" badge on the dashboard (the firm's lockout keeps
its red "Hit DLL") and reports as personalDll in the day outcomes
instead of loss, per account type and per firm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-11 18:51:03 -05:00
co-authored by Claude Fable 5
parent c58e981b1e
commit 6685a75864
5 changed files with 30 additions and 6 deletions
+6
View File
@@ -154,6 +154,12 @@ export async function GET() {
// on the Tuesday after a holiday this includes Sun/Mon. // on the Tuesday after a holiday this includes Sun/Mon.
realizedPnL: combined.realizedPnL, realizedPnL: combined.realizedPnL,
daysTraded, daysTraded,
// A stop-enabled account whose day booked red via its own
// stop — the personal DLL. Firm DLL lockouts show via
// !active instead.
personalDll: !!cfg && (cfg.firstDayStop || cfg.minDayStop)
&& acc.active && !client.positions[acc.id]
&& combined.realizedPnL <= -25,
positionDirection: client.positions[acc.id] positionDirection: client.positions[acc.id]
? (client.positions[acc.id].netPos > 0 ? 'long' as const : 'short' as const) ? (client.positions[acc.id].netPos > 0 ? 'long' as const : 'short' as const)
: null, : null,
+2
View File
@@ -362,6 +362,8 @@ function AccountRow({ account, firm, hideDead, privacy, onAssigned }: {
? <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-red-100 text-red-700">Hit DLL</span> ? <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-red-100 text-red-700">Hit DLL</span>
: account.positionDirection : account.positionDirection
? <span className={`inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold ${account.positionDirection === 'long' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{account.positionDirection === 'long' ? 'Long' : 'Short'}</span> ? <span className={`inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold ${account.positionDirection === 'long' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{account.positionDirection === 'long' ? 'Long' : 'Short'}</span>
: account.personalDll
? <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-amber-100 text-amber-700" title="Booked the day red via its own stop — make-back rides next session">Personal DLL</span>
: account.targetHit : account.targetHit
? <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-700">Target Hit</span> ? <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-700">Target Hit</span>
: <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-slate-100 text-slate-500">Flat</span>} : <span className="inline-block px-2.5 py-0.5 rounded-full text-xs font-semibold bg-slate-100 text-slate-500">Flat</span>}
+4
View File
@@ -22,6 +22,9 @@ export interface AccountTypeStats {
targetHit?: number; targetHit?: number;
partial?: number; partial?: number;
loss?: number; loss?: number;
/** Of the finished accounts, how many booked the day via their own
* obfuscation stop (personal DLL) rather than a genuine loss. */
personalDll?: number;
/** Live accounts by trading days completed toward the current target: /** Live accounts by trading days completed toward the current target:
* {daysTraded: accountCount}. Feeds the master's payout calendar, so * {daysTraded: accountCount}. Feeds the master's payout calendar, so
* accounts that are dead, DLL-locked, or in the red are left out — the * accounts that are dead, DLL-locked, or in the red are left out — the
@@ -56,6 +59,7 @@ export interface FirmStats {
targetHit?: number; targetHit?: number;
partial?: number; partial?: number;
loss?: number; loss?: number;
personalDll?: number;
/** Per account type, summing to the firm's own totals. Optional so a master /** Per account type, summing to the firm's own totals. Optional so a master
* on an older build simply ignores it. */ * on an older build simply ignores it. */
accountTypes?: AccountTypeStats[]; accountTypes?: AccountTypeStats[];
+12 -3
View File
@@ -32,7 +32,7 @@ function collectFirmStats() {
if (!client || client.accountList.length === 0) { if (!client || client.accountList.length === 0) {
return { return {
firm: f.name, totalAccounts: 0, accountsTraded: 0, inTrade: 0, firm: f.name, totalAccounts: 0, accountsTraded: 0, inTrade: 0,
targetHit: 0, partial: 0, loss: 0, accountTypes: [], targetHit: 0, partial: 0, loss: 0, personalDll: 0, accountTypes: [],
}; };
} }
@@ -42,6 +42,7 @@ function collectFirmStats() {
let targetHitCount = 0; let targetHitCount = 0;
let partialCount = 0; let partialCount = 0;
let lossCount = 0; let lossCount = 0;
let personalDllCount = 0;
// Per account type, so the master can say what the accounts still to // Per account type, so the master can say what the accounts still to
// trade actually are. Keyed by account type id; unassigned accounts // trade actually are. Keyed by account type id; unassigned accounts
@@ -172,7 +173,14 @@ function collectFirmStats() {
slice.accountsTraded++; slice.accountsTraded++;
// A grind-day fill (dayTarget 0) can flag targetHit on a // A grind-day fill (dayTarget 0) can flag targetHit on a
// negative day — a loss is still a loss, so red wins. // negative day — a loss is still a loss, so red wins.
if (combined.realizedPnL < 0) { // A red day on a stop-enabled account is its own personal
// DLL, booked on purpose — not a genuine loss. (A firm DLL
// lockout took the !acc.active branch above and stays a
// loss.)
if ((cfg.firstDayStop || cfg.minDayStop) && combined.realizedPnL <= -25) {
personalDllCount++;
slice.personalDll = (slice.personalDll ?? 0) + 1;
} else if (combined.realizedPnL < 0) {
lossCount++; lossCount++;
slice.loss = (slice.loss ?? 0) + 1; slice.loss = (slice.loss ?? 0) + 1;
} else if (targetHit) { } else if (targetHit) {
@@ -195,7 +203,8 @@ function collectFirmStats() {
return { return {
firm: f.name, totalAccounts, accountsTraded, inTrade, firm: f.name, totalAccounts, accountsTraded, inTrade,
targetHit: targetHitCount, partial: partialCount, loss: lossCount, accountTypes, targetHit: targetHitCount, partial: partialCount, loss: lossCount,
personalDll: personalDllCount, accountTypes,
}; };
}); });
} }
+3
View File
@@ -49,6 +49,9 @@ export interface AccountState {
totalProfit: number; totalProfit: number;
/** True when today's realizedPnL has met or exceeded the computed daily target */ /** True when today's realizedPnL has met or exceeded the computed daily target */
targetHit: boolean; targetHit: boolean;
/** True when a stop-enabled account booked its day red via its own stop —
* the personal DLL. It stands down until the next session's make-back. */
personalDll: boolean;
/** Current stage = 1 + number of withdrawals (negative fund transactions). Stage 1 = fresh account. */ /** Current stage = 1 + number of withdrawals (negative fund transactions). Stage 1 = fresh account. */
stage: number; stage: number;
/** Next trading day's target, computed server-side. null when account is dead or config is missing. */ /** Next trading day's target, computed server-side. null when account is dead or config is missing. */