57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
|
|
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,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|