Use resolved active contracts for orders
This commit is contained in:
@@ -28,12 +28,12 @@ export async function POST(
|
|||||||
const client = clients.get(firmId);
|
const client = clients.get(firmId);
|
||||||
if (!client) return NextResponse.json({ error: 'Client not found for firm' }, { status: 404 });
|
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);
|
const contract = await client.findFrontMonthContract(symbol);
|
||||||
if (!contract) {
|
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)
|
// 2. Place the order — resolves with the fill event (includes commission/fees)
|
||||||
const fill = await client.sendOrder(accountId, contract.name, qty, action, orderType, price);
|
const fill = await client.sendOrder(accountId, contract.name, qty, action, orderType, price);
|
||||||
|
|||||||
+10
-4
@@ -4,6 +4,7 @@ import axios from 'axios';
|
|||||||
import type { AccountItem, AuthLoginResponse, Contract } from './tradovate-helpers';
|
import type { AccountItem, AuthLoginResponse, Contract } from './tradovate-helpers';
|
||||||
import { computeSec, randomUUIDV4 } from './tradovate-helpers';
|
import { computeSec, randomUUIDV4 } from './tradovate-helpers';
|
||||||
import { POINT_VALUES } from './trading-logic';
|
import { POINT_VALUES } from './trading-logic';
|
||||||
|
import { getCachedContract, resolveContracts } from './contract-resolver';
|
||||||
|
|
||||||
export class TradovateClient {
|
export class TradovateClient {
|
||||||
private name: string;
|
private name: string;
|
||||||
@@ -543,18 +544,23 @@ export class TradovateClient {
|
|||||||
async findFrontMonthContract(productName: string): Promise<{ id: number; name: string; tickSize: number } | null> {
|
async findFrontMonthContract(productName: string): Promise<{ id: number; name: string; tickSize: number } | null> {
|
||||||
if (!this.accessInfo?.accessToken) return null;
|
if (!this.accessInfo?.accessToken) return null;
|
||||||
|
|
||||||
// Check the volume-based resolver cache first
|
// Prefer the volume-based resolver so the trading path matches the settings page.
|
||||||
const { getCachedContract } = require('./contract-resolver') as typeof import('./contract-resolver');
|
|
||||||
const cached = getCachedContract(productName);
|
const cached = getCachedContract(productName);
|
||||||
if (cached) return { id: cached.id, name: cached.name, tickSize: cached.tickSize };
|
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(
|
const res = await axios.get(
|
||||||
`https://demo.tradovateapi.com/v1/contract/suggest?t=${encodeURIComponent(productName)}&l=20`,
|
`https://demo.tradovateapi.com/v1/contract/suggest?t=${encodeURIComponent(productName)}&l=20`,
|
||||||
{ headers: { Authorization: `Bearer ${this.accessInfo.accessToken}` } }
|
{ headers: { Authorization: `Bearer ${this.accessInfo.accessToken}` } }
|
||||||
);
|
);
|
||||||
const contracts: Array<{ id: number; name: string; status: string; providerTickSize: number }> = res.data ?? [];
|
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));
|
const match = contracts.find((c) => c.name.startsWith(productName));
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
return { id: match.id, name: match.name, tickSize: match.providerTickSize ?? 0.25 };
|
return { id: match.id, name: match.name, tickSize: match.providerTickSize ?? 0.25 };
|
||||||
|
|||||||
Reference in New Issue
Block a user