Trade mix: name positions by a contract index that never expires

A Tradovate position opened before this build (so its entry wasn't recorded)
was named from the contract cache, whose entries expire after 30 minutes:
Majd's CL longs showed as #3273057. The resolver now also keeps a lasting
contract id → root index, which the mix reads after its recorded entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-27 20:04:42 -05:00
co-authored by Claude Opus 5.5
parent ed62788480
commit b125257d4e
2 changed files with 18 additions and 6 deletions
+6 -6
View File
@@ -5,14 +5,15 @@
* Open positions come off the client's position map, each contract named by
* the instrument it is: a DXtrade position carries its symbol; a futures one
* is looked up among the contracts this machine has entered (recorded at the
* fill, kept across restarts), then the resolver's cache and TopstepX's own.
* fill, kept across restarts), TopstepX's own, and every contract the
* resolver has resolved in this process (never expiring).
* Entries are counted as they fill and kept in a setting per trade date, so
* a restart doesn't lose the day.
*/
import { getSetting, setSetting } from './db';
import { currentTradeDateCT } from './trading-logic';
import { getAllCachedContracts } from './contract-resolver';
import { rootOfContract } from './contract-resolver';
import { errorMessage } from './platform-client';
import type { InstrumentCount, TradeMix } from './master-protocol';
@@ -71,16 +72,15 @@ export function tradeMixFor(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
client: any,
): TradeMix {
// Recorded at the fill, then TopstepX's own contracts, then every
// contract this process resolved (Tradovate's, however long ago).
const names = new Map<string, string>();
for (const [root, contract] of Object.entries(getAllCachedContracts())) {
if (contract) names.set(String(contract.id), root);
}
const own = typeof client.contractRoots === 'function' ? (client.contractRoots() as Map<string, string>) : null;
for (const [id, root] of own ?? []) names.set(id, root);
for (const [id, instrument] of Object.entries(readJson<Record<string, string>>(NAMES_KEY) ?? {})) names.set(id, instrument);
const instrumentOf = (contractId: unknown): string => (firm.platform === 'dxtrade'
? String(contractId).split('|')[0]
: names.get(String(contractId)) ?? `#${String(contractId)}`);
: names.get(String(contractId)) ?? rootOfContract(contractId as number | string) ?? `#${String(contractId)}`);
const open = new Map<string, InstrumentCount>();
for (const acc of (client.accountList ?? []) as { id: number }[]) {