전체적인 리팩토링

This commit is contained in:
2026-03-12 09:26:27 +09:00
parent 406af7408a
commit e51d767878
97 changed files with 13651 additions and 363 deletions

View File

@@ -0,0 +1,482 @@
/**
* @file features/autotrade/types/autotrade.types.ts
* @description 자동매매 기능에서 공통으로 사용하는 타입 정의입니다.
*/
export const AUTOTRADE_TECHNIQUE_IDS = [
"orb",
"vwap_reversion",
"volume_breakout",
"ma_crossover",
"gap_breakout",
"intraday_box_reversion",
"intraday_breakout_scalp",
] as const;
export type AutotradeTechniqueId = (typeof AUTOTRADE_TECHNIQUE_IDS)[number];
export interface AutotradeTechniqueOption {
id: AutotradeTechniqueId;
label: string;
description: string;
}
export const AUTOTRADE_TECHNIQUE_OPTIONS: AutotradeTechniqueOption[] = [
{
id: "orb",
label: "ORB(시가 범위 돌파)",
description: "시가 근처 범위를 돌파할 때 추세 진입 신호를 확인합니다.",
},
{
id: "vwap_reversion",
label: "VWAP 되돌림",
description: "VWAP에서 과하게 이탈한 가격이 평균으로 복귀하는 구간을 봅니다.",
},
{
id: "volume_breakout",
label: "거래량 돌파",
description: "거래량 급증과 함께 방향성이 생기는 순간을 포착합니다.",
},
{
id: "ma_crossover",
label: "이동평균 교차",
description: "단기/중기 평균선 교차로 추세 전환 여부를 확인합니다.",
},
{
id: "gap_breakout",
label: "갭 돌파",
description: "갭 상승/하락 이후 추가 돌파 또는 되돌림을 판단합니다.",
},
{
id: "intraday_box_reversion",
label: "상승 후 박스권 단타",
description:
"당일 상승 이후 박스권 횡보 구간에서 상단/하단 왕복(오르락내리락) 단타를 노립니다.",
},
{
id: "intraday_breakout_scalp",
label: "상승구간 눌림-재돌파 단타",
description:
"1분봉 상승 추세에서 저거래량 눌림 후 고점 재돌파(거래량 재유입) 구간을 노립니다.",
},
];
export const AUTOTRADE_DEFAULT_TECHNIQUES: AutotradeTechniqueId[] = [
"ma_crossover",
"vwap_reversion",
"intraday_box_reversion",
"intraday_breakout_scalp",
];
export type AutotradeEngineState =
| "IDLE"
| "RUNNING"
| "STOPPING"
| "STOPPED"
| "ERROR";
export const AUTOTRADE_AI_MODE_IDS = [
"auto",
"openai_api",
"subscription_cli",
"rule_fallback",
] as const;
export type AutotradeAiMode = (typeof AUTOTRADE_AI_MODE_IDS)[number];
export const AUTOTRADE_SUBSCRIPTION_CLI_VENDOR_IDS = [
"auto",
"codex",
"gemini",
] as const;
export type AutotradeSubscriptionCliVendor =
(typeof AUTOTRADE_SUBSCRIPTION_CLI_VENDOR_IDS)[number];
export interface AutotradeSubscriptionCliVendorOption {
id: AutotradeSubscriptionCliVendor;
label: string;
description: string;
}
export const AUTOTRADE_SUBSCRIPTION_CLI_VENDOR_OPTIONS: AutotradeSubscriptionCliVendorOption[] =
[
{
id: "auto",
label: "자동 선택",
description: "Codex -> Gemini 순서로 시도합니다.",
},
{
id: "codex",
label: "Codex CLI",
description: "OpenAI Codex CLI만 사용합니다.",
},
{
id: "gemini",
label: "Gemini CLI",
description: "Google Gemini CLI만 사용합니다.",
},
];
export interface AutotradeSubscriptionCliModelOption {
value: string;
label: string;
description: string;
}
// [출처] 공식 문서 기준 추천 프리셋
// - Codex Models: https://developers.openai.com/codex/models
// - Gemini CLI model command: https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/model.md
export const AUTOTRADE_SUBSCRIPTION_CLI_MODEL_OPTIONS = {
codex: [
{
value: "gpt-5.4",
label: "gpt-5.4",
description: "Codex 추천 기본형",
},
{
value: "gpt-5.3-codex",
label: "gpt-5.3-codex",
description: "Codex 5.3 고성능 라인",
},
{
value: "gpt-5.3-codex-spark",
label: "gpt-5.3-codex-spark",
description: "Codex 5.3 경량형",
},
{
value: "gpt-5.2-codex",
label: "gpt-5.2-codex",
description: "Codex 5.2 균형형",
},
{
value: "gpt-5.2",
label: "gpt-5.2",
description: "Codex 5.2 범용형",
},
{
value: "gpt-5.1-codex-max",
label: "gpt-5.1-codex-max",
description: "문맥 확장형 Codex 5.1",
},
{
value: "gpt-5.1",
label: "gpt-5.1",
description: "Codex 5.1 범용형",
},
{
value: "gpt-5.1-codex",
label: "gpt-5.1-codex",
description: "Codex 5.1 기본형",
},
{
value: "gpt-5-codex",
label: "gpt-5-codex (안정형)",
description: "Codex 안정형",
},
{
value: "gpt-5-codex-mini",
label: "gpt-5-codex-mini",
description: "Codex 경량형",
},
{
value: "gpt-5",
label: "gpt-5",
description: "Codex 범용 경량 라인",
},
] satisfies AutotradeSubscriptionCliModelOption[],
gemini: [
{
value: "auto",
label: "auto (권장)",
description: "상황에 따라 Pro/Flash 계열을 자동 선택",
},
{
value: "gemini-3.1-pro-preview",
label: "gemini-3.1-pro-preview (신규)",
description: "Gemini 3.1 고성능 추론/코딩 프리뷰",
},
{
value: "gemini-3.1-flash-lite-preview",
label: "gemini-3.1-flash-lite-preview",
description: "Gemini 3.1 경량 고속 프리뷰",
},
{
value: "gemini-3-flash-preview",
label: "gemini-3-flash-preview",
description: "Gemini 3 고속 프리뷰",
},
{
value: "gemini-2.5-pro",
label: "gemini-2.5-pro",
description: "고난도 추론 중심",
},
{
value: "gemini-2.5-flash",
label: "gemini-2.5-flash",
description: "속도/품질 균형형",
},
{
value: "gemini-2.5-flash-lite",
label: "gemini-2.5-flash-lite",
description: "가벼운 작업용 고속 모델",
},
{
value: "gemini-3-pro-preview",
label: "gemini-3-pro-preview (종료예정)",
description: "공식 문서 기준 2026-03-09 종료 예정 프리뷰",
},
] satisfies AutotradeSubscriptionCliModelOption[],
} as const;
export interface AutotradeAiModeOption {
id: AutotradeAiMode;
label: string;
description: string;
}
export const AUTOTRADE_AI_MODE_OPTIONS: AutotradeAiModeOption[] = [
{
id: "auto",
label: "자동(권장)",
description:
"OpenAI API 키가 있으면 OpenAI를 사용하고, 없으면 구독형 CLI를 시도합니다. 둘 다 실패하면 규칙 기반으로 전환합니다.",
},
{
id: "openai_api",
label: "OpenAI API",
description: "서버에서 OpenAI API를 직접 호출해 판단합니다.",
},
{
id: "subscription_cli",
label: "구독형 CLI 자동판단",
description: "서버에 설치된 Codex/Gemini CLI로 자동 판단을 생성합니다.",
},
{
id: "rule_fallback",
label: "규칙 기반",
description: "AI 호출 없이 내부 규칙 엔진으로만 판단합니다.",
},
];
export type AutotradeStopReason =
| "browser_exit"
| "external_leave"
| "manual"
| "emergency"
| "heartbeat_timeout";
export interface AutotradeSetupFormValues {
aiMode: AutotradeAiMode;
subscriptionCliVendor: AutotradeSubscriptionCliVendor;
subscriptionCliModel: string;
prompt: string;
selectedTechniques: AutotradeTechniqueId[];
allocationPercent: number;
allocationAmount: number;
dailyLossPercent: number;
dailyLossAmount: number;
confidenceThreshold: number;
agreeStopOnExit: boolean;
}
export interface AutotradeCompiledStrategy {
provider: "openai" | "fallback" | "subscription_cli";
providerVendor?: "codex" | "gemini";
providerModel?: string;
summary: string;
selectedTechniques: AutotradeTechniqueId[];
confidenceThreshold: number;
maxDailyOrders: number;
cooldownSec: number;
maxOrderAmountRatio: number;
createdAt: string;
}
export interface AutotradeValidationResult {
isValid: boolean;
blockedReasons: string[];
warnings: string[];
cashBalance: number;
effectiveAllocationAmount: number;
effectiveDailyLossLimit: number;
}
export interface AutotradeMinuteCandle {
time: string;
open: number;
high: number;
low: number;
close: number;
volume: number;
timestamp?: number;
}
export interface AutotradeMinutePatternContext {
timeframe: "1m";
candleCount: number;
impulseDirection: "up" | "down" | "flat";
impulseBarCount: number;
consolidationBarCount: number;
impulseChangeRate?: number;
impulseRangePercent?: number;
consolidationRangePercent?: number;
consolidationCloseClusterPercent?: number;
consolidationVolumeRatio?: number;
breakoutUpper?: number;
breakoutLower?: number;
}
export interface AutotradeBudgetContext {
setupAllocationPercent: number;
setupAllocationAmount: number;
effectiveAllocationAmount: number;
strategyMaxOrderAmountRatio: number;
effectiveOrderBudgetAmount: number;
estimatedBuyUnitCost: number;
estimatedBuyableQuantity: number;
}
export interface AutotradePortfolioContext {
holdingQuantity: number;
sellableQuantity: number;
averagePrice: number;
estimatedSellableNetAmount?: number;
}
export interface AutotradeExecutionCostProfileSnapshot {
buyFeeRate: number;
sellFeeRate: number;
sellTaxRate: number;
}
export interface AutotradeSessionInfo {
sessionId: string;
symbol: string;
runtimeState: "RUNNING" | "STOPPED";
leaderTabId: string;
startedAt: string;
lastHeartbeatAt: string;
endedAt: string | null;
stopReason: AutotradeStopReason | null;
effectiveAllocationAmount: number;
effectiveDailyLossLimit: number;
}
export interface AutotradeMarketSnapshot {
symbol: string;
stockName?: string;
market?: "KOSPI" | "KOSDAQ";
requestAtIso?: string;
requestAtKst?: string;
tickTime?: string;
executionClassCode?: string;
isExpected?: boolean;
trId?: string;
currentPrice: number;
prevClose?: number;
changeRate: number;
open: number;
high: number;
low: number;
tradeVolume: number;
accumulatedVolume: number;
tradeStrength?: number;
askPrice1?: number;
bidPrice1?: number;
askSize1?: number;
bidSize1?: number;
totalAskSize?: number;
totalBidSize?: number;
buyExecutionCount?: number;
sellExecutionCount?: number;
netBuyExecutionCount?: number;
spread?: number;
spreadRate?: number;
dayRangePercent?: number;
dayRangePosition?: number;
volumeRatio?: number;
recentTradeCount?: number;
recentTradeVolumeSum?: number;
recentAverageTradeVolume?: number;
accumulatedVolumeDelta?: number;
netBuyExecutionDelta?: number;
orderBookImbalance?: number;
liquidityDepth?: number;
topLevelOrderBookImbalance?: number;
buySellExecutionRatio?: number;
recentPriceHigh?: number;
recentPriceLow?: number;
recentPriceRangePercent?: number;
recentTradeVolumes?: number[];
recentNetBuyTrail?: number[];
recentTickAgesSec?: number[];
intradayMomentum?: number;
recentReturns?: number[];
recentPrices: number[];
marketDataLatencySec?: number;
recentMinuteCandles?: AutotradeMinuteCandle[];
minutePatternContext?: AutotradeMinutePatternContext;
budgetContext?: AutotradeBudgetContext;
portfolioContext?: AutotradePortfolioContext;
executionCostProfile?: AutotradeExecutionCostProfileSnapshot;
}
export interface AutotradeProposedOrder {
symbol: string;
side: "buy" | "sell";
orderType: "limit" | "market";
price?: number;
quantity?: number;
}
export interface AutotradeSignalCandidate {
signal: "buy" | "sell" | "hold";
confidence: number;
reason: string;
ttlSec: number;
riskFlags: string[];
proposedOrder?: AutotradeProposedOrder;
source: "openai" | "fallback" | "subscription_cli";
providerVendor?: "codex" | "gemini";
providerModel?: string;
}
export interface AutotradeRuntimeLog {
id: string;
level: "info" | "warning" | "error";
stage?:
| "session"
| "strategy_compile"
| "strategy_validate"
| "signal_request"
| "signal_response"
| "risk_gate"
| "order_execution"
| "order_blocked"
| "provider_fallback"
| "engine_error";
message: string;
detail?: string;
createdAt: string;
}
export interface AutotradeCompileResponse {
ok: boolean;
compiledStrategy: AutotradeCompiledStrategy;
}
export interface AutotradeValidateResponse {
ok: boolean;
validation: AutotradeValidationResult;
}
export interface AutotradeSessionResponse {
ok: boolean;
session: AutotradeSessionInfo;
}
export interface AutotradeSignalResponse {
ok: boolean;
signal: AutotradeSignalCandidate;
}