DXtrade: an entry never closes a hand trade, and a flatten never opens one
DXtrade dropped the intent the engine now passes, so on the live Tradeify247 accounts an entry could still close a hand position as the "entry" and the leg's flatten then OPEN a position with no exits. The open/close decision moves out of vest-helpers into platform-client's orderEffect, which both clients now follow to pick OPEN or CLOSE. A flatten's flat read is "nothing to flatten" only when the client's own record agrees. One empty read just after a fill the client saw may be the gateway lagging, so it stays a plain refusal and keeps the FLATTEN FAILED alarm. A check now fails if an engine order drops its intent or a client stops going by orderEffect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e90f21ed91
commit
378530595e
+7
-6
@@ -47,13 +47,13 @@ import {
|
||||
import { getCachedFirm } from './firm-catalog';
|
||||
import { firmInstruments } from './instruments';
|
||||
import type { PlatformClient, PlatformAccount, PlatformContract, ClientHealth, PickContext, OrderIntent } from './platform-client';
|
||||
import { OrderNotPlacedError, NothingToCloseError, isOrderNotPlaced, errorMessage } from './platform-client';
|
||||
import { OrderNotPlacedError, isOrderNotPlaced, errorMessage, orderEffect } 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, vestOrderEffect, VEST_STATUS_ACTIVE, VEST_TXN, VEST_STOP_SLIPPAGE_FRACTION,
|
||||
parseHistoryPosition, jwtClaimStrings, accountScopedToken, VEST_STATUS_ACTIVE, VEST_TXN, VEST_STOP_SLIPPAGE_FRACTION,
|
||||
} from './vest-helpers';
|
||||
import type { VestCapitalAccount, VestPosition, VestOpenOrder, VestTxn, VestTrade } from './vest-helpers';
|
||||
|
||||
@@ -1606,7 +1606,7 @@ 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 —
|
||||
* only the one `intent` names, when given (vestOrderEffect). Adding to a
|
||||
* only the one `intent` names, when given (orderEffect). 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`.
|
||||
@@ -1645,9 +1645,10 @@ export class VestClient implements PlatformClient {
|
||||
} catch (err) {
|
||||
throw new OrderNotPlacedError(`Vest position check failed — order not sent: ${errorMessage(err)}`);
|
||||
}
|
||||
const existing = positions.find((p) => p.symbol === symbol);
|
||||
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 held = positions.find((p) => p.symbol === symbol);
|
||||
const recorded = this.positionsDetail.get(accountId)?.find((p) => p.symbol === symbol)?.side ?? null;
|
||||
// orderEffect picks open or close; `existing` is the position a close closes.
|
||||
const existing = orderEffect({ symbol, held: held?.side ?? null, recorded, side, intent }) === 'close' ? held : undefined;
|
||||
|
||||
const units = roundToStep(contracts * step, step);
|
||||
const qty = (n: number) => formatDecimal(n, spec.sizeDecimals);
|
||||
|
||||
Reference in New Issue
Block a user