Instruments: no fee lookup — commissions barely differ between instruments
Entry cost is one tick of spread; fees can be added later if they matter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ee4550512c
commit
ff9b107452
+3
-36
@@ -10,8 +10,8 @@
|
||||
* near the last entry, nothing expected to hold 12 hours or more.
|
||||
*
|
||||
* Movement comes from Yahoo's hourly candles (7 days, measured once per trade
|
||||
* date); the spread is one tick. Fees are shown, never used to choose:
|
||||
* Tradovate's fee parameters, or TopstepX's last measured fill.
|
||||
* date); the spread is one tick. Commissions play no part — they barely
|
||||
* differ between instruments.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
@@ -76,8 +76,6 @@ const CONCURRENCY = 4;
|
||||
const g = globalThis as typeof globalThis & {
|
||||
__futuresMarket?: Map<string, Market>;
|
||||
__futuresMarketInflight?: Map<string, Promise<void>>;
|
||||
__futuresFees?: Map<string, { tradeDate: string; fees: Record<string, number>; at: number }>;
|
||||
__futuresMeasuredFees?: Map<string, number>;
|
||||
};
|
||||
const market = (g.__futuresMarket ??= new Map());
|
||||
const inflight = (g.__futuresMarketInflight ??= new Map());
|
||||
@@ -143,33 +141,6 @@ export function marketOf(root: string): { price: number | null; dailyMove: numbe
|
||||
return m ? { price: m.price, dailyMove: m.dailyMove, error: m.error } : null;
|
||||
}
|
||||
|
||||
// ── fees (shown, never used to choose) ─────────────────────────────────────
|
||||
|
||||
const feeCache = (g.__futuresFees ??= new Map());
|
||||
const measuredFees = (g.__futuresMeasuredFees ??= new Map());
|
||||
|
||||
/** A fill's commission per contract per side, for platforms with no fee
|
||||
* lookup (TopstepX). */
|
||||
export function recordMeasuredFee(firmId: string, root: string, perContract: number): void {
|
||||
if (Number.isFinite(perContract) && perContract >= 0) measuredFees.set(`${firmId}|${root}`, Math.round(perContract * 100) / 100);
|
||||
}
|
||||
|
||||
/** Tradovate's fee parameters, fetched once per trade date per firm. */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function refreshFees(firm: { id: string; platform: string }, client: any): void {
|
||||
if (firm.platform !== 'tradovate' || typeof client?.fetchInstrumentFees !== 'function') return;
|
||||
const cached = feeCache.get(firm.id);
|
||||
if (cached && cached.tradeDate === currentTradeDateCT() && (Object.keys(cached.fees).length > 0 || Date.now() - cached.at < RETRY_MS)) return;
|
||||
feeCache.set(firm.id, { tradeDate: currentTradeDateCT(), fees: cached?.fees ?? {}, at: Date.now() });
|
||||
void Promise.resolve(client.fetchInstrumentFees([...CME_ROOTS]))
|
||||
.then((fees: Record<string, number>) => feeCache.set(firm.id, { tradeDate: currentTradeDateCT(), fees: fees ?? {}, at: Date.now() }))
|
||||
.catch((err: unknown) => console.warn(`[instruments] fee lookup failed: ${errorMessage(err)}`));
|
||||
}
|
||||
|
||||
export function feesPerSide(firmId: string, root: string): number | null {
|
||||
return feeCache.get(firmId)?.fees[root] ?? measuredFees.get(`${firmId}|${root}`) ?? null;
|
||||
}
|
||||
|
||||
// ── the draw ────────────────────────────────────────────────────────────────
|
||||
|
||||
/** The batch's leg, for the hold estimate: its dollars, its stop (Infinity =
|
||||
@@ -240,17 +211,14 @@ export function simulateDraws(roots: readonly string[], leg: LegShape | null, n:
|
||||
|
||||
/**
|
||||
* A futures firm's instruments as the draw sees them right now: every CME
|
||||
* root with its entry cost, movement and contract, and — sized for `lead`'s
|
||||
* root with its spread, movement and contract, and — sized for `lead`'s
|
||||
* next leg — the listed ones' holds, the ones over the cap, and each one's
|
||||
* chance.
|
||||
*/
|
||||
export function instrumentStatsFor(
|
||||
firm: FirmLike & { id: string },
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
client: any,
|
||||
lead: (LegShape & { account: string }) | null,
|
||||
): InstrumentStats {
|
||||
refreshFees({ id: firm.id, platform: firm.platform }, client);
|
||||
const listed = firmInstruments(firm);
|
||||
const board = futuresBoard(listed, lead);
|
||||
const disabled = new Set(board.disabled);
|
||||
@@ -269,7 +237,6 @@ export function instrumentStatsFor(
|
||||
listed: listed.includes(root),
|
||||
contract: getCachedContract(root)?.name ?? null,
|
||||
spreadDollars: spreadDollars(root),
|
||||
feesPerSide: feesPerSide(firm.id, root),
|
||||
dailyMoveDollars: dailyMove === null ? null : Math.round(dailyMove),
|
||||
holdHours: holdHours === null ? null : Math.round(holdHours * 10) / 10,
|
||||
disabled: listed.includes(root) ? disabled.has(root) : holdHours !== null && holdHours >= MAX_HOLD_HOURS,
|
||||
|
||||
Reference in New Issue
Block a user