Place OCO stop brackets for the catalog's obfuscation options

When an account type opts in, legs are placed as an OCO pair via
order/placeoco - the usual limit plus a stop: the first cycle day's stop
keeps the day's net floor at -$1,000, min-day trades stop at 2x their
target, and the day after any red day rides stopless to make it back. A
stop-out books the day: eligibility refuses the account once realized
P&L falls short of the legs fired (or is simply negative on an opted-in
account, which survives restarts). If placeoco is rejected the leg falls
back to the classic lone limit with a loud log rather than leave a
position without an exit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-11 18:28:21 -05:00
co-authored by Claude Fable 5
parent ae0f93a44f
commit c58e981b1e
7 changed files with 161 additions and 12 deletions
+46
View File
@@ -951,6 +951,52 @@ export class TradovateClient {
});
}
/** Place an OCO exit pair after an entry fill: a limit take-profit linked
* to a stop, via order/placeoco — Tradovate cancels the sibling when one
* fills. Resolves on command ack (does not wait for a fill), like
* placeOrderNoWait. */
async placeOcoExit(
accountId: number,
contractSymbol: string,
quantity: number,
exitAction: 'Buy' | 'Sell',
limitPrice: number,
stopPrice: number,
): Promise<{ orderId?: number }> {
if (!this.ws) throw new Error('WebSocket not connected');
const msgId = this.getMsgId();
return new Promise((resolve, reject) => {
this.directEventCallbacks[msgId] = (response: any) => {
if (typeof response === 'string') {
reject(new Error(response));
return;
}
resolve({ orderId: response?.orderId ?? response?.id });
};
this.ws.send(
`order/placeoco\n${msgId}\n\n${JSON.stringify({
accountId,
action: exitAction,
symbol: contractSymbol,
orderQty: quantity,
orderType: 'Limit',
price: limitPrice,
timeInForce: 'Day',
text: 'DOM',
other: {
action: exitAction,
orderType: 'Stop',
stopPrice,
timeInForce: 'Day',
text: 'DOM',
},
})}`
);
});
}
/** Close the WebSocket connection and stop all intervals. Call before discarding the instance. */
public disconnect(): void {
this.intentionalDisconnect = true;