Vest: claim the profit at the target, then trade again the same day
On Vest, a claim lowers the daily-loss floor by the amount claimed. Winning $1,500 and claiming it leaves $3,000 of room the same day, the full drawdown. The bot now uses that. - Payout claims: a scheduler step (runClaims, under the lane mutex, before the flat gate) claims every payoutable account that reached its stage's target, via a platform that takes payouts by API. The claim is all profit above the starting balance. It then clears the account's legs and entry outcome for the day so it can trade its next stage. Failures retry after 5 minutes. The auto_claim setting 'off' disables it. - VestClient.claimProfit: POST /v3/capital/withdraw to the login's main account (account_type 1 on /v3/accounts). The payout is booked at once: today's fund row and the cycle cut, with today's P&L counted from the post-claim balance. The history read later rebooks it from Vest's WITHDRAW row, and the books check counts a claim until that row shows. - Same-day stages: an account type can pick its stage by the payouts since the firm's last daily reset (stagesSameDay, sameDayClaims). A stage can carry its own daily loss limit. - That limit replaces the type's everywhere room, stops and last stands are worked out: the batch, the manual leg, copy trade, the dashboard and the report. - Room from the platform's own floor: dailyLossFloor (Vest) makes the room min(stage limit, balance − Vest's floor). A same-day claim that lowers the floor therefore frees room, and the room is never more than Vest allows. VestClient.dayStartBalance is gone. - Before the 8 PM ET reset: from 7:55 PM ET, each open Vest position on an account with a daily limit is fitted to the room the reset will leave (resetSafeStop). Its stop comes in via PUT/POST stop-loss, or the position closes when there is no room. - Checks for same-day stages, the stage limit, floorRoom, the post-claim plan, the next reset across both time changes, and resetSafeStop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
335af486d4
commit
32081f3dcf
+56
-5
@@ -16,6 +16,8 @@ export interface WithdrawalStage {
|
||||
* fields fall back to the account type's defaults. */
|
||||
withdrawalPct?: number;
|
||||
payoutCap?: number;
|
||||
/** This stage's hard daily loss limit in dollars; absent = the type's. */
|
||||
dailyLossLimit?: number;
|
||||
}
|
||||
|
||||
export interface EffectiveConfig {
|
||||
@@ -24,6 +26,17 @@ export interface EffectiveConfig {
|
||||
minTradingDays: number;
|
||||
minDayPnL: number;
|
||||
drain: boolean;
|
||||
/** The stage's hard daily loss limit (0 = none): its own, else the type's. */
|
||||
dailyLossLimit: number;
|
||||
}
|
||||
|
||||
/** What picks a stage beyond the fund rows: the type's daily loss limit (a
|
||||
* stage may override it), and for a type whose stages reset each day, how
|
||||
* many payouts the account has made since the firm's last daily reset. */
|
||||
export interface StageInputs {
|
||||
dailyLossLimit?: number;
|
||||
stagesSameDay?: boolean;
|
||||
sameDayWithdrawals?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,20 +60,28 @@ export function resolveEffectiveConfig(
|
||||
priorProfit: number,
|
||||
fundTransactions: { date: string; amount: number }[],
|
||||
minDayPnL = 0,
|
||||
inputs: StageInputs = {},
|
||||
): EffectiveConfig {
|
||||
const dailyLossLimit = Math.max(0, inputs.dailyLossLimit ?? 0);
|
||||
if (targetSameEquity) {
|
||||
// Simple Mode A: effective target = profitTarget - equityProfit, computed in computeDailyTarget.
|
||||
// Since computeDailyTarget already compares (amount - accountSize) against profitTarget,
|
||||
// no adjustment needed here — just return the base values.
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false };
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false, dailyLossLimit };
|
||||
}
|
||||
if (withdrawalStages.length > 0) {
|
||||
const withdrawalCount = fundTransactions.filter((f) => f.amount < 0).length;
|
||||
// Same-day stages (Vest): only today's payouts count, so every new
|
||||
// day starts back at stage 1 — the room a payout frees lasts only
|
||||
// until the firm's next daily reset.
|
||||
const withdrawalCount = inputs.stagesSameDay
|
||||
? Math.max(0, Math.floor(inputs.sameDayWithdrawals ?? 0))
|
||||
: fundTransactions.filter((f) => f.amount < 0).length;
|
||||
if (withdrawalCount === 0) {
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false }; // Stage 1 = base values
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false, dailyLossLimit }; // Stage 1 = base values
|
||||
}
|
||||
const idx = Math.min(withdrawalCount - 1, withdrawalStages.length - 1);
|
||||
const stage = withdrawalStages[idx];
|
||||
const stageLimit = typeof stage.dailyLossLimit === 'number' && stage.dailyLossLimit >= 0 ? stage.dailyLossLimit : dailyLossLimit;
|
||||
if (stage.drain === true) {
|
||||
return {
|
||||
profitTarget: 0,
|
||||
@@ -73,12 +94,16 @@ export function resolveEffectiveConfig(
|
||||
// qualifying bar sits at the grid floor — every draw counts.
|
||||
minDayPnL: DRAIN_DAY_MIN,
|
||||
drain: true,
|
||||
dailyLossLimit: stageLimit,
|
||||
};
|
||||
}
|
||||
// Fall back to base minTradingDays when the stage doesn't specify one (0 or missing)
|
||||
return { profitTarget: stage.profit, consistency: stage.consistency, minTradingDays: stage.minTradingDays || minTradingDays, minDayPnL, drain: false };
|
||||
return {
|
||||
profitTarget: stage.profit, consistency: stage.consistency, minTradingDays: stage.minTradingDays || minTradingDays,
|
||||
minDayPnL, drain: false, dailyLossLimit: stageLimit,
|
||||
};
|
||||
}
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false };
|
||||
return { profitTarget, consistency, minTradingDays, minDayPnL, drain: false, dailyLossLimit };
|
||||
}
|
||||
|
||||
/** @deprecated Use resolveEffectiveConfig instead */
|
||||
@@ -1325,6 +1350,32 @@ export const DLL_MIN_STOP = 50;
|
||||
* the next reset has its whole move counted against the NEW day — room
|
||||
* borrowed from today's profit would be a breach tomorrow.
|
||||
*/
|
||||
/** The stage inputs for one account: the type's daily loss limit, and for a
|
||||
* type whose stages reset each day, the client's count of today's payouts. */
|
||||
export function stageInputsFor(
|
||||
client: { sameDayClaims?: (accountId: number) => number },
|
||||
accountId: number,
|
||||
cfg: { dailyLossLimit?: number; stagesSameDay?: boolean },
|
||||
): StageInputs {
|
||||
const sameDay = cfg.stagesSameDay === true;
|
||||
return {
|
||||
dailyLossLimit: cfg.dailyLossLimit ?? 0,
|
||||
stagesSameDay: sameDay,
|
||||
sameDayWithdrawals: sameDay && typeof client.sameDayClaims === 'function' ? client.sameDayClaims(accountId) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* dllRoom where the platform states its daily-loss floor itself (Vest: the
|
||||
* equity the account may not fall below today, already moved by the day's
|
||||
* transfers): never more than the stage's limit, nor than the floor leaves.
|
||||
* Infinity when there is no limit.
|
||||
*/
|
||||
export function floorRoom(o: { dailyLossLimit: number; closedBalance: number; floor: number }): number {
|
||||
if (!(o.dailyLossLimit > 0)) return Infinity;
|
||||
return round2(Math.min(o.dailyLossLimit, o.closedBalance - o.floor));
|
||||
}
|
||||
|
||||
export function dllRoom(o: {
|
||||
dailyLossLimit: number;
|
||||
closedBalance: number;
|
||||
|
||||
Reference in New Issue
Block a user