Instruments: each firm's list from the master, and a session-aware futures draw
- lib/instrument-specs.ts (pure, tested): the CME specs — point value, tick size, one-tick spread, Yahoo ticker — replacing POINT_VALUES; the draw's maths; the 12-hour hold cap; the micro for drain and extra-day legs; reading a firm's list (the master's, or the old rules against an older master: this machine's enabled set less bans, the symbol map). - lib/instruments.ts: the one module the trading paths ask. Measures every future's daily move from Yahoo's hourly candles once per trade date, and shows entry cost (a tick of spread plus Tradovate's fee parameters or TopstepX's last measured fill) without ever choosing by it. - Futures Auto draws one root per batch from the batch firm's list: movement per dollar of spread early in the session with a 10% floor each, sliding to the fastest movers by the last entry; nothing expected to hold 12h or more; micros only if nothing else is listed. Every draw logs its odds. - The batch is chosen before its instrument, and a group that can't trade anything yields to the next. Copy trade matches the held contract against the listed roots and won't copy an instrument it can't identify. Manual legs, manual orders and drain/extra-day legs follow the lists. - DXtrade firms rank their own list whatever root was drawn. - Capabilities, the dashboard's trade bar and contract resolves follow the lists; the trade bar and scheduler default to Auto. The master link reconnects when master_dashboard_url changes, and the catalog drops ?since after a switch so two masters' versions can't collide. - Reports carry each futures firm's instrumentStats for the master's Firms page; GET /api/instruments shows them (?simulate=N runs the draw, no orders); the Settings page shows the lists read-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
302e9b4911
commit
02544fbdf5
+16
-15
@@ -14,9 +14,9 @@
|
||||
* Differences from the futures platforms that shape this file:
|
||||
* - Account codes are strings ("clearing:account"); ids are a stable hash of
|
||||
* them (dxAccountId) so every numeric account table keeps working.
|
||||
* - There are no CME products. The firm's symbol map (master Firms page) says
|
||||
* which DXtrade symbols a CME root may trade as, and findFrontMonthContract
|
||||
* picks the cheapest per unit of volatility right now (scoreCandidate).
|
||||
* - There are no CME products. The firm's instrument list (master Firms page)
|
||||
* says which DXtrade symbols it trades, whatever root a batch drew, and
|
||||
* findFrontMonthContract picks among them (rankForTarget / scoreCandidate).
|
||||
* One engine "contract" is one quantity step of the symbol, and the step
|
||||
* rides in the contract name, so the engine's target/stop math is unchanged.
|
||||
* - Quantities are units, and positions are closed by positionCode
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
} from './db';
|
||||
import { getAccountConfig } from './account-type-resolver';
|
||||
import { getCachedFirm } from './firm-catalog';
|
||||
import { firmInstruments } from './instruments';
|
||||
import type { SymbolMapCandidate } from './master-protocol';
|
||||
import type { PlatformClient, PlatformAccount, PlatformContract, ClientHealth, PickContext } from './platform-client';
|
||||
import { OrderNotPlacedError, isOrderNotPlaced, errorMessage } from './platform-client';
|
||||
@@ -1162,24 +1163,26 @@ export class DXTradeClient implements PlatformClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* The contract a CME root trades as on this firm right now, from the
|
||||
* symbol map. With a pick context (the batch's leg), each candidate is
|
||||
* The contract this firm trades right now, from its instrument list —
|
||||
* the root is ignored. With a pick context (the batch's leg), each candidate is
|
||||
* sized as the leg would be — at its own leverage — and the cheapest one
|
||||
* that reaches the target hold wins (else the quickest); without one, the
|
||||
* cheapest per unit of volatility (see scoreCandidate). Sticky while an
|
||||
* account holds one of the candidates; a batch's pick is reused briefly
|
||||
* so its other lookups (drain legs) agree.
|
||||
*/
|
||||
public async findFrontMonthContract(root: string, ctx?: PickContext): Promise<PlatformContract | null> {
|
||||
public async findFrontMonthContract(_root: string, ctx?: PickContext): Promise<PlatformContract | null> {
|
||||
if (!this.token) return null;
|
||||
// The firm's own list: whatever root the batch drew, a DXtrade firm
|
||||
// chooses among its instruments. One cache key for every root.
|
||||
const root = '*';
|
||||
const firm = getCachedFirm(this.cfg.firmId);
|
||||
const map = firm?.symbolMap ?? {};
|
||||
const candidates = map[root.toUpperCase()] ?? map['*'] ?? [];
|
||||
const candidates: SymbolMapCandidate[] = firm ? firmInstruments(firm).map((symbol) => ({ symbol })) : [];
|
||||
const now = Date.now();
|
||||
if (candidates.length === 0) {
|
||||
if (now - (this.unmappedLoggedAt.get(root) ?? 0) > 10 * 60_000) {
|
||||
this.unmappedLoggedAt.set(root, now);
|
||||
console.log(`${this.tag} no symbol mapped for ${root} (nor '*') — skipping; set the symbol map on the master Firms page`);
|
||||
console.log(`${this.tag} no instruments listed — skipping; add them on the master Firms page`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1194,14 +1197,12 @@ export class DXTradeClient implements PlatformClient {
|
||||
return pick;
|
||||
}
|
||||
|
||||
/** Every mapped symbol, looked at afresh whatever the accounts hold: the
|
||||
/** Every listed symbol, looked at afresh whatever the accounts hold: the
|
||||
* day's volatility, and the candidates the dashboard ranks. */
|
||||
private async surveyCandidates(): Promise<void> {
|
||||
const all = new Map<string, SymbolMapCandidate>();
|
||||
for (const list of Object.values(getCachedFirm(this.cfg.firmId)?.symbolMap ?? {})) {
|
||||
for (const c of list) if (!all.has(c.symbol)) all.set(c.symbol, c);
|
||||
}
|
||||
if (all.size > 0) await this.pickCandidate('survey', [...all.values()], false);
|
||||
const firm = getCachedFirm(this.cfg.firmId);
|
||||
const list = firm ? firmInstruments(firm) : [];
|
||||
if (list.length > 0) await this.pickCandidate('survey', list.map((symbol) => ({ symbol })), false);
|
||||
}
|
||||
|
||||
private async pickCandidate(root: string, candidates: SymbolMapCandidate[], sticky: boolean, ctx?: PickContext): Promise<PlatformContract | null> {
|
||||
|
||||
Reference in New Issue
Block a user