Files
autofirmer-expanded/scripts/check-vest.mjs
T
Brandon LiandClaude Opus 5.5 ec3e6ea388 Vest: no leg opens into a market close, and accounts plan on Vest's day
Market close: a Vest entry now needs its symbol's market to stay open
for RESET_CLEAR_HOLDS holds of the leg (clearsClose), the clearance the
8 PM ET reset already gets: at the target hold, and at the leg's sized
hold when a leverage cap stretches it. That covers the weekly close and
the daily maintenance hour as Vest's market-hours periods list them.
Before a closure that runs past an 8 PM ET reset (the weekend, a
holiday: longClosure), the bot's own legs still open in the last 5
minutes are closed at market, loudly; a position the bot didn't open is
named in the log and left alone.

Vest's day: Vest accounts are planned, and their legs and entries
counted, on Vest's day (20:00 ET) instead of the Central date
(tradeDateFor, PlatformClient.tradeDate). Between 5 PM CT and the reset
the Central date counted the open Vest day as a prior day and as today's
realized P&L both ($3,000 to go, $800 made: $2,050 left, not $1,900) and
started the leg count over. Other platforms keep the Central date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 14:12:27 -05:00

426 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Checks for the Vest client's pure helpers (lib/vest-helpers.ts), the
// relayed-session store (lib/vest-session.ts), and the trade date the engine
// plans Vest accounts on (lib/trading-logic.ts).
//
// npm run check
//
// These files import only Node built-ins, so Node strips their types and loads
// them directly; .mjs keeps this file out of Next's type check. The samples are
// shaped like the gateway's real answers (captured 2026-10-26).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
isVestWalletAddress, vestAccountId, num, toMs, formatDecimal, vestDayOf, lastVestResetMs, parseCapitalAccount,
vestAccountName, parsePosition, awaitingLegs, parseOpenOrder, unrealizedPnl, parseTxn, bookTxns, txnKind, VEST_TXN,
hourlyVolatility, marketOpen, MIN_HOURLY_RETURNS, nextVestResetMs, resetSafeStop, parseHistoryPosition, jwtClaimStrings,
accountScopedToken, longClosure,
} from '../lib/vest-helpers.ts';
import { putVestSession, vestSessionFor, dropVestSession, listVestSessions, refuseVestSession } from '../lib/vest-session.ts';
import { computeDayPlan, currentTradeDateCT, tradeDateFor } from '../lib/trading-logic.ts';
const iso = (s) => Date.parse(s);
test('wallet addresses and account ids', () => {
assert.equal(isVestWalletAddress('0x7ce10771819f80130e204ddb5ca1522e48a9b451'), true);
assert.equal(isVestWalletAddress('0x7CE10771819F80130E204DDB5CA1522E48A9B451'), true);
assert.equal(isVestWalletAddress('0x7ce1'), false);
assert.equal(isVestWalletAddress(''), false);
const id = vestAccountId('843f60e2-e013-5a0e-8b6c-face84bc5509');
assert.equal(id, vestAccountId('843F60E2-E013-5A0E-8B6C-FACE84BC5509'));
assert.notEqual(id, vestAccountId('102566b2-515a-51c5-9ad7-4a7eae0a6a2a'));
assert.ok(id >= 2 ** 50 && Number.isSafeInteger(id));
});
test('numbers, times and decimals as the gateway writes them', () => {
assert.equal(num('49968.511115'), 49968.511115);
assert.equal(num(''), undefined);
assert.equal(num(null), undefined);
assert.equal(toMs(1793035089), 1793035089000);
assert.equal(toMs(1793035089277), 1793035089277);
assert.equal(toMs('2026-10-26T18:41:02.886Z'), iso('2026-10-26T18:41:02.886Z'));
assert.ok(Number.isNaN(toMs(undefined)));
assert.equal(formatDecimal(29554.5, 2), '29554.5');
assert.equal(formatDecimal(50, 2), '50');
assert.equal(formatDecimal(3 * 0.0001, 4), '0.0003');
assert.equal(formatDecimal(42.65449999, 4), '42.6545');
assert.equal(formatDecimal(1e-7, 4), '0');
});
test("Vest's day turns at 20:00 New York, summer and winter", () => {
// EDT (UTC−4): 20:00 ET is 00:00 UTC.
assert.equal(vestDayOf(iso('2026-10-26T23:59:00Z')), '2026-10-26');
assert.equal(vestDayOf(iso('2026-10-27T00:00:00Z')), '2026-10-27');
assert.equal(vestDayOf(iso('2026-10-26T22:05:00Z')), '2026-10-26'); // the bots' 6:05 PM ET open
// EST (UTC−5): 20:00 ET is 01:00 UTC.
assert.equal(vestDayOf(iso('2026-12-01T00:30:00Z')), '2026-11-30');
assert.equal(vestDayOf(iso('2026-12-01T01:00:00Z')), '2026-12-01');
// Month and year ends.
assert.equal(vestDayOf(iso('2026-12-31T23:00:00Z')), '2026-12-31');
assert.equal(vestDayOf(iso('2027-01-01T01:30:00Z')), '2027-01-01');
assert.equal(lastVestResetMs(iso('2026-10-26T18:00:00Z')), iso('2026-10-26T00:00:00Z'));
assert.equal(lastVestResetMs(iso('2026-10-27T00:00:00Z')), iso('2026-10-27T00:00:00Z'));
assert.equal(lastVestResetMs(iso('2026-12-01T00:30:00Z')), iso('2026-11-30T01:00:00Z'));
assert.equal(lastVestResetMs(iso('2026-12-01T01:00:01Z')), iso('2026-12-01T01:00:00Z'));
// Across the November switch back (Nov 1 2026, 2 AM ET).
assert.equal(lastVestResetMs(iso('2026-11-01T12:00:00Z')), iso('2026-11-01T00:00:00Z'));
assert.equal(lastVestResetMs(iso('2026-11-02T12:00:00Z')), iso('2026-11-02T01:00:00Z'));
});
const evalAccount = {
payment_id: 'tailoredpay:vest-markets:production:12727557177',
id: 'e7f69717-bd18-5cb7-9466-673cb283d0b2',
plan_id: '86bb03f9-7422-513c-8d5c-c546cb8d221d',
account_type: 2, stage: 1, status: 2,
initial_capital: '50000.00000000', max_drawdown_pct: '0.060000', max_leverage: '5.0000', max_profit_split_pct: '0.8000',
target_equity: '55000.00000000', max_drawdown_limit: '47000.00000000', max_daily_loss_pct: '0.030000',
daily_reset_balance: '50000.00000000000000', daily_reset_equity: '50000.00000000000000',
daily_reset_base_limit: '1500.000000000000000', daily_loss_floor: '48500.00000000000000', daily_reset_at: 1793035043842,
origin: 'eval_purchase', plan_name: 'Vest Capital Evaluation Fee | 10% Goal | 3% Daily Loss | 80/20 | Emerald',
plan_product_type: 'eval', step: 1, steps_total: 1, root_account_id: 'e7f69717-bd18-5cb7-9466-673cb283d0b2',
stages: [
{ ordinal: 1, kind: 'eval', initial_capital: '50000.00000000', profit_target: '5000.00000000', max_drawdown_pct: '0.060000' },
{ ordinal: 2, kind: 'funded', initial_capital: '50000.00000000', profit_target: '', max_drawdown_pct: '0.060000' },
],
attempt_index: 34, activated_at: 1793035043834, created_at: 1793035043756, updated_at: 1793035043843,
};
test('capital accounts parse, and are named for their rules', () => {
const a = parseCapitalAccount(evalAccount);
assert.equal(a.id, evalAccount.id);
assert.equal(a.kind, 'eval');
assert.equal(a.status, 2);
assert.equal(a.initialCapital, 50000);
assert.equal(a.profitTarget, 5000);
assert.equal(a.maxDrawdownLimit, 47000);
assert.equal(a.dailyLossFloor, 48500);
assert.equal(a.maxDailyLossPct, 0.03);
assert.equal(a.dailyResetAt, 1793035043842);
assert.equal(vestAccountName(a), 'V50K-E-D3-T10-34');
assert.equal(vestAccountName(parseCapitalAccount({ ...evalAccount, stage: 2 })), 'V50K-F-D3-34');
assert.equal(vestAccountName(parseCapitalAccount({ ...evalAccount, max_daily_loss_pct: '0' })), 'V50K-E-D0-T10-34');
assert.equal(vestAccountName(parseCapitalAccount({
...evalAccount, initial_capital: '5000.00000000', attempt_index: 12, stage: 2, steps_total: 2,
stages: [
{ ordinal: 1, kind: 'eval', profit_target: '500.00000000' },
{ ordinal: 2, kind: 'eval', profit_target: '250.00000000' },
{ ordinal: 3, kind: 'funded', profit_target: '' },
],
})), 'V5K-E-D3-T5-12');
assert.equal(vestAccountName(parseCapitalAccount({
...evalAccount, plan_product_type: 'instant_funded', initial_capital: '25000.00000000', max_daily_loss_pct: '0', attempt_index: 7,
stages: [{ ordinal: 1, kind: 'funded', profit_target: '' }],
})), 'V25K-I-7');
assert.equal(vestAccountName(parseCapitalAccount({
...evalAccount, plan_product_type: 'instant_funded', initial_capital: '500.00000000', attempt_index: 1,
stages: [{ ordinal: 1, kind: 'funded', profit_target: '' }],
})), 'V500-I-1');
assert.equal(parseCapitalAccount({ nope: true }), null);
});
const openPosition = {
positionId: 'afe96d7e-f5ac-4b8a-aae8-f5b63b74e508', accountId: '843f60e2-e013-5a0e-8b6c-face84bc5509', accountType: 2,
symbol: 'NDX-USD-PERP', side: 'short', quantity: '42.6545', openPrice: '29529.250000000', closePrice: '290.250000000',
openDate: 1793035089277, pnl: '0', fee: '31.488885', funding: '-5.423271000000000000', collateral: '24944.027426', leverage: '50',
createdAt: 1793035089244, updatedAt: 1793037621104,
stopLosses: [{ id: 'e22311d2-fbd9-4ab8-b65f-c2921f58fefc', triggerPrice: '29554.500000000', sizeMode: 'full_position', executionType: 'market', status: 'awaiting' }],
takeProfits: [],
};
test('positions, their legs and unrealized P&L', () => {
const p = parsePosition(openPosition);
assert.equal(p.side, 'short');
assert.equal(p.quantity, 42.6545);
assert.equal(p.collateral, 24944.027426);
assert.equal(p.openedAt, 1793035089277);
assert.equal(awaitingLegs(p).length, 1);
assert.equal(awaitingLegs({ ...p, stopLosses: [{ ...p.stopLosses[0], status: 'triggered' }] }).length, 0);
// Short 42.6545 from 29529.25, marked at 29477.50: +51.75 points each.
assert.ok(Math.abs(unrealizedPnl(p, 29477.5) - 42.6545 * 51.75) < 1e-6);
assert.equal(unrealizedPnl(p, NaN), 0);
assert.equal(parsePosition({ ...openPosition, quantity: '0' }), null);
assert.equal(parseOpenOrder({ order_id: 'o1', account_id: 'a1', position_id: 'p1', symbol: 'GC-PERP', created_at: 1 }).positionId, 'p1');
assert.equal(parseOpenOrder({ symbol: 'x' }), null);
});
test('transactions book into daily P&L and fund rows by Vest day', () => {
assert.equal(txnKind(VEST_TXN.FEE), 'trade');
assert.equal(txnKind(VEST_TXN.REALIZED_PNL), 'trade');
assert.equal(txnKind(VEST_TXN.FUNDING), 'trade');
assert.equal(txnKind(VEST_TXN.WITHDRAW), 'fund');
assert.equal(txnKind(VEST_TXN.FILL_MARGIN), 'margin');
assert.equal(txnKind(VEST_TXN.ORDER_MARGIN_RELEASE), 'margin');
const w = parseTxn({ account_type: 3, action_id: 'a9', amount: '1200', created_at: iso('2026-10-26T15:00:00Z'), transaction_type: 2 });
assert.equal(w.amount, -1200);
assert.equal(w.kind, 'fund');
assert.equal(parseTxn({ amount: 'x', transaction_type: 9, created_at: 1 }), null);
const rows = [
{ action_id: 'd', amount: '50000', created_at: iso('2026-10-20T14:00:00Z'), transaction_type: 1 },
{ action_id: 'o', amount: '-24944.02', created_at: iso('2026-10-26T17:18:00Z'), transaction_type: 6 },
{ action_id: 'f1', amount: '-31.49', created_at: iso('2026-10-26T17:18:00Z'), transaction_type: 8 },
// After 20:00 ET on the 26th: the 27th's day.
{ action_id: 'r1', amount: '812.5', created_at: iso('2026-10-27T00:30:00Z'), transaction_type: 9 },
{ action_id: 'f2', amount: '-31.2', created_at: iso('2026-10-27T00:30:00Z'), transaction_type: 8 },
{ action_id: 'u1', amount: '-5.42', created_at: iso('2026-10-27T01:00:00Z'), transaction_type: 10 },
].map(parseTxn);
const { daily, funds } = bookTxns(rows);
assert.deepEqual([...funds.entries()], [['2026-10-20', 50000]]);
assert.ok(Math.abs(daily.get('2026-10-26').pnl - -31.49) < 1e-9);
assert.ok(Math.abs(daily.get('2026-10-27').pnl - (812.5 - 31.2 - 5.42)) < 1e-9);
assert.ok(Math.abs(daily.get('2026-10-27').fees - 31.2) < 1e-9);
});
test('hourly volatility leaves out the gaps', () => {
const start = iso('2026-10-19T00:00:00Z');
const kl = [];
let price = 29000;
for (let h = 0; h < 60; h++) {
price *= h % 2 === 0 ? 1.001 : 1 / 1.001;
kl.push([start + h * 3_600_000, '0', '0', '0', String(price), '1']);
}
const base = hourlyVolatility(kl);
assert.ok(base > 0.0009 && base < 0.0011, `σ ${base}`);
// A weekend gap with a 3% jump: not an hour's move, so σ doesn't change.
const gapped = [...kl, [start + 120 * 3_600_000, '0', '0', '0', String(price * 1.03), '1']];
assert.ok(Math.abs(hourlyVolatility(gapped) - base) < 1e-12);
// Any order in, the same σ out.
assert.equal(hourlyVolatility([...kl].reverse()), base);
assert.equal(hourlyVolatility(kl.slice(0, MIN_HOURLY_RETURNS)), null);
});
test('market hours: open stretches, closures and crypto', () => {
const periods = [
{ fromMs: 0, toMs: 100, state: { session: 'unspecified', closure: 'weekly', tradingStatus: 'CLOSE', price: { allow: false } } },
{ fromMs: 100, toMs: 200, state: { session: 'overnight', closure: 'unspecified', tradingStatus: 'OPEN', price: { allow: true } } },
{ fromMs: 200, toMs: 300, state: { session: 'regular', closure: 'unspecified', tradingStatus: 'OPEN', price: { allow: true } } },
{ fromMs: 300, toMs: 400, state: { session: 'unspecified', closure: 'maintenance', tradingStatus: 'OPEN', price: { allow: true } } },
{ fromMs: 400, toMs: 500, state: { session: 'overnight', closure: 'unspecified', tradingStatus: 'OPEN', price: { allow: true } } },
];
assert.deepEqual(marketOpen(periods, 50), { open: false, closesAt: null });
assert.deepEqual(marketOpen(periods, 150), { open: true, closesAt: 300 });
assert.deepEqual(marketOpen(periods, 350), { open: false, closesAt: null });
assert.deepEqual(marketOpen(periods, 450), { open: true, closesAt: 500 });
assert.deepEqual(marketOpen(periods, 600), { open: false, closesAt: null });
assert.deepEqual(marketOpen([], 600), { open: true, closesAt: null });
});
test('market hours: a closure past an 8 PM ET reset is a long one, the maintenance hour is not', () => {
const open = (from, to) => ({ fromMs: iso(from), toMs: iso(to), state: { session: 'overnight', closure: 'unspecified', tradingStatus: 'OPEN', price: { allow: true } } });
const shut = (from, to, closure) => ({ fromMs: iso(from), toMs: iso(to), state: { session: 'unspecified', closure, tradingStatus: closure === 'weekly' ? 'CLOSE' : 'OPEN', price: { allow: closure !== 'weekly' } } });
const week = [
open('2026-10-28T22:00:00Z', '2026-10-29T21:00:00Z'),
shut('2026-10-29T21:00:00Z', '2026-10-29T22:00:00Z', 'maintenance'),
open('2026-10-29T22:00:00Z', '2026-10-30T21:00:00Z'),
shut('2026-10-30T21:00:00Z', '2026-11-01T23:00:00Z', 'weekly'),
open('2026-11-01T23:00:00Z', '2026-11-02T22:00:00Z'),
shut('2026-11-02T22:00:00Z', '2026-11-02T23:00:00Z', 'maintenance'),
open('2026-11-02T23:00:00Z', '2026-11-03T22:00:00Z'),
];
// The weekend: shut through Friday's and Saturday's 8 PM ET resets.
const friday = marketOpen(week, iso('2026-10-30T20:57:00Z'));
assert.deepEqual(friday, { open: true, closesAt: iso('2026-10-30T21:00:00Z') });
assert.equal(longClosure(week, friday.closesAt), true);
// The maintenance hour reopens before the evening's reset, in summer and winter.
const thursday = marketOpen(week, iso('2026-10-29T20:57:00Z'));
assert.equal(thursday.closesAt, iso('2026-10-29T21:00:00Z'));
assert.equal(longClosure(week, thursday.closesAt), false);
assert.equal(longClosure(week, iso('2026-11-02T22:00:00Z')), false);
// A gap in the periods is shut too (a holiday), and this one outlasts the reset.
const early = [open('2026-11-25T14:00:00Z', '2026-11-25T18:00:00Z'), open('2026-11-26T23:00:00Z', '2026-11-27T18:00:00Z')];
assert.equal(longClosure(early, iso('2026-11-25T18:00:00Z')), true);
// Shut as far as the periods go: long. Periods that just end: no close known.
assert.equal(longClosure(week.slice(0, 4), iso('2026-10-30T21:00:00Z')), true);
assert.equal(longClosure(week.slice(0, 3), iso('2026-10-30T21:00:00Z')), false);
assert.equal(longClosure(undefined, iso('2026-10-30T21:00:00Z')), false);
});
test("a Vest account plans on Vest's day: the 5 PM CT roll doesn't count today's P&L twice", () => {
const vest = { tradeDate: vestDayOf };
// 3:30 and 5:30 PM CT (4:30 / 6:30 PM ET): the Central date rolls at
// 5 PM CT, Vest's day only at 8 PM ET.
const before = new Date('2026-10-27T20:30:00Z');
const after = new Date('2026-10-27T22:30:00Z');
assert.equal(currentTradeDateCT(after), '2026-10-28');
assert.equal(tradeDateFor(vest, after), '2026-10-27');
assert.equal(tradeDateFor(vest, new Date('2026-10-28T00:00:00Z')), '2026-10-28');
// Every other platform keeps the Central date.
assert.equal(tradeDateFor(undefined, after), '2026-10-28');
assert.equal(tradeDateFor({}, after), '2026-10-28');
// $3,000 to go, 3 min days of $150, $800 made today: Vest's row for its
// day, and the realized P&L since its reset.
const rows = [{ date: '2026-10-27', pnl: 800 }];
const plan = (date) => computeDayPlan(7, date, 3000, 0, rows, 150, 3, 800, 800);
assert.equal(plan(currentTradeDateCT(before)).remainingToday, 1900);
assert.deepEqual(plan(tradeDateFor(vest, after)), plan(currentTradeDateCT(before)));
// On the Central date the $800 is a prior min day and today's P&L both.
assert.equal(plan(currentTradeDateCT(after)).remainingToday, 2050);
});
test('relayed sessions: taken, kept fresher, dropped, never listed with tokens', () => {
const now = iso('2026-10-26T18:40:00Z');
const jwt = (n) => `eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIke${n}In0.sig${n}`;
const wallet = '0x7CE10771819F80130E204DDB5CA1522E48A9B451';
assert.equal(putVestSession({ accessToken: 'nope', expiresAt: now + 900_000, primaryAddr: wallet, userId: 'u' }, now), 'accessToken is not a JWT');
assert.equal(putVestSession({ accessToken: jwt(1), expiresAt: now + 10_000, primaryAddr: wallet, userId: 'u' }, now), 'token already expired');
assert.equal(putVestSession({ accessToken: jwt(1), expiresAt: now + 900_000, primaryAddr: '0x12', userId: 'u' }, now), 'primaryAddr is not a wallet address');
assert.equal(putVestSession({ accessToken: jwt(1), expiresAt: now + 900_000, primaryAddr: wallet, userId: 'u' }, now), null);
// An older token relayed late doesn't replace the fresher one.
assert.equal(putVestSession({ accessToken: jwt(0), expiresAt: now + 300_000, primaryAddr: wallet, userId: 'u' }, now), null);
assert.equal(vestSessionFor(wallet.toLowerCase(), now).accessToken, jwt(1));
assert.equal(vestSessionFor(wallet, now + 890_000), null); // under 30s left
const listed = listVestSessions();
assert.equal(listed.length, 1);
assert.equal(listed[0].primaryAddr, wallet.toLowerCase());
assert.equal('accessToken' in listed[0], false);
dropVestSession(wallet, jwt(0)); // not the held one: kept
assert.ok(vestSessionFor(wallet, now));
dropVestSession(wallet, jwt(1));
assert.equal(vestSessionFor(wallet, now), null);
});
test('the next 8 PM ET reset, across both time changes', () => {
assert.equal(nextVestResetMs(iso('2026-10-26T18:00:00Z')), iso('2026-10-27T00:00:00Z'));
assert.equal(nextVestResetMs(iso('2026-10-27T00:00:00Z')), iso('2026-10-28T00:00:00Z'));
assert.equal(nextVestResetMs(iso('2026-10-31T23:59:00Z')), iso('2026-11-01T00:00:00Z'));
// Nov 1 2026, the clocks go back: that evening's reset is 01:00 UTC.
assert.equal(nextVestResetMs(iso('2026-11-01T12:00:00Z')), iso('2026-11-02T01:00:00Z'));
assert.equal(nextVestResetMs(iso('2026-12-01T00:30:00Z')), iso('2026-12-01T01:00:00Z'));
});
test('resetSafeStop: fit an open position into the room the reset will leave', () => {
// A stage-2 leg after a claim: balance $50,000, 3% → the reset floor is
// $48,500 (above the $47k drawdown floor). $150 reserve, a $3 fee: $1,347 to lose.
const base = {
side: 'long', quantity: 4, openPrice: 29500, mark: 29480, balance: 50000, dailyLossPct: 0.03,
maxDrawdownFloor: 47000, reserve: 150, exitFeeRate: 0.000025, tick: 0.25,
};
// No stop at all (a last stand): one comes in at open − 1347/4, rounded up to the tick.
const fee = 0.000025 * 4 * 29480;
const allowed = 50000 - 48500 - 150 - fee;
const want = Math.ceil((29500 - allowed / 4) / 0.25) * 0.25;
assert.deepEqual(resetSafeStop({ ...base, currentStop: null }), { action: 'stop', price: want });
// A wider stop is pulled in; one already tighter is left alone.
assert.deepEqual(resetSafeStop({ ...base, currentStop: 29000 }), { action: 'stop', price: want });
assert.deepEqual(resetSafeStop({ ...base, currentStop: 29300 }), { action: 'none' });
// The mark already past where the stop must go: close.
assert.deepEqual(resetSafeStop({ ...base, mark: 29100, currentStop: null }), { action: 'close' });
// Short: mirrored, rounded down.
const short = resetSafeStop({ ...base, side: 'short', mark: 29510, currentStop: null });
assert.equal(short.action, 'stop');
assert.equal(short.price, Math.floor((29500 + (50000 - 48500 - 150 - 0.000025 * 4 * 29510) / 4) / 0.25) * 0.25);
// No room past the reset at all: close.
assert.deepEqual(resetSafeStop({ ...base, balance: 48600, maxDrawdownFloor: 48600 }), { action: 'close' });
// The drawdown floor binds when it's above the daily one.
const dd = resetSafeStop({ ...base, maxDrawdownFloor: 49000, currentStop: null });
assert.equal(dd.action, 'stop');
assert.ok(dd.price > want);
// A second position on the account gets what the first leaves: $500
// already committed at the first one's stop, from the same $48,500 floor.
const second = resetSafeStop({ ...base, balance: 49500, floorBalance: 50000, currentStop: null });
assert.equal(second.action, 'stop');
assert.equal(second.price, Math.ceil((29500 - (allowed - 500) / 4) / 0.25) * 0.25);
assert.ok(second.price > want);
// ...and nothing when the first takes it all.
assert.deepEqual(resetSafeStop({ ...base, balance: 48600, floorBalance: 50000, currentStop: null }), { action: 'close' });
// With no room left, a position already up keeps going on a stop that
// locks in the shortfall: one already locking in more is left alone...
const up = { ...base, mark: 29560, balance: 48600, floorBalance: 50000 };
assert.deepEqual(resetSafeStop({ ...up, currentStop: 29520 }), { action: 'none' });
// ...one with none gets it, on the profit side, still under the mark...
const shortfall = 48600 - 48500 - 150 - 0.000025 * 4 * 29560;
assert.deepEqual(resetSafeStop({ ...up, currentStop: null }), { action: 'stop', price: Math.ceil((29500 - shortfall / 4) / 0.25) * 0.25 });
assert.ok(resetSafeStop({ ...up, currentStop: null }).price > 29500);
// ...and with no mark to tell, it closes.
assert.deepEqual(resetSafeStop({ ...up, mark: NaN, currentStop: null }), { action: 'close' });
});
test('parseHistoryPosition: a closed position from GET /v3/positions', () => {
// Shaped like the app's History tab reads it (camelCase, decimal strings).
const t = parseHistoryPosition({
positionId: 'bdda9024-63a9-4abc-a5a8-f83df8ba030a', accountId: '843f60e2-e013-5a0e-8b6c-face84bc5509', symbol: 'NDX-USD-PERP',
side: 'short', quantity: '4.2712', leverage: '50', openPrice: '29529.75', closePrice: '29556.50',
pnl: '-114.25', fee: '6.31', funding: '-0.12', collateral: '2522.54', takeProfits: [], stopLosses: [],
openDate: 1793065590123, closeDate: 1793066346456, createdAt: 1793065590123, updatedAt: 1793066346456, closeReason: 'user_requested',
});
assert.deepEqual(t, {
positionId: 'bdda9024-63a9-4abc-a5a8-f83df8ba030a', symbol: 'NDX-USD-PERP', side: 'short', quantity: 4.2712, leverage: 50,
openPrice: 29529.75, closePrice: 29556.5, pnl: -114.25, fee: 6.31, funding: -0.12,
openedAt: 1793065590123, closedAt: 1793066346456, closeReason: 'user_requested',
});
});
test('parseHistoryPosition: open, snake_case, seconds, and junk', () => {
const open = parseHistoryPosition({
position_id: 'p1', symbol: 'GC-PERP', side: 'LONG', quantity: '0', open_price: '4208.9', close_price: '',
pnl: '', open_date: 1793065590, close_date: null, close_reason: '',
});
assert.equal(open.side, 'long');
assert.equal(open.quantity, 0, 'a size Vest leaves out reads 0 — the store keeps the one it knows');
assert.equal(open.openPrice, 4208.9);
assert.equal(open.closePrice, null);
assert.equal(open.pnl, null);
assert.equal(open.openedAt, 1793065590000);
assert.equal(open.closedAt, null);
assert.equal(open.closeReason, null);
const held = parseHistoryPosition({
positionId: 'p3', symbol: 'NDX-USD-PERP', side: 'short', quantity: '85.7363', openPrice: '29538.96', closePrice: '290.25',
pnl: '0.0', fee: '63.314059', funding: '0.0', openDate: 1793065422000, closeDate: null,
});
assert.equal(held.closePrice, null, "an open position's placeholder close price is dropped");
assert.equal(held.pnl, null, 'and its $0 P&L');
assert.equal(held.fee, 63.314059, 'its entry fee is real');
assert.equal(parseHistoryPosition({ symbol: 'GC-PERP', openDate: 1 }), null, 'no id');
assert.equal(parseHistoryPosition({ positionId: 'p2', symbol: 'GC-PERP' }), null, 'no open time');
assert.equal(parseHistoryPosition(null), null);
});
test('an account\'s own token is told from the login\'s', () => {
const b64 = (o) => Buffer.from(JSON.stringify(o)).toString('base64url');
const acct = '275e765a-2d3d-5de3-8386-71aaa847c0e8';
const accountToken = `${b64({ alg: 'ES256' })}.${b64({ sub: '3cab1794-47bc-523b-91e6-64a9ad26dcbf', acc: { id: acct.toUpperCase() } })}.sig`;
assert.ok(jwtClaimStrings(accountToken).includes(acct));
assert.deepEqual(jwtClaimStrings('not.a-jwt.at-all'), []);
assert.deepEqual(jwtClaimStrings('nodots'), []);
// The login sees every account and its primary one.
assert.equal(accountScopedToken({ capitalIds: [acct, 'b', 'c'], hasPrimary: true, claims: [] }), false);
// One account and no primary: an account's token, whatever its claims.
assert.equal(accountScopedToken({ capitalIds: [acct], hasPrimary: false, claims: [] }), true);
// One account with a primary: only when the token names that account.
assert.equal(accountScopedToken({ capitalIds: [acct], hasPrimary: true, claims: jwtClaimStrings(accountToken) }), true);
assert.equal(accountScopedToken({ capitalIds: [acct], hasPrimary: true, claims: ['3cab1794-47bc-523b-91e6-64a9ad26dcbf'] }), false);
// A login with no capital accounts yet.
assert.equal(accountScopedToken({ capitalIds: [], hasPrimary: true, claims: [] }), false);
});
test('a refused account token falls back to the login\'s and stays refused', () => {
const now = iso('2026-10-27T01:00:00Z');
const wallet = '0x00000000000000000000000000000000000000aa';
const login = 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJsb2dpbiJ9.loginsignature-loginsignature-loginsig';
const account = 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJhY2N0In0.accountsignature-accountsignature-acct';
assert.equal(putVestSession({ accessToken: login, expiresAt: now + 600_000, primaryAddr: wallet, userId: 'u' }, now), null);
// The account's token lives longer, so it replaces the login's...
assert.equal(putVestSession({ accessToken: account, expiresAt: now + 900_000, primaryAddr: wallet, userId: 'u' }, now), null);
assert.equal(vestSessionFor(wallet, now).accessToken, account);
// ...until the client finds it sees one account: back to the login's.
refuseVestSession(wallet, account, now);
assert.equal(vestSessionFor(wallet, now).accessToken, login);
// Relayed again (the old extension posts it every minute): refused.
assert.match(putVestSession({ accessToken: account, expiresAt: now + 900_000, primaryAddr: wallet, userId: 'u' }, now), /one Vest account's token/);
assert.equal(vestSessionFor(wallet, now).accessToken, login);
// The login's next renewal is taken as usual.
const renewed = 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJsb2dpbiJ9.renewedsignature-renewedsignature-rene';
assert.equal(putVestSession({ accessToken: renewed, expiresAt: now + 1_200_000, primaryAddr: wallet, userId: 'u' }, now), null);
assert.equal(vestSessionFor(wallet, now).accessToken, renewed);
// Refusing a token that isn't held changes nothing held.
refuseVestSession(wallet, 'x.y.zzz', now);
assert.equal(vestSessionFor(wallet, now).accessToken, renewed);
// No good token to fall back to: none at all.
const wallet2 = '0x00000000000000000000000000000000000000bb';
assert.equal(putVestSession({ accessToken: account.replace('acct', 'accu'), expiresAt: now + 900_000, primaryAddr: wallet2, userId: 'u' }, now), null);
refuseVestSession(wallet2, account.replace('acct', 'accu'), now);
assert.equal(vestSessionFor(wallet2, now), null);
});