DXtrade: volatility measured once a day, next-leg hold on the dashboard, closed accounts dead
Each instrument's volatility is measured at the first good sync (or pick) of
a trade date and held for the day, so a day's sizing and hold estimate stay
put. The dashboard shows each DXtrade account's next-leg hold ("hold ~Nh")
with its symbol, notional, volatility and win odds. Accounts the firm has
closed (TERMINATED, EXPIRED, NO_TRADING) read as dead instead of inactive,
and skip the books check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9fc1ed6908
commit
6876223fa4
+40
-2
@@ -3,10 +3,12 @@ import { loadDailyPnL } from '@/lib/db';
|
||||
import { getClients } from '@/lib/clients';
|
||||
import { getCachedCatalog } from '@/lib/firm-catalog';
|
||||
import { getAccountConfig, getInferredSize, matchAccountType } from '@/lib/account-type-resolver';
|
||||
import { historyFresh } from '@/lib/platform-client';
|
||||
import { historyFresh, type PlatformContract } from '@/lib/platform-client';
|
||||
import type { AccountState } from '@/types';
|
||||
import {
|
||||
combineCmeSessions, computeDayPlan, cycleCompletion, resolveEffectiveConfig, currentTradeDateCT,
|
||||
obfuscationStopDollars, dllRoom as computeDllRoom, dllStopDollars, botStopsActive,
|
||||
obfuscationStopDollars, dllRoom as computeDllRoom, dllStopDollars, botStopsActive, sizeNotional,
|
||||
DEFAULT_TARGET_HOLD_HOURS,
|
||||
LEG_COMPLETION_EPSILON,
|
||||
} from '@/lib/trading-logic';
|
||||
|
||||
@@ -86,6 +88,7 @@ export async function GET() {
|
||||
let effectiveProfitTarget: number | null = null;
|
||||
let nextStop: number | null = null;
|
||||
let dllRoom: number | null = null;
|
||||
let holdEstimate: AccountState['holdEstimate'] = null;
|
||||
if (cfg && effective && !isDead) {
|
||||
const equityProfit = cash.amount - cfg.accountSize;
|
||||
const plan = computeDayPlan(
|
||||
@@ -121,6 +124,40 @@ export async function GET() {
|
||||
if (Number.isFinite(stop) && stop > 0) nextStop = Math.round(stop * 100) / 100;
|
||||
}
|
||||
|
||||
// DXtrade: size the next leg the way fireLeg would, on the
|
||||
// instrument the selector last picked and today's measured
|
||||
// volatility, to show how long it is expected to hold.
|
||||
const pick = (client as { lastPickedContract?: PlatformContract | null }).lastPickedContract;
|
||||
if (plan && plan.remainingToday > 0 && pick?.stepNotional && pick.volatilityPerHour) {
|
||||
const pct = pick.commissionPct ?? f.commissionPct ?? 0;
|
||||
const obf = obfuscationStopDollars(cfg, dailyPnL, plan, combined.tradeDate, plan.remainingToday);
|
||||
const room = dllRoom;
|
||||
const sizing = sizeNotional({
|
||||
leg: plan.remainingToday,
|
||||
stopDollarsAt: (n) => Math.min(
|
||||
Number.isFinite(obf) && obf > 0 ? obf : Infinity,
|
||||
room !== null && dll > 0
|
||||
? dllStopDollars({ room, dailyLossLimit: dll, entryFee: (pct / 100) * n, commissionPct: pct })
|
||||
: Infinity,
|
||||
),
|
||||
volatilityPerHour: pick.volatilityPerHour,
|
||||
targetHoldHours: f.targetHoldHours || DEFAULT_TARGET_HOLD_HOURS,
|
||||
commissionPct: pct,
|
||||
spreadFraction: pick.spreadFraction,
|
||||
financingPctPerDay: f.financingPctPerDay ?? 0,
|
||||
maxNotional: cfg.maxNotional ?? 0,
|
||||
});
|
||||
if (sizing) {
|
||||
holdEstimate = {
|
||||
hours: Math.round(sizing.predictedHoldHours * 10) / 10,
|
||||
symbol: String(pick.id),
|
||||
notional: Math.round(sizing.notional),
|
||||
volatilityPerDay: pick.volatilityPerHour * Math.sqrt(24),
|
||||
winProbability: sizing.winProbability,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Effective profit target = max(stage target, consistency realTarget).
|
||||
// If a big day forces the consistency rule, the account must reach maxDay/consistency
|
||||
// in total trading profit for the stage, not just profitTarget.
|
||||
@@ -228,6 +265,7 @@ export async function GET() {
|
||||
dailyTarget,
|
||||
nextStop,
|
||||
dllRoom,
|
||||
holdEstimate,
|
||||
remainingToday,
|
||||
effectiveProfitTarget,
|
||||
dailyPnL,
|
||||
|
||||
@@ -390,6 +390,15 @@ function AccountRow({ account, firm, hideDead, privacy, onAssigned }: {
|
||||
{typeof account.dllRoom === 'number' && (
|
||||
<span className="text-slate-500" title="Hard daily loss limit: what the firm's day may still lose before a breach. 10% of the limit is never spent."> · DLL room ${fmt(account.dllRoom)}</span>
|
||||
)}
|
||||
{account.holdEstimate && (
|
||||
<span
|
||||
className="text-slate-500"
|
||||
title={`Next leg on ${account.holdEstimate.symbol}: $${Math.round(account.holdEstimate.notional).toLocaleString()} notional at today's measured volatility of ${(account.holdEstimate.volatilityPerDay * 100).toFixed(2)}%/day`
|
||||
+ (account.holdEstimate.winProbability !== null ? `, wins ~${Math.round(account.holdEstimate.winProbability * 100)}% of the time` : '')}
|
||||
>
|
||||
{' · hold ~'}{account.holdEstimate.hours < 48 ? `${Math.round(account.holdEstimate.hours)}h` : `${(account.holdEstimate.hours / 24).toFixed(1)}d`}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user