diff --git a/app/api/firms/[id]/orders/route.ts b/app/api/firms/[id]/orders/route.ts index 379be56..29203f7 100644 --- a/app/api/firms/[id]/orders/route.ts +++ b/app/api/firms/[id]/orders/route.ts @@ -28,12 +28,12 @@ export async function POST( const client = clients.get(firmId); if (!client) return NextResponse.json({ error: 'Client not found for firm' }, { status: 404 }); - // 1. Find the front-month contract + // 1. Find the active contract chosen by the shared resolver const contract = await client.findFrontMonthContract(symbol); if (!contract) { - return NextResponse.json({ error: `Could not find active front-month contract for ${symbol}` }, { status: 404 }); + return NextResponse.json({ error: `Could not find an active contract for ${symbol}` }, { status: 404 }); } - console.log(`[order] front-month: ${contract.name} (id=${contract.id})`); + console.log(`[order] active contract: ${contract.name} (id=${contract.id})`); // 2. Place the order — resolves with the fill event (includes commission/fees) const fill = await client.sendOrder(accountId, contract.name, qty, action, orderType, price); diff --git a/lib/tradovate-class.ts b/lib/tradovate-class.ts index f43cb27..b44a801 100644 --- a/lib/tradovate-class.ts +++ b/lib/tradovate-class.ts @@ -4,6 +4,7 @@ import axios from 'axios'; import type { AccountItem, AuthLoginResponse, Contract } from './tradovate-helpers'; import { computeSec, randomUUIDV4 } from './tradovate-helpers'; import { POINT_VALUES } from './trading-logic'; +import { getCachedContract, resolveContracts } from './contract-resolver'; export class TradovateClient { private name: string; @@ -543,18 +544,23 @@ export class TradovateClient { async findFrontMonthContract(productName: string): Promise<{ id: number; name: string; tickSize: number } | null> { if (!this.accessInfo?.accessToken) return null; - // Check the volume-based resolver cache first - const { getCachedContract } = require('./contract-resolver') as typeof import('./contract-resolver'); + // Prefer the volume-based resolver so the trading path matches the settings page. const cached = getCachedContract(productName); if (cached) return { id: cached.id, name: cached.name, tickSize: cached.tickSize }; - // Fallback: use suggest API directly (first call before resolver has run) + // If the cache is cold, resolve on demand with the same logic the settings page uses. + const resolved = await resolveContracts([productName], this.accessInfo.accessToken); + const contract = resolved[productName]; + if (contract) { + return { id: contract.id, name: contract.name, tickSize: contract.tickSize }; + } + + // Last resort: fall back to Tradovate's ordered suggestions. const res = await axios.get( `https://demo.tradovateapi.com/v1/contract/suggest?t=${encodeURIComponent(productName)}&l=20`, { headers: { Authorization: `Bearer ${this.accessInfo.accessToken}` } } ); const contracts: Array<{ id: number; name: string; status: string; providerTickSize: number }> = res.data ?? []; - // Front-month = first contract whose name starts with the product symbol (results are ordered front→back) const match = contracts.find((c) => c.name.startsWith(productName)); if (!match) return null; return { id: match.id, name: match.name, tickSize: match.providerTickSize ?? 0.25 };