DXtrade: count closed-trade P&L, week-long volatility, broker leverage caps, swap in the take-profit

- Transfers: a close books its SETTLEMENT and COMMISSION under one
  transaction code, and keying by the code alone let the commission
  overwrite the P&L. Keys now carry the type, so the day-start balance,
  daily-loss room, realized P&L, daily history and books check all see
  closed-trade profit and loss.
- Volatility from a week of hourly closes (was 48 h), still measured once
  per trade date, so one quiet weekend can't triple a hold estimate.
- Each symbol's margin rate from the account's instrument details caps
  the position at available funds / rate (Tradeify: 5:1 crypto, 2:1
  stocks); the account type's max notional stays as an optional ceiling.
- Firms with overnight financing add the swap a winning leg is expected
  to pay (the winners' mean hold, (A²+2AB)/3σ²) to its take-profit, and
  the sizing and hold estimate count it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-26 18:11:42 -05:00
co-authored by Claude Opus 5.5
parent 675b4c89f8
commit a2d9df5ba4
10 changed files with 309 additions and 81 deletions
+29 -11
View File
@@ -14,6 +14,7 @@ import { getAccountConfig } from './account-type-resolver';
import {
combineCmeSessions, computeDayPlan, groupLegFraction, pickLegAmount, resolveEffectiveConfig, currentTradeDateCT,
obfuscationStopDollars, takeProfitPlan, percentFee, dllRoom, dllStopDollars, dllViable, stopTicks, sizeNotional,
notionalCap, expectedSwapToWin,
DEFAULT_TARGET_HOLD_HOURS,
isNoTradeTime, isPastSessionClose,
BIG_DAY_THRESHOLD, LEG_COMPLETION_EPSILON, MAX_LEGS_PER_DAY, POINT_VALUES,
@@ -968,10 +969,13 @@ async function flattenAndFail(
* Size: futures trade one contract per $1,000 of leg; a platform that prices
* positions (stepNotional) sizes by notional instead — whatever makes the
* leg's expected hold the firm's target, from the instrument's recent
* volatility — under the account type's notional cap.
* volatility — under the account type's notional cap and the instrument's
* margin rate (the firm's leverage).
*
* Fees: a percent-of-notional firm's fee is computed off the fill; otherwise
* it is read off the realized-P&L change a second after it.
* it is read off the realized-P&L change a second after it. A firm that
* charges overnight swap has the swap a winning leg is expected to pay added
* to its take-profit.
*
* Stop: the tighter of the obfuscation stop and the hard daily-loss stop.
* With a daily loss limit, no position is left without one — no room for a
@@ -1026,6 +1030,12 @@ async function fireLeg(
const stepNotional = legContract.stepNotional ?? 0;
if (stepNotional > 0) {
const liveCash = client.accountCashBalances[acc.id] ?? item.cash;
const cap = notionalCap({
maxNotional: cfg.maxNotional ?? 0,
marginRate: legContract.marginRate,
availableFunds: liveCash.availableFunds ?? liveCash.amount,
});
const obfStop = Number.isFinite(obfuscation) && obfuscation > 0 ? obfuscation : Infinity;
const stopDollarsAt = (notional: number) => Math.min(
obfStop,
@@ -1039,14 +1049,13 @@ async function fireLeg(
commissionPct: pct,
spreadFraction: legContract.spreadFraction,
financingPctPerDay: firm.financingPctPerDay ?? 0,
maxNotional: cfg.maxNotional ?? 0,
maxNotional: cap,
});
if (!sizing) {
throw new Error(`no position size for ${legContract.name} — its recent volatility is unknown`);
}
contracts = Math.max(1, Math.round(sizing.notional / stepNotional));
const maxNotional = cfg.maxNotional ?? 0;
if (maxNotional > 0) contracts = Math.min(contracts, Math.max(1, Math.floor(maxNotional / stepNotional)));
if (cap > 0) contracts = Math.min(contracts, Math.max(1, Math.floor(cap / stepNotional)));
if ((legContract.maxContracts ?? 0) > 0) contracts = Math.min(contracts, legContract.maxContracts!);
contracts = Math.max(contracts, legContract.minContracts ?? 1);
// The real size is known now: re-check the daily-loss room with it.
@@ -1085,11 +1094,6 @@ async function fireLeg(
measured = entryCommissionMeasured(item.cash.realizedPnL, updatedCash.realizedPnL, contracts);
entryFee = measured ?? FALLBACK_COMMISSION_PER_CONTRACT * contracts;
}
const tp = takeProfitPlan({
side: action, entry: fill.price, leg: legAmount, pointValue: pv, contracts,
tickSize: legContract.tickSize, commissionPct: pct, entryCommission: entryFee,
});
// The stop. On a measured-fee firm whose fee didn't show, the daily-loss
// stop reserves the worst per-contract fee, not the $2.50 fallback.
const dllFee = pct > 0 ? entryFee : (measured ?? MAX_COMMISSION_PER_CONTRACT * contracts);
@@ -1100,6 +1104,20 @@ async function fireLeg(
`daily-loss room ($${round2(item.dllRoom)}) doesn't cover a one-tick stop`);
}
// The take-profit clears the leg, both fees and — where the firm charges
// overnight swap — what a winning leg is expected to pay in it.
const swapReserve = expectedSwapToWin({
distance: legAmount + 2 * entryFee,
stopDollars: ticks !== null ? ticks * legContract.tickSize * pv * contracts : Infinity,
notional: fill.price * pv * contracts,
volatilityPerHour: legContract.volatilityPerHour,
financingPctPerDay: firm.financingPctPerDay ?? 0,
});
const tp = takeProfitPlan({
side: action, entry: fill.price, leg: legAmount + swapReserve, pointValue: pv, contracts,
tickSize: legContract.tickSize, commissionPct: pct, entryCommission: entryFee,
});
let exitOrder: { orderId?: number };
let stopPrice: number | null = null;
if (ticks !== null) {
@@ -1143,7 +1161,7 @@ async function fireLeg(
? ` / stop @ ${stopPrice}`
+ (dllOn ? ` (dll room $${round2(item.dllRoom)} → $${round2(dllDollars)}${Number.isFinite(obfuscation) && obfuscation > 0 ? `, obf $${round2(obfuscation)}` : ''})` : '')
: '';
console.log(`${tag} ${acc.name} (${item.firmName}) ${action} ${contracts}x${instrumentLabel} @ ${fill.price} | leg $${legAmount} of day $${item.plan.dayTarget} (remaining $${item.plan.remainingToday}) [${item.plan.path}${item.drain ? ' drain' : ''}] (+$${tp.totalCommission.toFixed(2)} comm)${sizeNote} | exit @ ${tp.exitPrice}${stopNote} (orderId=${exitOrder.orderId})`);
console.log(`${tag} ${acc.name} (${item.firmName}) ${action} ${contracts}x${instrumentLabel} @ ${fill.price} | leg $${legAmount} of day $${item.plan.dayTarget} (remaining $${item.plan.remainingToday}) [${item.plan.path}${item.drain ? ' drain' : ''}] (+$${tp.totalCommission.toFixed(2)} comm${swapReserve > 0 ? `, +$${swapReserve.toFixed(2)} expected swap` : ''})${sizeNote} | exit @ ${tp.exitPrice}${stopNote} (orderId=${exitOrder.orderId})`);
return {
account: acc.name,