Futures legs respect a firm's per-instrument contract limit

A firm can cap how many contracts of an instrument its accounts may hold
(the catalog's instrumentLimits, set on the master's Firms page). A leg
trades one contract per $1,000, capped by the account type's max contracts
and that limit. The Auto draw sizes each instrument's hold with the capped
count, so a limit that slows a leg past 12 hours takes it out of the draw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-28 13:45:44 -05:00
co-authored by Claude Opus 5.5
parent b125257d4e
commit 58e7414849
6 changed files with 71 additions and 13 deletions
+11 -4
View File
@@ -23,7 +23,7 @@ import {
} from './trading-logic';
import { getSetting, setSetting } from './db';
import type { LaneTick, TickReason } from './master-protocol';
import { POINT_VALUES, housekeepingRoot, HOUSEKEEPING_ROOT, MAX_HOLD_HOURS } from './instrument-specs';
import { POINT_VALUES, housekeepingRoot, HOUSEKEEPING_ROOT, MAX_HOLD_HOURS, futuresContracts } from './instrument-specs';
import { firmInstruments, drawFuturesInstrument, spreadBlock, type LegShape } from './instruments';
import { recordEntry } from './trade-mix';
import { errorMessage, isClientReady, isOrderNotPlaced, type PlatformClient, type PlatformContract, type PickContext } from './platform-client';
@@ -486,7 +486,12 @@ function legShapeFor(item: SizingItem, tradeDate: string): LegShape {
const hard = Number.isFinite(item.dllRoom) && dll > 0
? dllStopDollars({ room: item.dllRoom, dailyLossLimit: dll, entryFee: FALLBACK_COMMISSION_PER_CONTRACT, commissionPct: 0 })
: Infinity;
return { leg, stopDollars: Math.min(Number.isFinite(obf) && obf > 0 ? obf : Infinity, hard), maxPositionSize: cfg.maxPositionSize };
return {
leg,
stopDollars: Math.min(Number.isFinite(obf) && obf > 0 ? obf : Infinity, hard),
maxPositionSize: cfg.maxPositionSize,
limits: item.firm.instrumentLimits,
};
}
/** What the fire path needs of a contract — PlatformContract minus the id. */
@@ -1310,8 +1315,10 @@ async function fireLeg(
}
}
} else {
const rawContracts = Math.max(1, Math.ceil(legAmount / 1000));
contracts = cfg.maxPositionSize > 0 ? Math.min(rawContracts, cfg.maxPositionSize) : rawContracts;
// One per $1,000, capped by the account type's max contracts and
// the firm's limit on this instrument.
const legRoot = item.drain ? housekeepingFor(item.firm) : symbolLabel;
contracts = futuresContracts(legAmount, cfg.maxPositionSize, item.firm.instrumentLimits?.[legRoot] ?? 0);
}
} catch (err) {
unrecordLeg(acc.id, tradeDate, legAmount);