export type ExecutableOrderSide = "buy" | "sell"; export interface ClampExecutableOrderQuantityParams { side: ExecutableOrderSide; requestedQuantity: number; maxBuyQuantity?: number; holdingQuantity?: number; sellableQuantity?: number; } export interface ClampExecutableOrderQuantityResult { ok: boolean; quantity: number; adjusted: boolean; } /** * [목적] * 주문 전 계좌 제약(매수가능/보유/매도가능)에 맞춰 최종 실행 수량을 순수 계산합니다. * * [입력 -> 처리 -> 결과] * 요청 수량/사이드/제약값 -> 가능 최대 수량으로 clamp -> 실행 가능 여부와 수량 반환 */ export function clampExecutableOrderQuantity( params: ClampExecutableOrderQuantityParams, ): ClampExecutableOrderQuantityResult { const requestedQuantity = Math.max(0, Math.floor(params.requestedQuantity)); if (requestedQuantity <= 0) { return { ok: false, quantity: 0, adjusted: false, }; } if (params.side === "buy") { const maxBuyQuantity = Math.max(0, Math.floor(params.maxBuyQuantity ?? 0)); const quantity = Math.min(requestedQuantity, maxBuyQuantity); return { ok: quantity > 0, quantity, adjusted: quantity !== requestedQuantity, }; } const holdingQuantity = Math.max(0, Math.floor(params.holdingQuantity ?? 0)); const sellableQuantity = Math.max(0, Math.floor(params.sellableQuantity ?? 0)); const maxSellQuantity = Math.min(holdingQuantity, sellableQuantity); const quantity = Math.min(requestedQuantity, maxSellQuantity); return { ok: quantity > 0, quantity, adjusted: quantity !== requestedQuantity, }; }