diff --git a/README.md b/README.md index bbe789f..6313542 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,7 @@ the batch firm's list, at random but weighted: the type's max contracts); an instrument whose expected hold for the batch's leg is 12 h or more is out of the draw (e.g. 6E at a $1,000 leg, about a week). -- Micros stay out of the draw; drain and extra-day legs trade the micro of a - listed product (MNQ when NQ is listed). +- Micros stay out of the draw; drain and extra-day legs always trade MNQ. Every draw logs its odds. `GET /api/instruments` shows each firm's list with spread, daily move, hold and chance (`?simulate=10000` runs the real draw diff --git a/lib/auto-trade.ts b/lib/auto-trade.ts index 52d11db..6c90d99 100644 --- a/lib/auto-trade.ts +++ b/lib/auto-trade.ts @@ -23,7 +23,7 @@ import { } from './trading-logic'; import { getSetting, setSetting } from './db'; import type { LaneTick, TickReason } from './master-protocol'; -import { POINT_VALUES, housekeepingRoot, MAX_HOLD_HOURS } from './instrument-specs'; +import { POINT_VALUES, housekeepingRoot, HOUSEKEEPING_ROOT, MAX_HOLD_HOURS } 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'; @@ -434,7 +434,7 @@ function releaseTradeMutex(lease: number, lane: string): void { * housekeepingRoot); a DXtrade firm its current pick. */ function housekeepingFor(firm: { platform: string; instruments?: string[]; bannedSymbols?: string[]; symbolMap?: Record }): string { if (firm.platform === 'dxtrade') return '*'; - return housekeepingRoot(firmInstruments(firm)) ?? 'MNQ'; + return housekeepingRoot(firmInstruments(firm)) ?? HOUSEKEEPING_ROOT; } /** The instrument a leg actually trades: drain legs go on the firm's diff --git a/lib/instrument-specs.ts b/lib/instrument-specs.ts index 03c6d90..73b963e 100644 --- a/lib/instrument-specs.ts +++ b/lib/instrument-specs.ts @@ -227,21 +227,16 @@ export function pickByChance(chances: { symbol: string; chance: number }[], rand return chances.length > 0 ? chances[chances.length - 1].symbol : null; } +/** What every futures firm's drain and extra-day legs trade. */ +export const HOUSEKEEPING_ROOT = 'MNQ'; + /** - * What a firm's drain and extra-day legs trade — tiny, so a micro: MNQ when - * NQ or MNQ is listed (as always), else the micro of a listed full-size - * contract, else a listed micro, else the listed root with the smallest tick - * value. null for an empty list. + * What a firm's drain and extra-day legs trade: always MNQ — tiny targets + * belong on a micro, whatever the firm lists for its full-size legs. null + * for an empty list: that firm trades nothing. */ export function housekeepingRoot(listed: readonly string[]): string | null { - const roots = listed.filter((r) => CME_SPECS[r]); - if (roots.length === 0) return null; - if (roots.includes('NQ') || roots.includes('MNQ')) return 'MNQ'; - const twin = roots.find((r) => MICRO_OF[r]); - if (twin) return MICRO_OF[twin]; - const micro = roots.find((r) => CME_SPECS[r].micro); - if (micro) return micro; - return [...roots].sort((a, b) => tickValue(a) - tickValue(b))[0]; + return listed.some((r) => CME_SPECS[r]) ? HOUSEKEEPING_ROOT : null; } const DXTRADE_SYMBOL = /^[A-Za-z0-9./_:-]{1,32}$/; diff --git a/scripts/check-instruments.mjs b/scripts/check-instruments.mjs index d23c390..26e2a76 100644 --- a/scripts/check-instruments.mjs +++ b/scripts/check-instruments.mjs @@ -140,12 +140,13 @@ test('10k seeded draws land on the chances', () => { assert.equal(pickByChance([], 0.5), null); }); -test('drain and extra-day legs: a micro of what is listed', () => { +test('drain and extra-day legs: always MNQ', () => { + // Always MNQ, whatever the firm lists; nothing for a firm that lists nothing. assert.equal(housekeepingRoot(['NQ', 'GC', 'CL', '6E']), 'MNQ'); assert.equal(housekeepingRoot(['MNQ']), 'MNQ'); - assert.equal(housekeepingRoot(['GC', 'CL']), 'MGC'); - assert.equal(housekeepingRoot(['6E', 'ZN']), '6E'); // no micro: the smallest tick value - assert.equal(housekeepingRoot(['MCL', 'ZB']), 'MCL'); + assert.equal(housekeepingRoot(['GC', 'CL']), 'MNQ'); + assert.equal(housekeepingRoot(['6E', 'ZN']), 'MNQ'); + assert.equal(housekeepingRoot(['MCL', 'ZB']), 'MNQ'); assert.equal(housekeepingRoot([]), null); });