Honor the catalog's min-legs setting and carve $500 pieces

Big days (> the $1,700 split threshold) must carve at least the account
type's minLegs from the catalog — pickLegAmount reserves the minimum
piece for each leg still owed, forcing a carve even once the residual
dips below the threshold, clamped to what the pieces allow. Days at or
below the threshold still trade as one leg. The smallest split piece
drops from $800 to $500. An older master without the field means no
minimum.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-11 15:32:40 -05:00
co-authored by Claude Fable 5
parent 620ab54d42
commit ae0f93a44f
6 changed files with 43 additions and 8 deletions
+16 -2
View File
@@ -53,7 +53,7 @@ export function resolveEffectiveProfitTarget(
/** Split a day's target into multiple legs when it exceeds this. */
export const BIG_DAY_THRESHOLD = 1700;
/** Every split leg must be at least this. */
export const MIN_LEG = 800;
export const MIN_LEG = 500;
/** After at least one trade, a residual at or below this counts as "day done". */
export const LEG_COMPLETION_EPSILON = 25;
/** Safety valve: never fire more than this many legs per account per trade date. */
@@ -112,8 +112,22 @@ export function groupLegFraction(prefix: string, tradeDate: string, legIndex: nu
* Pick the next leg to trade toward today's remaining target.
* remaining ≤ BIG_DAY_THRESHOLD → trade it all in one final leg; otherwise carve a random
* $5-rounded piece from [MIN_LEG, remaining − MIN_LEG] so the remainder also stays ≥ MIN_LEG.
*
* minLegsLeft (an account's min-legs setting minus the legs already fired
* today) forces a carve even below the big-day threshold and reserves MIN_LEG
* for each leg still owed after this one. It is clamped to what MIN_LEG-sized
* pieces make possible, so a small coast day is never forced into impossible
* splits.
*/
export function pickLegAmount(remainingToday: number, rand: () => number = Math.random): number {
export function pickLegAmount(remainingToday: number, rand: () => number = Math.random, minLegsLeft = 0): number {
const owedAfter = Math.min(minLegsLeft, Math.floor(remainingToday / MIN_LEG)) - 1;
if (owedAfter > 0) {
const lo = MIN_LEG;
const hi = remainingToday - owedAfter * MIN_LEG;
if (hi <= lo) return lo;
const leg = Math.round((lo + rand() * (hi - lo)) / 5) * 5;
return Math.min(Math.max(leg, lo), hi);
}
if (remainingToday <= BIG_DAY_THRESHOLD) return round2(remainingToday);
const lo = MIN_LEG;
const hi = remainingToday - MIN_LEG;