feat: 프로젝트 개요 및 컴포넌트별 명세, 로드맵 등 문서 추가
This commit is contained in:
695
docs/04-workflows.md
Normal file
695
docs/04-workflows.md
Normal file
@@ -0,0 +1,695 @@
|
||||
# QuantBench 주요 워크플로우
|
||||
|
||||
이 문서는 시스템의 핵심 워크플로우를 상세히 설명합니다.
|
||||
|
||||
## 1. 전략 실행 워크플로우
|
||||
|
||||
### 1.1 전체 흐름
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Scheduler
|
||||
participant M as Mgmt
|
||||
participant ST as Strategy
|
||||
participant D as Data
|
||||
participant R as Risk
|
||||
participant B as Balance
|
||||
participant A as Analytics
|
||||
participant MO as Monitor
|
||||
|
||||
Note over S: 스케줄 트리거 발생
|
||||
|
||||
S->>M: 1. 컨테이너 정보 조회
|
||||
M-->>S: 컨테이너 설정 & 현재 포지션
|
||||
|
||||
S->>ST: 2. 전략 실행 요청
|
||||
ST->>D: 2.1 시장 데이터 조회
|
||||
D-->>ST: 최신 시세 데이터
|
||||
ST->>ST: 2.2 신호 생성
|
||||
ST-->>S: 매매 신호 반환
|
||||
|
||||
S->>R: 3. 리스크 체크
|
||||
R->>R: 3.1 포지션 사이즈 검증
|
||||
R->>R: 3.2 VaR 계산
|
||||
R->>R: 3.3 한도 확인
|
||||
|
||||
alt 리스크 체크 실패
|
||||
R-->>S: 검증 실패
|
||||
S->>MO: 알림 발송
|
||||
MO-->>S: 알림 완료
|
||||
Note over S: 실행 중단
|
||||
else 리스크 체크 통과
|
||||
R-->>S: 검증 통과
|
||||
|
||||
alt 승인 필요 모드
|
||||
S->>S: 승인 요청 생성
|
||||
S->>MO: 사용자에게 알림
|
||||
Note over S: 사용자 승인 대기
|
||||
S->>S: 승인 완료
|
||||
end
|
||||
|
||||
S->>B: 4. 주문 제출
|
||||
B->>B: 4.1 증권사 API 호출
|
||||
B-->>S: 주문 접수 완료
|
||||
|
||||
Note over B: 체결 대기...
|
||||
|
||||
B->>B: 4.2 체결 확인
|
||||
B->>M: 5. 포지션 업데이트
|
||||
M->>M: 5.1 컨테이너 밸런스 갱신
|
||||
|
||||
M->>A: 6. 성과 계산 트리거
|
||||
A->>A: 6.1 수익률 계산
|
||||
A->>A: 6.2 리스크 지표 갱신
|
||||
|
||||
A->>MO: 7. 메트릭 업데이트
|
||||
MO->>MO: 7.1 대시보드 갱신
|
||||
MO->>MO: 7.2 이상 탐지
|
||||
|
||||
alt 이상 탐지 시
|
||||
MO->>MO: 알림 발송
|
||||
end
|
||||
|
||||
S->>S: 8. 실행 완료 기록
|
||||
end
|
||||
```
|
||||
|
||||
### 1.2 상세 단계
|
||||
|
||||
#### 1단계: 스케줄 트리거
|
||||
```typescript
|
||||
// 스케줄러가 설정된 시간에 실행을 트리거
|
||||
scheduler.onTrigger(schedule => {
|
||||
const container = mgmt.getContainer(schedule.containerId)
|
||||
const execution = scheduler.createExecution(container, schedule)
|
||||
scheduler.executeStrategy(execution)
|
||||
})
|
||||
```
|
||||
|
||||
#### 2단계: 신호 생성
|
||||
```typescript
|
||||
// 전략이 시장 데이터를 기반으로 신호 생성
|
||||
const marketData = await data.getMarketData(container.strategyUniverse)
|
||||
const signals = await strategy.generateSignals(marketData, container.parameters)
|
||||
|
||||
// 생성된 신호 예시
|
||||
// [
|
||||
// { symbol: 'AAPL', action: 'BUY', targetWeight: 0.3 },
|
||||
// { symbol: 'GOOGL', action: 'SELL', targetWeight: 0 }
|
||||
// ]
|
||||
```
|
||||
|
||||
#### 3단계: 리스크 검증
|
||||
```typescript
|
||||
// 신호를 주문으로 변환
|
||||
const plannedOrders = strategy.signalsToOrders(signals, container)
|
||||
|
||||
// 리스크 검증
|
||||
const riskResult = await risk.validateOrders(plannedOrders, container)
|
||||
|
||||
if (!riskResult.passed) {
|
||||
// 리스크 위반 시 실행 중단
|
||||
await monitor.sendAlert({
|
||||
type: 'RISK',
|
||||
severity: 'ERROR',
|
||||
message: `리스크 검증 실패: ${riskResult.violations}`,
|
||||
containerId: container.id
|
||||
})
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### 4단계: 주문 실행
|
||||
```typescript
|
||||
// 승인 필요 모드인 경우
|
||||
if (schedule.executionMode === 'APPROVAL_REQUIRED') {
|
||||
const approvalRequest = await scheduler.requestApproval(execution)
|
||||
await monitor.sendNotification(approvalRequest)
|
||||
|
||||
// 승인 대기
|
||||
const approved = await scheduler.waitForApproval(approvalRequest)
|
||||
if (!approved) return
|
||||
}
|
||||
|
||||
// 주문 제출
|
||||
const executedOrders = []
|
||||
for (const order of plannedOrders) {
|
||||
const result = await balance.placeOrder(order)
|
||||
executedOrders.push(result)
|
||||
}
|
||||
```
|
||||
|
||||
#### 5단계: 포지션 업데이트
|
||||
```typescript
|
||||
// 체결 완료 후 컨테이너 밸런스 업데이트
|
||||
for (const order of executedOrders) {
|
||||
if (order.status === 'FILLED') {
|
||||
await mgmt.updatePosition(container.id, {
|
||||
symbol: order.symbol,
|
||||
quantity: order.side === 'BUY' ? order.filledQuantity : -order.filledQuantity,
|
||||
price: order.averageFillPrice
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 밸런스 조정 (reconciliation)
|
||||
await mgmt.reconcileContainer(container.id)
|
||||
```
|
||||
|
||||
## 2. 리스크 관리 워크플로우
|
||||
|
||||
### 2.1 사전 주문 검증
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Start([주문 요청]) --> GetPosition[현재 포지션 조회]
|
||||
GetPosition --> CalcNewPosition[신규 포지션 계산]
|
||||
|
||||
CalcNewPosition --> CheckBalance{잔고 충분?}
|
||||
CheckBalance -->|No| Reject1[주문 거부:<br/>잔고 부족]
|
||||
CheckBalance -->|Yes| CheckSize
|
||||
|
||||
CheckSize{포지션 사이즈<br/>한도 내?}
|
||||
CheckSize -->|No| Reject2[주문 거부:<br/>사이즈 초과]
|
||||
CheckSize -->|Yes| CheckConcentration
|
||||
|
||||
CheckConcentration{집중도<br/>한도 내?}
|
||||
CheckConcentration -->|No| Reject3[주문 거부:<br/>집중도 초과]
|
||||
CheckConcentration -->|Yes| CalcVaR
|
||||
|
||||
CalcVaR[VaR 계산]
|
||||
CalcVaR --> CheckVaR{VaR<br/>한도 내?}
|
||||
CheckVaR -->|No| Warning1[경고 발생<br/>계속 진행]
|
||||
CheckVaR -->|Yes| CheckDrawdown
|
||||
|
||||
Warning1 --> CheckDrawdown
|
||||
|
||||
CheckDrawdown{최대 낙폭<br/>한도 내?}
|
||||
CheckDrawdown -->|No| Reject4[주문 거부:<br/>MDD 초과]
|
||||
CheckDrawdown -->|Yes| CheckLeverage
|
||||
|
||||
CheckLeverage{레버리지<br/>한도 내?}
|
||||
CheckLeverage -->|No| Reject5[주문 거부:<br/>레버리지 초과]
|
||||
CheckLeverage -->|Yes| Approve
|
||||
|
||||
Approve([검증 통과:<br/>주문 실행])
|
||||
|
||||
Reject1 --> Log[리스크 로그 기록]
|
||||
Reject2 --> Log
|
||||
Reject3 --> Log
|
||||
Reject4 --> Log
|
||||
Reject5 --> Log
|
||||
|
||||
Approve --> ExecuteOrder[주문 제출]
|
||||
ExecuteOrder --> Monitor[실시간 모니터링]
|
||||
|
||||
Monitor --> CheckStop{손절/익절<br/>조건 충족?}
|
||||
CheckStop -->|Yes| AutoClose[자동 청산]
|
||||
CheckStop -->|No| Monitor
|
||||
|
||||
style Start fill:#4CAF50,color:#fff
|
||||
style Approve fill:#4CAF50,color:#fff
|
||||
style Reject1 fill:#F44336,color:#fff
|
||||
style Reject2 fill:#F44336,color:#fff
|
||||
style Reject3 fill:#F44336,color:#fff
|
||||
style Reject4 fill:#F44336,color:#fff
|
||||
style Reject5 fill:#F44336,color:#fff
|
||||
style Warning1 fill:#FF9800,color:#fff
|
||||
```
|
||||
|
||||
### 2.2 실시간 리스크 모니터링
|
||||
|
||||
```typescript
|
||||
// 실시간 포지션 모니터링
|
||||
monitor.watchContainer(container.id, async (positions, marketData) => {
|
||||
// 손절 조건 체크
|
||||
for (const position of positions) {
|
||||
const stopLoss = risk.getStopLoss(container.id, position.symbol)
|
||||
if (stopLoss && position.unrealizedPnLPct <= -stopLoss.pct) {
|
||||
// 손절 트리거
|
||||
await scheduler.executeEmergencyClose(container.id, position.symbol)
|
||||
await monitor.sendAlert({
|
||||
type: 'RISK',
|
||||
severity: 'WARNING',
|
||||
message: `손절 실행: ${position.symbol} ${position.unrealizedPnLPct}%`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MDD 체크
|
||||
const performance = await analytics.getCurrentPerformance(container.id)
|
||||
const limits = await risk.getRiskLimits(container.id)
|
||||
|
||||
if (performance.currentDrawdown >= limits.loss.maxDrawdownPct) {
|
||||
// 컨테이너 일시 정지
|
||||
await mgmt.pauseContainer(container.id)
|
||||
await monitor.sendAlert({
|
||||
type: 'RISK',
|
||||
severity: 'CRITICAL',
|
||||
message: `최대 낙폭 초과: ${performance.currentDrawdown}%`
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 3. 승인 워크플로우
|
||||
|
||||
### 3.1 승인 요청 및 처리
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Trigger[스케줄 트리거] --> GenerateSignals[신호 생성]
|
||||
GenerateSignals --> RiskCheck[리스크 체크]
|
||||
|
||||
RiskCheck --> CheckMode{실행 모드}
|
||||
|
||||
CheckMode -->|AUTO| AutoExecute[자동 실행]
|
||||
CheckMode -->|APPROVAL| CreateRequest
|
||||
|
||||
CreateRequest[승인 요청 생성]
|
||||
CreateRequest --> SendNotification[알림 발송]
|
||||
SendNotification --> WaitApproval[승인 대기]
|
||||
|
||||
WaitApproval --> CheckTimeout{타임아웃?}
|
||||
CheckTimeout -->|Yes| Expired[요청 만료]
|
||||
CheckTimeout -->|No| WaitApproval
|
||||
|
||||
WaitApproval --> UserDecision{사용자 결정}
|
||||
|
||||
UserDecision -->|승인| Execute[주문 실행]
|
||||
UserDecision -->|거부| Rejected[실행 거부]
|
||||
|
||||
AutoExecute --> Execute
|
||||
|
||||
Execute --> PlaceOrders[주문 제출]
|
||||
PlaceOrders --> Success{성공?}
|
||||
|
||||
Success -->|Yes| NotifySuccess[성공 알림]
|
||||
Success -->|No| NotifyFail[실패 알림]
|
||||
|
||||
Rejected --> LogRejection[거부 로그]
|
||||
Expired --> LogExpired[만료 로그]
|
||||
|
||||
NotifySuccess --> End([완료])
|
||||
NotifyFail --> End
|
||||
LogRejection --> End
|
||||
LogExpired --> End
|
||||
|
||||
style Execute fill:#4CAF50,color:#fff
|
||||
style Rejected fill:#F44336,color:#fff
|
||||
style Expired fill:#FF9800,color:#fff
|
||||
style AutoExecute fill:#2196F3,color:#fff
|
||||
```
|
||||
|
||||
### 3.2 승인 요청 생성
|
||||
|
||||
```typescript
|
||||
async function createApprovalRequest(execution: Execution): Promise<ApprovalRequest> {
|
||||
// 주문 요약 생성
|
||||
const summary = {
|
||||
numOrders: execution.plannedOrders.length,
|
||||
buyValue: execution.plannedOrders
|
||||
.filter(o => o.side === 'BUY')
|
||||
.reduce((sum, o) => sum + o.quantity * o.price!, 0),
|
||||
sellValue: execution.plannedOrders
|
||||
.filter(o => o.side === 'SELL')
|
||||
.reduce((sum, o) => sum + o.quantity * o.price!, 0),
|
||||
estimatedCommission: execution.estimatedCost.commission
|
||||
}
|
||||
|
||||
// 승인 요청 생성
|
||||
const request = await scheduler.createApprovalRequest({
|
||||
executionId: execution.id,
|
||||
summary,
|
||||
orders: execution.plannedOrders,
|
||||
expiresAt: new Date(Date.now() + 30 * 60 * 1000) // 30분 후 만료
|
||||
})
|
||||
|
||||
// 알림 발송
|
||||
await monitor.sendNotification({
|
||||
type: 'EXECUTION',
|
||||
severity: 'INFO',
|
||||
title: '주문 승인 요청',
|
||||
message: `
|
||||
${summary.numOrders}건의 주문이 대기 중입니다.
|
||||
매수: ${summary.buyValue.toLocaleString()}원
|
||||
매도: ${summary.sellValue.toLocaleString()}원
|
||||
예상 수수료: ${summary.estimatedCommission.toLocaleString()}원
|
||||
`,
|
||||
channels: ['EMAIL', 'PUSH']
|
||||
})
|
||||
|
||||
return request
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 컨테이너 생명주기
|
||||
|
||||
### 4.1 상태 전이도
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Created: createContainer()
|
||||
|
||||
Created --> Active: activate()
|
||||
Created --> [*]: delete()
|
||||
|
||||
Active --> Running: scheduler trigger
|
||||
Active --> Paused: pause()
|
||||
Active --> [*]: delete()
|
||||
|
||||
Running --> Active: execution complete
|
||||
Running --> Error: execution failed
|
||||
Running --> Paused: emergency stop
|
||||
|
||||
Paused --> Active: resume()
|
||||
Paused --> [*]: delete()
|
||||
|
||||
Error --> Active: resolve & restart
|
||||
Error --> Paused: manual intervention
|
||||
Error --> [*]: delete()
|
||||
|
||||
state Running {
|
||||
[*] --> GenerateSignals
|
||||
GenerateSignals --> RiskCheck
|
||||
RiskCheck --> PlaceOrders: pass
|
||||
RiskCheck --> [*]: fail
|
||||
PlaceOrders --> AwaitFill
|
||||
AwaitFill --> UpdatePosition
|
||||
UpdatePosition --> [*]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 컨테이너 생성 및 활성화
|
||||
|
||||
```typescript
|
||||
// 1. 컨테이너 생성
|
||||
const container = await mgmt.createContainer({
|
||||
accountId: 'account-123',
|
||||
name: '성장주 전략',
|
||||
allocation: {
|
||||
initialAmount: 10000000, // 1천만원
|
||||
cashReserve: 1000000 // 최소 100만원 현금 보유
|
||||
},
|
||||
constraints: {
|
||||
maxSinglePositionPct: 20, // 단일 종목 최대 20%
|
||||
maxDrawdown: 15, // 최대 낙폭 15%
|
||||
allowedAssetClasses: ['STOCK']
|
||||
}
|
||||
})
|
||||
|
||||
// 2. 전략 할당
|
||||
await mgmt.assignStrategy(container.id, 'strategy-momentum-v1')
|
||||
|
||||
// 3. 리스크 한도 설정
|
||||
await risk.setRiskLimits(container.id, {
|
||||
position: {
|
||||
maxSinglePositionPct: 20,
|
||||
maxSectorPct: 40,
|
||||
maxTotalLeverage: 1.0
|
||||
},
|
||||
loss: {
|
||||
maxDailyLossPct: 5,
|
||||
maxDrawdownPct: 15,
|
||||
stopLossEnabled: true
|
||||
}
|
||||
})
|
||||
|
||||
// 4. 스케줄 설정
|
||||
await scheduler.createSchedule({
|
||||
containerId: container.id,
|
||||
trigger: {
|
||||
type: 'CRON',
|
||||
expression: '0 9 * * 1-5' // 평일 오전 9시
|
||||
},
|
||||
executionMode: 'APPROVAL_REQUIRED',
|
||||
constraints: {
|
||||
marketHoursOnly: true,
|
||||
skipHolidays: true
|
||||
}
|
||||
})
|
||||
|
||||
// 5. 컨테이너 활성화
|
||||
await mgmt.activateContainer(container.id)
|
||||
```
|
||||
|
||||
## 5. 백테스트 워크플로우
|
||||
|
||||
### 5.1 백테스트 실행
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 사용자
|
||||
participant ST as Strategy
|
||||
participant D as Data
|
||||
participant BE as Backtest Engine
|
||||
participant A as Analytics
|
||||
|
||||
U->>ST: 백테스트 요청
|
||||
ST->>D: 과거 데이터 조회
|
||||
D-->>ST: 시계열 데이터
|
||||
|
||||
ST->>BE: 백테스트 시작
|
||||
|
||||
loop 각 날짜마다
|
||||
BE->>ST: 신호 생성
|
||||
ST-->>BE: 매매 신호
|
||||
BE->>BE: 가상 주문 체결
|
||||
BE->>BE: 포트폴리오 업데이트
|
||||
end
|
||||
|
||||
BE->>A: 성과 계산
|
||||
A-->>BE: 성과 지표
|
||||
|
||||
BE-->>U: 백테스트 결과
|
||||
```
|
||||
|
||||
### 5.2 백테스트 구현
|
||||
|
||||
```typescript
|
||||
async function runBacktest(config: BacktestConfig): Promise<BacktestResult> {
|
||||
// 1. 데이터 로드
|
||||
const marketData = await data.getHistoricalPrices(
|
||||
config.universe,
|
||||
config.startDate,
|
||||
config.endDate
|
||||
)
|
||||
|
||||
// 2. 초기 상태 설정
|
||||
let portfolio = {
|
||||
cash: config.initialCapital,
|
||||
positions: {} as Record<string, number>,
|
||||
equity: [{ date: config.startDate, value: config.initialCapital }]
|
||||
}
|
||||
|
||||
const trades: Trade[] = []
|
||||
|
||||
// 3. 시뮬레이션 실행
|
||||
for (const date of marketData.dates) {
|
||||
// 신호 생성
|
||||
const signals = await strategy.generateSignals(
|
||||
marketData.getDataAsOf(date)
|
||||
)
|
||||
|
||||
// 주문 생성 및 체결
|
||||
for (const signal of signals) {
|
||||
if (signal.action === 'HOLD') continue
|
||||
|
||||
const price = marketData.getPrice(signal.symbol, date)
|
||||
const quantity = calculateQuantity(signal, portfolio, price)
|
||||
|
||||
// 슬리피지 및 수수료 적용
|
||||
const executionPrice = applySlippage(price, signal.action, config.costs.slippage)
|
||||
const commission = executionPrice * quantity * config.costs.commission
|
||||
|
||||
// 포트폴리오 업데이트
|
||||
if (signal.action === 'BUY') {
|
||||
portfolio.cash -= executionPrice * quantity + commission
|
||||
portfolio.positions[signal.symbol] = (portfolio.positions[signal.symbol] || 0) + quantity
|
||||
} else {
|
||||
portfolio.cash += executionPrice * quantity - commission
|
||||
portfolio.positions[signal.symbol] -= quantity
|
||||
}
|
||||
|
||||
// 거래 기록
|
||||
trades.push({
|
||||
date,
|
||||
symbol: signal.symbol,
|
||||
action: signal.action,
|
||||
quantity,
|
||||
price: executionPrice,
|
||||
commission
|
||||
})
|
||||
}
|
||||
|
||||
// 일일 자산 가치 계산
|
||||
const positionsValue = Object.entries(portfolio.positions)
|
||||
.reduce((sum, [symbol, qty]) => {
|
||||
return sum + qty * marketData.getPrice(symbol, date)
|
||||
}, 0)
|
||||
|
||||
portfolio.equity.push({
|
||||
date,
|
||||
value: portfolio.cash + positionsValue,
|
||||
cash: portfolio.cash,
|
||||
positions: { ...portfolio.positions }
|
||||
})
|
||||
}
|
||||
|
||||
// 4. 성과 지표 계산
|
||||
const metrics = await analytics.calculateMetrics({
|
||||
equity: portfolio.equity,
|
||||
trades,
|
||||
benchmark: config.benchmark
|
||||
})
|
||||
|
||||
return {
|
||||
id: generateId(),
|
||||
strategyId: config.strategyId,
|
||||
config,
|
||||
equity: portfolio.equity,
|
||||
trades,
|
||||
metrics,
|
||||
runAt: new Date(),
|
||||
duration: Date.now() - startTime
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 데이터 수집 워크플로우
|
||||
|
||||
### 6.1 데이터 수집 파이프라인
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "외부 소스"
|
||||
Broker[증권사 API]
|
||||
Yahoo[Yahoo Finance]
|
||||
Alpha[Alpha Vantage]
|
||||
end
|
||||
|
||||
subgraph "데이터 수집"
|
||||
Collector[Data Collector]
|
||||
RealTime[실시간 스트림]
|
||||
Historical[과거 데이터 수집]
|
||||
end
|
||||
|
||||
subgraph "데이터 처리"
|
||||
Validator[데이터 검증]
|
||||
Adjuster[조정 처리]
|
||||
QualityCheck[품질 체크]
|
||||
end
|
||||
|
||||
subgraph "저장소"
|
||||
Cache[(Redis<br/>실시간)]
|
||||
TimeSeries[(TimescaleDB<br/>과거)]
|
||||
Metadata[(Metadata<br/>종목 정보)]
|
||||
end
|
||||
|
||||
Broker --> RealTime
|
||||
Yahoo --> Historical
|
||||
Alpha --> Historical
|
||||
|
||||
RealTime --> Collector
|
||||
Historical --> Collector
|
||||
|
||||
Collector --> Validator
|
||||
Validator --> QualityCheck
|
||||
QualityCheck -->|통과| Adjuster
|
||||
QualityCheck -->|실패| Alert[품질 알림]
|
||||
|
||||
Adjuster --> Cache
|
||||
Adjuster --> TimeSeries
|
||||
Adjuster --> Metadata
|
||||
|
||||
style Cache fill:#FF6B6B,color:#fff
|
||||
style TimeSeries fill:#4ECDC4,color:#fff
|
||||
style Metadata fill:#45B7D1,color:#fff
|
||||
```
|
||||
|
||||
### 6.2 데이터 품질 관리
|
||||
|
||||
```typescript
|
||||
// 데이터 수집 및 검증
|
||||
async function collectAndValidateData(symbols: string[], date: Date) {
|
||||
for (const symbol of symbols) {
|
||||
// 1. 데이터 수집
|
||||
const rawData = await dataProvider.fetchPriceData(symbol, date)
|
||||
|
||||
// 2. 데이터 검증
|
||||
const validationResult = await data.validateData(rawData)
|
||||
|
||||
if (!validationResult.isValid) {
|
||||
// 품질 문제 감지
|
||||
await monitor.sendAlert({
|
||||
type: 'SYSTEM',
|
||||
severity: 'WARNING',
|
||||
message: `데이터 품질 문제: ${symbol} - ${validationResult.issues.join(', ')}`
|
||||
})
|
||||
|
||||
// 결측치 보정 시도
|
||||
if (validationResult.canFill) {
|
||||
rawData = await data.fillMissingData(rawData, 'FORWARD_FILL')
|
||||
} else {
|
||||
continue // 보정 불가능하면 skip
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 기업 행동 조정 (배당, 액면분할 등)
|
||||
const adjustedData = await data.adjustForCorporateActions(rawData, symbol)
|
||||
|
||||
// 4. 저장
|
||||
await data.storePriceData(adjustedData)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 성과 분석 워크플로우
|
||||
|
||||
### 7.1 일일 성과 계산
|
||||
|
||||
```typescript
|
||||
// 매일 장 마감 후 실행
|
||||
scheduler.scheduleDaily('MARKET_CLOSE', async () => {
|
||||
const containers = await mgmt.getActiveContainers()
|
||||
|
||||
for (const container of containers) {
|
||||
// 1. 현재 포지션 평가
|
||||
const positions = await mgmt.getPositions(container.id)
|
||||
const marketData = await data.getLatestPrices(positions.map(p => p.symbol))
|
||||
|
||||
// 2. 일일 수익률 계산
|
||||
const dailyReturn = await analytics.calculateDailyReturn(
|
||||
container.id,
|
||||
positions,
|
||||
marketData
|
||||
)
|
||||
|
||||
// 3. 누적 성과 업데이트
|
||||
await analytics.updatePerformance(container.id, {
|
||||
date: new Date(),
|
||||
return: dailyReturn,
|
||||
equity: positions.reduce((sum, p) => sum + p.marketValue, 0)
|
||||
})
|
||||
|
||||
// 4. 리스크 지표 재계산
|
||||
const riskMetrics = await analytics.calculateRiskMetrics(container.id)
|
||||
|
||||
// 5. 이상 탐지
|
||||
if (dailyReturn < -5) { // 일일 손실 5% 초과
|
||||
await monitor.sendAlert({
|
||||
type: 'PERFORMANCE',
|
||||
severity: 'WARNING',
|
||||
message: `일일 손실 초과: ${container.name} ${dailyReturn.toFixed(2)}%`
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 8. 관련 문서
|
||||
|
||||
- [시스템 개요](./01-overview.md)
|
||||
- [전체 아키텍처](./02-architecture.md)
|
||||
- [공통 데이터 모델](./03-data-models.md)
|
||||
- [구현 로드맵](./05-roadmap.md)
|
||||
Reference in New Issue
Block a user