Live futures spreads off one client, and the master's max entry spread
One quote feed per machine: a TopstepX client's market hub, else a Tradovate client's market-data socket, subscribed to every listed root's front month and the drain micro. A supervisor keeps it on a working client, dropping one that fails or goes quiet while the market trades. The Auto draw weighs each instrument by its live spread (the typical tick when no fresh quote is at hand). No futures entry pays more than the master's max entry spread ($15 until set) to cross the book: the draw leaves such an instrument out, an explicit symbol sits the tick out, and fireLeg and the extra-day trade check it last before sending. The Settings page and the master's stats show the live spreads and the feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1782c16145
commit
db1d12327a
+18
-1
@@ -22,7 +22,7 @@ import {
|
||||
} from './trading-logic';
|
||||
import { getSetting, setSetting } from './db';
|
||||
import { POINT_VALUES, housekeepingRoot } from './instrument-specs';
|
||||
import { firmInstruments, drawFuturesInstrument, type LegShape } from './instruments';
|
||||
import { firmInstruments, drawFuturesInstrument, spreadBlock, type LegShape } from './instruments';
|
||||
import { errorMessage, isClientReady, isOrderNotPlaced, type PlatformClient, type PlatformContract, type PickContext } from './platform-client';
|
||||
|
||||
// ── process-wide state ───────────────────────────────────────────────────────
|
||||
@@ -922,6 +922,15 @@ async function runTradeLocked(
|
||||
const root = explicit ?? await drawFuturesInstrument(allowed.filter((r) => !tried.includes(r)), shape);
|
||||
if (!root) break;
|
||||
tried.push(root);
|
||||
// Paying more than the master's limit to cross the book:
|
||||
// not this tick (the draw left it out already; an explicit
|
||||
// symbol waits for the spread to come back in).
|
||||
const wide = spreadBlock(root);
|
||||
if (wide) {
|
||||
console.log(`[auto-trade] ${wide} — not trading it this tick`);
|
||||
if (explicit) break;
|
||||
continue;
|
||||
}
|
||||
const resolved: EligibleItem[] = [];
|
||||
for (const { firm, client } of [...new Map(pool.map((it) => [it.firm.id, it])).values()]) {
|
||||
try {
|
||||
@@ -991,6 +1000,8 @@ async function runTradeLocked(
|
||||
// immediately — P&L doesn't matter.
|
||||
if (item.isExtraDay) {
|
||||
const microRoot = housekeepingFor(item.firm);
|
||||
const wide = spreadBlock(microRoot);
|
||||
if (wide) throw new Error(`${wide} — no extra-day trade`);
|
||||
const mnqContract = await client.findFrontMonthContract(microRoot);
|
||||
if (!mnqContract) throw new Error(`${microRoot} contract not found for extra-day trade`);
|
||||
|
||||
@@ -1185,6 +1196,12 @@ async function fireLeg(
|
||||
const inst = await legInstrument(client, item.firm, item.drain, item.contract, item.pv);
|
||||
legContract = inst.contract;
|
||||
pv = inst.pointValue;
|
||||
// A futures entry never pays more than the master's limit to cross
|
||||
// the book — the last word for every path (batch, copy, manual, drain).
|
||||
if (!((legContract.stepNotional ?? 0) > 0)) {
|
||||
const wide = spreadBlock(item.drain ? housekeepingFor(item.firm) : symbolLabel);
|
||||
if (wide) throw new Error(`${wide} — not entering`);
|
||||
}
|
||||
plan = sizeLeg(item, legContract, legAmount, tradeDate);
|
||||
pct = plan.pct;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user