Vest: the bot activates a passed evaluation's funded account, and a passed one no longer holds the sync
A passed Vest evaluation sits at GOAL_PASSED (status 3) until someone presses
"Go to funded account", which sends POST /v3/capital/activate-funded
{eval_account_id} on the evaluation's own token (read from the Vest app's
bundle, 2026-10-29); Vest then opens the funded account and marks the
evaluation CLAIMED (6). The 5 evaluations that passed on 2026-10-29 waited
for a hand activation.
runActivations now asks for it, the way runClaims claims: from the
scheduler's tick (not awaited, so a slow ask doesn't hold the lane's entry)
and from the claim watcher while the scheduler is off, for evaluations with
an account type only, 15 minutes apart after a failure. Off with the
auto_activate setting at 'off' (dashboard: "Auto-activate funded"). The
client asks for an evaluation once at a time, never again after Vest
answered, and after a timeout not until a list read a minute on says either
way; a refusal frees it at once. The rule is the Vest app's own: an
evaluation at GOAL_PASSED, not blocked, on its plan's last step.
Vest's active balance list leaves a passed evaluation out, and a missing
balance made every sync short of good (15:31-15:34Z), which holds and then
quarantines the lane. Only a live account's missing balance does now. Log
lines name Vest's statuses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0eb56b3949
commit
8a5b2a64e7
+37
-1
@@ -14,7 +14,8 @@ import {
|
||||
vestAccountName, parsePosition, awaitingLegs, parseOpenOrder, unrealizedPnl, parseTxn, bookTxns, txnKind, VEST_TXN,
|
||||
hourlyVolatility, marketOpen, MIN_HOURLY_RETURNS, nextVestResetMs, resetSafeStop, parseHistoryPosition, jwtClaimStrings,
|
||||
accountScopedToken, VestApiError, VestNotSentError, nothingWentIn, tokenForRequest, claimWithdrawal, settleClaims,
|
||||
claimFundRows, readClaims, payoutDaysOf, payoutsSince, longClosure, unknownOpenFill,
|
||||
claimFundRows, readClaims, payoutDaysOf, payoutsSince, longClosure, unknownOpenFill, awaitingActivation, vestStatusName,
|
||||
VEST_STATUS,
|
||||
} from '../lib/vest-helpers.ts';
|
||||
import { putVestSession, vestSessionFor, dropVestSession, listVestSessions, refuseVestSession } from '../lib/vest-session.ts';
|
||||
import { computeDayPlan, currentTradeDateCT, tradeDateFor, cycleCompletion } from '../lib/trading-logic.ts';
|
||||
@@ -120,6 +121,41 @@ test('capital accounts parse, and are named for their rules', () => {
|
||||
assert.equal(parseCapitalAccount({ nope: true }), null);
|
||||
});
|
||||
|
||||
test('a passed evaluation waits on its funded account, as the Vest app decides', () => {
|
||||
const passed = { ...evalAccount, status: VEST_STATUS.GOAL_PASSED };
|
||||
const a = parseCapitalAccount(passed);
|
||||
assert.equal(a.step, 1);
|
||||
assert.equal(a.stepsTotal, 1);
|
||||
assert.equal(a.accountType, 2);
|
||||
assert.equal(a.blocked, false);
|
||||
assert.equal(awaitingActivation(a), true);
|
||||
|
||||
// Still trading, already claimed, failed or expired: nothing to activate.
|
||||
for (const status of [1, 2, 4, 5, 6, 7]) assert.equal(awaitingActivation(parseCapitalAccount({ ...evalAccount, status })), false, `status ${status}`);
|
||||
// Blocked by Vest.
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, blocked_at: 1793287000000 })), false);
|
||||
// A funded account that reached its goal isn't an evaluation — on the
|
||||
// last step's number too, so only the evaluation test can say so.
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, account_type: 3, stage: 2, step: 1 })), false);
|
||||
// Either of Vest's marks makes it one.
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, account_type: 3 })), true);
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, stage: 2, step: 1 })), true);
|
||||
// A 2-Step's first step passes on to its second by itself; the last one waits.
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, step: 1, steps_total: 2 })), false);
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...passed, step: 2, steps_total: 2 })), true);
|
||||
|
||||
// Without step or steps_total, the Vest app's own fallbacks.
|
||||
const { step, steps_total, ...bare } = passed;
|
||||
assert.equal(awaitingActivation(parseCapitalAccount(bare)), true);
|
||||
assert.equal(parseCapitalAccount({ ...bare, stage: 2 }).step, 2);
|
||||
assert.equal(parseCapitalAccount({ ...bare, plan_product_type: 'instant_funded' }).stepsTotal, 0);
|
||||
assert.equal(awaitingActivation(parseCapitalAccount({ ...bare, plan_product_type: 'instant_funded' })), false);
|
||||
|
||||
assert.equal(vestStatusName(3), 'goal passed (status 3)');
|
||||
assert.equal(vestStatusName(6), 'claimed (status 6)');
|
||||
assert.equal(vestStatusName(9), 'unknown (status 9)');
|
||||
});
|
||||
|
||||
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',
|
||||
|
||||
Reference in New Issue
Block a user