Vest: a crypto leg's stop holds back the engine's default slippage, not NDX's

Crypto (exchangeInfo asset "crypto": BTC, ETH, SOL, HYPE) trades on weekends
when NDX, SPX and GC are shut, but its stop slippage on Vest is unmeasured, and
its book is a fraction of NDX's: about $10M an hour on a weekend against $600M,
with a batch's stops firing together. Its stops now leave 0.1% of the
position unspent (the engine's default) instead of the 0.02% measured on NDX
stop-outs; every other symbol keeps 0.02%.

The share rides on the picked contract and is used wherever a Vest leg's stop
is sized: the batch's plan, the leg's own stop, the dashboard's next stop, and
the 7:55 PM ET reset guard, which now sums each open position's own share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-30 17:26:53 -05:00
co-authored by Claude Opus 5.5
parent 618976bf5f
commit 10da0ca83e
8 changed files with 94 additions and 16 deletions
+12 -5
View File
@@ -53,7 +53,7 @@ import { vestSessionFor, dropVestSession, listVestSessions, refuseVestSession }
import {
vestAccountId, vestAccountName, vestDayOf, lastVestResetMs, nextVestResetMs, parseCapitalAccount, parsePosition, parseOpenOrder,
parseTxn, bookTxns, awaitingLegs, unknownOpenFill, unrealizedPnl, hourlyVolatility, marketOpen, longClosure, num, toMs, formatDecimal, resetSafeStop,
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_STOP_SLIPPAGE_FRACTION,
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_STOP_SLIPPAGE_FRACTION, vestStopSlippageFraction,
vestStatusName, awaitingActivation,
VestApiError, VestNotSentError, nothingWentIn, tokenForRequest, settleClaims, readClaims, payoutDaysOf, payoutsSince,
} from './vest-helpers';
@@ -155,6 +155,9 @@ interface SymbolSpec {
marginRate: number;
tradingStatus: string;
capitalTradingStatus: string;
/** exchangeInfo's asset class: "crypto", "forex", "stock" (indices and
* commodities too). */
asset: string;
}
export interface VestConfig {
@@ -558,6 +561,7 @@ export class VestClient implements PlatformClient {
marginRate: capitalIm && capitalIm > 0 ? capitalIm : 0,
tradingStatus: String(s.tradingStatus ?? ''),
capitalTradingStatus: String(s.capitalTradingStatus ?? s.tradingStatus ?? ''),
asset: String(s.asset ?? ''),
});
}
if (next.size > 0) {
@@ -1230,13 +1234,15 @@ export class VestClient implements PlatformClient {
|| (x.currentStop !== null && y.currentStop !== null ? lossAt(x.pos, x.currentStop) - lossAt(y.pos, y.currentStop) : 0)
|| x.pos.openedAt - y.pos.openedAt || x.pos.positionId.localeCompare(y.pos.positionId));
let committed = 0;
let notionalSoFar = 0;
// The slippage reserve the positions so far hold together, in
// dollars: each its own symbol's share of its notional.
let slippageSoFar = 0;
for (const { pos, currentStop } of held) {
const spec = specs.get(pos.symbol);
if (!spec) {
// Can't be fitted without its tick, but it still holds
// its loss against the room the others share.
notionalSoFar += pos.quantity * pos.openPrice;
slippageSoFar += this.stopSlippageFraction * pos.quantity * pos.openPrice;
if (currentStop !== null) committed += lossAt(pos, currentStop);
else if (!guard.unfittable.has(pos.positionId)) {
guard.unfittable.add(pos.positionId);
@@ -1248,13 +1254,13 @@ export class VestClient implements PlatformClient {
const mark = this.marks.get(pos.symbol)?.mark ?? NaN;
const stopLeg = pos.stopLosses.find((l) => l.status === 'awaiting') ?? null;
const notional = pos.quantity * (Number.isFinite(mark) ? mark : pos.openPrice);
notionalSoFar += notional;
slippageSoFar += vestStopSlippageFraction(spec.asset) * notional;
const exitFee = spec.takerFee * notional;
const decision = resetSafeStop({
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, this.stopSlippageFraction),
reserve: dllReserve(cash.amount * a.maxDailyLossPct, slippageSoFar, 1),
exitFeeRate: spec.takerFee, currentStop, tick: spec.tick,
});
const stopAt = decision.action === 'stop' ? decision.price : decision.action === 'none' ? currentStop : null;
@@ -2022,6 +2028,7 @@ export class VestClient implements PlatformClient {
minContracts: 1,
marginRate: spec.marginRate || undefined,
closesAt: gateAt ?? undefined,
stopSlippageFraction: vestStopSlippageFraction(spec.asset),
},
});
}