Vest: an entry never closes a hand trade, and a flatten never opens one
sendOrder takes an optional intent. On Vest an 'open' is refused when the account already holds the symbol, where it used to close that position as the "entry"; the leg's exits then found nothing and its flatten opened a fresh position with no TP or stop. A 'close' only closes or reduces, and on a flat account is refused with NothingToCloseError, which flattenAndFail reports as nothing to flatten instead of FLATTEN FAILED. Entries, the extra-day round trip and flattens pass their intent; the manual order route and the other platforms keep today's behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
beddbdcf6c
commit
e90f21ed91
+13
-9
@@ -46,14 +46,14 @@ import {
|
||||
} from './db';
|
||||
import { getCachedFirm } from './firm-catalog';
|
||||
import { firmInstruments } from './instruments';
|
||||
import type { PlatformClient, PlatformAccount, PlatformContract, ClientHealth, PickContext } from './platform-client';
|
||||
import { OrderNotPlacedError, isOrderNotPlaced, errorMessage } from './platform-client';
|
||||
import type { PlatformClient, PlatformAccount, PlatformContract, ClientHealth, PickContext, OrderIntent } from './platform-client';
|
||||
import { OrderNotPlacedError, NothingToCloseError, isOrderNotPlaced, errorMessage } from './platform-client';
|
||||
import { encodeContractName, decodeContractName, roundToStep, scoreCandidate } from './dxtrade-helpers';
|
||||
import { vestSessionFor, dropVestSession, listVestSessions, refuseVestSession } from './vest-session';
|
||||
import {
|
||||
vestAccountId, vestAccountName, vestDayOf, lastVestResetMs, nextVestResetMs, parseCapitalAccount, parsePosition, parseOpenOrder,
|
||||
parseTxn, bookTxns, awaitingLegs, unrealizedPnl, hourlyVolatility, marketOpen, num, toMs, formatDecimal, resetSafeStop,
|
||||
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_TXN, VEST_STOP_SLIPPAGE_FRACTION,
|
||||
parseHistoryPosition, jwtClaimStrings, accountScopedToken, vestOrderEffect, VEST_STATUS_ACTIVE, VEST_TXN, VEST_STOP_SLIPPAGE_FRACTION,
|
||||
} from './vest-helpers';
|
||||
import type { VestCapitalAccount, VestPosition, VestOpenOrder, VestTxn, VestTrade } from './vest-helpers';
|
||||
|
||||
@@ -1605,10 +1605,11 @@ export class VestClient implements PlatformClient {
|
||||
|
||||
/**
|
||||
* Place an order and wait for its fill: an open when the account is flat,
|
||||
* a close (or reduce) of the position when it holds the opposite side.
|
||||
* Adding to a position is refused, and so is an open against another
|
||||
* account's opposite position in the same symbol (no hedging). Resolves
|
||||
* with the fill `price`.
|
||||
* a close (or reduce) of the position when it holds the opposite side —
|
||||
* only the one `intent` names, when given (vestOrderEffect). Adding to a
|
||||
* position is refused, and so is an open against another account's
|
||||
* opposite position in the same symbol (no hedging). Resolves with the
|
||||
* fill `price`.
|
||||
*/
|
||||
public async sendOrder(
|
||||
accountId: number,
|
||||
@@ -1617,9 +1618,10 @@ export class VestClient implements PlatformClient {
|
||||
action: 'Buy' | 'Sell',
|
||||
orderType: 'Market' | 'Limit',
|
||||
price?: number,
|
||||
intent?: OrderIntent,
|
||||
): Promise<Record<string, unknown>> {
|
||||
try {
|
||||
return await this.sendOrderAndWait(accountId, contractSymbol, quantity, action, orderType, price);
|
||||
return await this.sendOrderAndWait(accountId, contractSymbol, quantity, action, orderType, price, intent);
|
||||
} catch (err) {
|
||||
if (!isOrderNotPlaced(err)) this.suspectAccounts.set(accountId, Date.now());
|
||||
throw err;
|
||||
@@ -1633,6 +1635,7 @@ export class VestClient implements PlatformClient {
|
||||
action: 'Buy' | 'Sell',
|
||||
orderType: 'Market' | 'Limit',
|
||||
price?: number,
|
||||
intent?: OrderIntent,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { uuid, symbol, step, spec } = this.orderContext(accountId, contractSymbol);
|
||||
const side: 'long' | 'short' = action === 'Buy' ? 'long' : 'short';
|
||||
@@ -1643,7 +1646,8 @@ export class VestClient implements PlatformClient {
|
||||
throw new OrderNotPlacedError(`Vest position check failed — order not sent: ${errorMessage(err)}`);
|
||||
}
|
||||
const existing = positions.find((p) => p.symbol === symbol);
|
||||
if (existing && existing.side === side) throw new OrderNotPlacedError(`adding to an open ${symbol} position is not supported`);
|
||||
const route = vestOrderEffect({ symbol, held: existing?.side ?? null, side, intent });
|
||||
if ('refused' in route) throw route.nothingToClose ? new NothingToCloseError(route.refused) : new OrderNotPlacedError(route.refused);
|
||||
|
||||
const units = roundToStep(contracts * step, step);
|
||||
const qty = (n: number) => formatDecimal(n, spec.sizeDecimals);
|
||||
|
||||
Reference in New Issue
Block a user