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:
co-authored by
Claude Opus 5.5
parent
675b4c89f8
commit
a2d9df5ba4
+29
-11
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user