Smoke test: closing legs without quantity, the stop-first fallback, and a 22 s hold before closing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-26 17:42:02 -05:00
co-authored by Claude Opus 5.5
parent 6876223fa4
commit 59af95f0b1
+12 -2
View File
@@ -193,17 +193,27 @@ if (CONFIRM && ORDER_ACCOUNT) {
const px = pos.openPrice; const px = pos.openPrice;
const tick = inst.priceIncrement || 0.01; const tick = inst.priceIncrement || 0.01;
const far = (p) => Math.round(p / tick) * tick; const far = (p) => Math.round(p / tick) * tick;
// Exactly what lib/dxtrade-class.ts sends: a closing STOP/LIMIT names
// the position, never a quantity (code 33 otherwise).
const leg = (c, type, price) => ({ const leg = (c, type, price) => ({
account: ORDER_ACCOUNT, orderCode: c, type, instrument: symbol, quantity: pos.quantity, positionEffect: 'CLOSE', account: ORDER_ACCOUNT, orderCode: c, type, instrument: symbol, positionEffect: 'CLOSE',
positionCode: pos.positionCode, side: 'SELL', tif: 'GTC', ...(type === 'LIMIT' ? { limitPrice: price } : { stopPrice: price }), positionCode: pos.positionCode, side: 'SELL', tif: 'GTC', ...(type === 'LIMIT' ? { limitPrice: price } : { stopPrice: price }),
}); });
const group = await call('POST', `/accounts/${acct}/orders`, { const group = await call('POST', `/accounts/${acct}/orders`, {
body: { orders: [leg(`${code}-tp`, 'LIMIT', far(px * 1.05)), leg(`${code}-sl`, 'STOP', far(px * 0.95))], contingencyType: 'OCO' }, body: { orders: [leg(`${code}-tp`, 'LIMIT', far(px * 1.05)), leg(`${code}-sl`, 'STOP', far(px * 0.95))], contingencyType: 'OCO' },
}); });
show(`native OCO group of CLOSE legs (HTTP ${group.status})`, group.data); show(`native OCO group of CLOSE legs (HTTP ${group.status})`, group.data);
show('open orders now', (await call('GET', `/accounts/${acct}/orders`)).data); if (group.status >= 400) {
// The bot's fallback: the stop on its own first, then the take-profit.
show('fallback: lone STOP CLOSE', (await call('POST', `/accounts/${acct}/orders`, { body: leg(`${code}-sl1`, 'STOP', far(px * 0.95)) })));
show('fallback: lone LIMIT CLOSE', (await call('POST', `/accounts/${acct}/orders`, { body: leg(`${code}-tp1`, 'LIMIT', far(px * 1.05)) })));
}
show('open orders now (the exit pair should be here)', (await call('GET', `/accounts/${acct}/orders`)).data);
const del = await call('DELETE', `/accounts/${acct}/orders/${encodeURIComponent(`${code}-tp`)}`); const del = await call('DELETE', `/accounts/${acct}/orders/${encodeURIComponent(`${code}-tp`)}`);
show(`cancel without If-Match (HTTP ${del.status}; 403/412/428 means it wants one)`, del.data); show(`cancel without If-Match (HTTP ${del.status}; 403/412/428 means it wants one)`, del.data);
// Tradeify: every trade must be held at least 20 seconds.
console.log('holding 22 s before closing (minimum hold)…');
await sleep(22_000);
const flatten = await call('POST', `/accounts/${acct}/close`, { body: { account: ORDER_ACCOUNT, closePositions: true, cancelOrders: true } }); const flatten = await call('POST', `/accounts/${acct}/close`, { body: { account: ORDER_ACCOUNT, closePositions: true, cancelOrders: true } });
show(`flatten via /close (HTTP ${flatten.status})`, flatten.data); show(`flatten via /close (HTTP ${flatten.status})`, flatten.data);
} }