Vest: stops reserve 0.02% of the position for slippage, not 0.1%
A platform may now state its own stop slippage share (PlatformClient.stopSlippageFraction); the daily-loss reserve is the larger of 10% of the limit and that share of the position. Vest's 9 measured stop-outs filled 0.9 points past the trigger on average and 2.25 at worst, against the ~30 points 0.1% held back. On a $1,400 day with ~$495k of NDX the stop goes from ~$880 to ~$1,230, and the leg sizes up for the same hold. Sizing, the placed stop, the dashboard's next stop and the reset guard all use it; other platforms keep 0.1%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f0c1569d96
commit
beddbdcf6c
+2
-1
@@ -1399,6 +1399,7 @@ function sizeLeg(item: SizingItem, contract: LegContract, legAmount: number, tra
|
||||
maxLossRoom: floorRoom,
|
||||
obfuscationDollars: obfuscation,
|
||||
availableFunds: live.availableFunds ?? live.amount,
|
||||
slippageFraction: item.client.stopSlippageFraction,
|
||||
});
|
||||
return { ...plan, pct, obfuscation, maxLossRoom: floorRoom };
|
||||
}
|
||||
@@ -1545,7 +1546,7 @@ async function fireLeg(
|
||||
const dllFee = pct > 0 ? entryFee : (measured ?? MAX_COMMISSION_PER_CONTRACT * contracts);
|
||||
const notional = fill.price * pv * contracts;
|
||||
const dllDollars = dllOn && !plan.lastStand
|
||||
? dllStopDollars({ room: item.dllRoom, dailyLossLimit: dll, entryFee: dllFee, commissionPct: pct, notional })
|
||||
? dllStopDollars({ room: item.dllRoom, dailyLossLimit: dll, entryFee: dllFee, commissionPct: pct, notional, slippageFraction: client.stopSlippageFraction })
|
||||
: Infinity;
|
||||
const ticks = stopTicks({ obfuscationDollars: obfuscation, dllDollars, pointValue: pv, contracts, tickSize: legContract.tickSize });
|
||||
if (ticks === 0) {
|
||||
|
||||
@@ -100,6 +100,7 @@ export function rankNextLeg(o: {
|
||||
maxLossRoom: floorRoom,
|
||||
obfuscationDollars: o.obfuscationDollars,
|
||||
availableFunds: funds,
|
||||
slippageFraction: o.client.stopSlippageFraction,
|
||||
});
|
||||
const p = plan(true);
|
||||
if (!p.sizing || p.cost === null) return [];
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* The hold this machine sizes DXtrade legs for. Every person's machine would
|
||||
* otherwise size a like account in a like state to the same position as
|
||||
* everyone else's; each person's own draw (personHoldHours) puts their sizes
|
||||
* within ±PERSON_SIZE_SPREAD of the plan instead, the same for all of their
|
||||
* accounts and new each trade date.
|
||||
* The hold this machine sizes DXtrade and Vest legs for. Every person's
|
||||
* machine would otherwise size a like account in a like state to the same
|
||||
* position as everyone else's; each person's own draw (personHoldHours) puts
|
||||
* their sizes within ±PERSON_SIZE_SPREAD of the plan instead, the same for
|
||||
* all of their accounts and new each trade date.
|
||||
*/
|
||||
|
||||
import { getSetting } from './db';
|
||||
@@ -15,7 +15,7 @@ function personKey(): string {
|
||||
return getSetting('instance_id') || getSetting('instance_name') || '';
|
||||
}
|
||||
|
||||
/** The hold a DXtrade firm's legs are sized for on this machine today. */
|
||||
/** The hold a DXtrade or Vest firm's legs are sized for on this machine today. */
|
||||
export function sizingHoldHours(
|
||||
firm: { targetHoldHours?: number } | null | undefined,
|
||||
tradeDate: string = currentTradeDateCT(),
|
||||
|
||||
@@ -74,6 +74,11 @@ export interface PlatformClient {
|
||||
fundTransactions: { [accountId: number]: { date: string; amount: number }[] };
|
||||
priorProfit: { [accountId: number]: number };
|
||||
autoLiqThresholds: { [accountId: number]: number };
|
||||
/** Share of a position's notional a stop may fill past its price — the
|
||||
* part of the daily-loss reserve that grows with size (dllReserve).
|
||||
* Only where this platform's stops are measured to fill closer than
|
||||
* DLL_SLIPPAGE_FRACTION, the default. */
|
||||
stopSlippageFraction?: number;
|
||||
|
||||
fetchDaysComplete: boolean;
|
||||
syncComplete: boolean;
|
||||
|
||||
+10
-6
@@ -1327,12 +1327,14 @@ export function obfuscationStopDollars(
|
||||
* that fills worse than its price (gaps, thin weekend books). */
|
||||
export const DLL_RESERVE_FRACTION = 0.10;
|
||||
/** …or this share of the position's notional, when that is more: a stop's
|
||||
* slippage grows with the position, a flat reserve doesn't. */
|
||||
* slippage grows with the position, a flat reserve doesn't. A platform whose
|
||||
* stops are known to fill closer states its own (stopSlippageFraction). */
|
||||
export const DLL_SLIPPAGE_FRACTION = 0.001;
|
||||
|
||||
/** The part of the day's room a stop leaves unspent. */
|
||||
export function dllReserve(dailyLossLimit: number, notional = 0): number {
|
||||
return Math.max(DLL_RESERVE_FRACTION * dailyLossLimit, DLL_SLIPPAGE_FRACTION * Math.max(0, notional));
|
||||
export function dllReserve(dailyLossLimit: number, notional = 0, slippageFraction = DLL_SLIPPAGE_FRACTION): number {
|
||||
const share = Number.isFinite(slippageFraction) && slippageFraction >= 0 ? slippageFraction : DLL_SLIPPAGE_FRACTION;
|
||||
return Math.max(DLL_RESERVE_FRACTION * dailyLossLimit, share * Math.max(0, notional));
|
||||
}
|
||||
/** Smallest daily-loss stop worth trading. Under a fair walk a leg of L with a
|
||||
* stop of S wins S/(S + L) of the time — about 9% for $50 against $500 — so
|
||||
@@ -1454,10 +1456,10 @@ export function dllRoom(o: {
|
||||
* reset has its entry fee on the old day's books, and the swap takes no more
|
||||
* than that slot on the new day's.
|
||||
*/
|
||||
export function dllStopDollars(o: { room: number; dailyLossLimit: number; entryFee: number; commissionPct: number; notional?: number }): number {
|
||||
export function dllStopDollars(o: { room: number; dailyLossLimit: number; entryFee: number; commissionPct: number; notional?: number; slippageFraction?: number }): number {
|
||||
if (!Number.isFinite(o.room)) return Infinity;
|
||||
const r = Math.max(0, o.commissionPct) / 100;
|
||||
return (o.room - dllReserve(o.dailyLossLimit, o.notional) - 2 * o.entryFee - 0.01) / (1 + r);
|
||||
return (o.room - dllReserve(o.dailyLossLimit, o.notional, o.slippageFraction) - 2 * o.entryFee - 0.01) / (1 + r);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1765,6 +1767,8 @@ export function planLegSize(o: {
|
||||
/** Infinity (or ≤ 0) = no obfuscation stop. */
|
||||
obfuscationDollars: number;
|
||||
availableFunds?: number;
|
||||
/** The platform's stopSlippageFraction; default DLL_SLIPPAGE_FRACTION. */
|
||||
slippageFraction?: number;
|
||||
}): {
|
||||
lastStand: boolean;
|
||||
cap: number;
|
||||
@@ -1780,7 +1784,7 @@ export function planLegSize(o: {
|
||||
obf,
|
||||
!dllOn ? Infinity
|
||||
: stand ? breachDistance({ dllRoom: o.dllRoom, maxLossRoom: o.maxLossRoom, entryFee: r * notional })
|
||||
: dllStopDollars({ room: o.dllRoom, dailyLossLimit: o.dailyLossLimit, entryFee: r * notional, commissionPct: o.commissionPct, notional }),
|
||||
: dllStopDollars({ room: o.dllRoom, dailyLossLimit: o.dailyLossLimit, entryFee: r * notional, commissionPct: o.commissionPct, notional, slippageFraction: o.slippageFraction }),
|
||||
);
|
||||
const cap = notionalCap({ marginRate: o.contract.marginRate, availableFunds: o.availableFunds });
|
||||
const step = o.contract.stepNotional ?? 0;
|
||||
|
||||
+3
-2
@@ -53,7 +53,7 @@ import { vestSessionFor, dropVestSession, listVestSessions, refuseVestSession }
|
||||
import {
|
||||
vestAccountId, vestAccountName, vestDayOf, lastVestResetMs, nextVestResetMs, parseCapitalAccount, parsePosition, parseOpenOrder,
|
||||
parseTxn, bookTxns, awaitingLegs, unrealizedPnl, hourlyVolatility, marketOpen, num, toMs, formatDecimal, resetSafeStop,
|
||||
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_TXN,
|
||||
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_TXN, VEST_STOP_SLIPPAGE_FRACTION,
|
||||
} from './vest-helpers';
|
||||
import type { VestCapitalAccount, VestPosition, VestOpenOrder, VestTxn, VestTrade } from './vest-helpers';
|
||||
|
||||
@@ -174,6 +174,7 @@ export class VestClient implements PlatformClient {
|
||||
public fundTransactions: { [accountId: number]: { date: string; amount: number }[] } = {};
|
||||
public priorProfit: { [accountId: number]: number } = {};
|
||||
public autoLiqThresholds: { [accountId: number]: number } = {};
|
||||
public readonly stopSlippageFraction = VEST_STOP_SLIPPAGE_FRACTION;
|
||||
|
||||
public fetchDaysComplete = false;
|
||||
public syncComplete = false;
|
||||
@@ -1010,7 +1011,7 @@ export class VestClient implements PlatformClient {
|
||||
side: pos.side, quantity: pos.quantity, openPrice: pos.openPrice, mark,
|
||||
balance: cash.amount - committed, dailyLossPct: a.maxDailyLossPct, maxDrawdownFloor: a.maxDrawdownLimit,
|
||||
floorBalance: cash.amount,
|
||||
reserve: dllReserve(cash.amount * a.maxDailyLossPct, notionalSoFar),
|
||||
reserve: dllReserve(cash.amount * a.maxDailyLossPct, notionalSoFar, this.stopSlippageFraction),
|
||||
exitFeeRate: spec.takerFee, currentStop, tick: spec.tick,
|
||||
});
|
||||
const stopAt = decision.action === 'stop' ? decision.price : decision.action === 'none' ? currentStop : null;
|
||||
|
||||
@@ -101,6 +101,12 @@ export function nextVestResetMs(now: number): number {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
/** Vest's stopSlippageFraction: 0.02% of the position, about 6 NDX points.
|
||||
* Measured 2026-10-27 on 9 bot stop-outs (Me, $74k–$132k): fills 0.9 points
|
||||
* past the trigger on average, 2.25 at worst — the 0.1% default held back
|
||||
* about 30. The flat 10% of the limit still applies under it. */
|
||||
export const VEST_STOP_SLIPPAGE_FRACTION = 0.0002;
|
||||
|
||||
/**
|
||||
* What to do with an open position before the daily reset so the reset can't
|
||||
* breach it. At 20:00 ET Vest captures the realized balance (collateral in,
|
||||
|
||||
Reference in New Issue
Block a user