-
Notifications
You must be signed in to change notification settings - Fork 0
App Architecture
JJong-03 edited this page Mar 13, 2026
·
6 revisions
Client → Web (Flask) → engine.py 직접 호출 → 동기 응답
Client → Web (Flask) → JobLauncher → K8s Job (Worker) → MySQL → Web (status polling)
전환의 핵심: 엔진 코드를 한 줄도 수정하지 않고 실행 모델만 교체했다.
| Responsibility | Web (Flask Deployment) | Worker (K8s Job) |
|---|---|---|
| Request validation, input sanitization | O | — |
run_id issuance (UUID4) |
O | — |
PENDING insert to MySQL |
O | — |
K8s Job 생성 (BatchV1Api) |
O | — |
| Backtest engine 실행 | — | O |
| Adapter 파생 데이터 생성 (metrics, charts) | — | O |
| Result persistence → MySQL | — | O |
RUNNING / SUCCEEDED / FAILED 상태 전이 |
— | O |
Status/result 조회 (/status/<run_id>) |
O | — |
| 성공 Job 삭제 | O | — |
| Response rendering (JSON/HTML) | O | — |
Invariants:
- Web은 stateless — 로컬 파일 I/O 없음, stdout/stderr 로깅만 사용
- Worker는 ephemeral — 단일 백테스트 실행 후 종료. 재시도는 K8s
backoffLimit로 관리 - MySQL이 결과의 source of truth — Web과 Worker 모두 MySQL을 통해서만 결과를 교환
상태 전이 규칙 및 Job 생명주기 → Execution Lifecycle
┌─ LocalSubprocessLauncher (dev/test)
JobLauncher (ABC) ───┤
└─ KubernetesJobLauncher (production)
환경변수 JOB_LAUNCHER_MODE로 전환:
-
local→ subprocess 기반 mock (Docker/K8s 없이 로컬 테스트) -
kubernetes→kubernetes.client.BatchV1Api호출
| 이유 | 설명 |
|---|---|
| Testability | 단위 테스트에서 K8s 의존성 없이 백테스트 파이프라인 전체를 검증 가능 |
| Local/Cluster parity | 개발자가 docker compose up으로 로컬에서 동일한 흐름을 실행 |
| 관심사 분리 | Web은 "어떻게 실행하는가"를 몰라도 됨. launcher 교체만으로 실행 환경 전환 |
stock_backtest/
├── app.py # Web 진입점 (Controller)
├── worker.py # K8s Job Worker 진입점
├── extensions.py # SQLAlchemy instance (순환 import 방지)
├── models.py # ORM model (Strategy preset, BacktestResult)
├── adapters/
│ └── adapter.py # Post-processing: 파생 metrics, charts 생성
├── backtest/
│ ├── engine.py # Core engine (IMMUTABLE — 수정 금지)
│ └── metrics.py # PerformanceMetrics
├── rules/ # Trading rule library
│ ├── base_rule.py # BaseRule, Signal, RuleMetadata
│ └── technical_rules.py # RSI, MACD, RSI+MACD, etc.
└── scripts/
├── config.py # 환경변수 기반 설정
└── logger_config.py # Structured logging (stdout/stderr)
Adapter는 엔진 출력으로부터 파생 데이터를 생성하는 계층이다. 엔진 코드를 수정하지 않고 UI에 필요한 추가 지표와 차트를 제공한다 (Rule 1 준수).
- equity_curve → drawdown_curve 유도
- equity_curve + trades → portfolio_curve 유도
- trades → win_rate, profit_factor, exposure_pct 계산
- Matplotlib Agg → Base64 PNG 차트 생성
- Timestamp ISO8601 포맷팅
- Signal 생성 로직 수정
- Trade 실행 규칙 변경
- 엔진 내부 공식(Sharpe, returns) 재계산
저장 vs 파생 경계 → Reproducibility
혼동 방지를 위한 용어 정의:
| 개념 | 위치 | 역할 |
|---|---|---|
| Rule | rules/ |
거래 로직 구현 (RSI, MACD 등). Immutable core의 일부. |
| Strategy Preset |
models.py (ORM) |
사용자가 저장한 UI 파라미터 조합. 로직을 정의하지 않는다. |
-
rule_type(예:"RSI") +params→ 실행 로직을 결정 -
rule_id(예:"RSI_14_30_70") → 추적/로깅용 헬퍼 slug (실행에 영향 없음)
용어 상세 정의 → Glossary
모듈 간 의존은 단방향이다. 하위 계층이 상위 계층을 import하지 않는다.
graph TD
APP["app.py<br/>(Web Controller)"]
WORKER["worker.py<br/>(Job Entrypoint)"]
MODELS["models.py<br/>(ORM)"]
EXT["extensions.py<br/>(SQLAlchemy instance)"]
ADAPTER["adapters/adapter.py<br/>(Post-processing)"]
ENGINE["backtest/engine.py<br/>(IMMUTABLE)"]
RULES["rules/<br/>(Trading Rules)"]
SCRIPTS["scripts/config.py<br/>scripts/logger_config.py"]
APP --> MODELS
APP --> ADAPTER
APP --> EXT
APP --> SCRIPTS
WORKER --> ENGINE
WORKER --> ADAPTER
WORKER --> RULES
WORKER --> SCRIPTS
MODELS --> EXT
ADAPTER --> ENGINE
classDef immutable fill:#f5f5f5,stroke:#999,stroke-width:2px,stroke-dasharray: 5 5
class ENGINE immutable
규칙:
-
app.py→models,adapters,extensions의존 허용 -
worker.py→engine,adapters,rules의존 허용 -
models.py→extensions.py만 의존 (역방향 금지) -
extensions.py는 어떤 모듈도 import하지 않음 — 순환 import 방지를 위한 독립 모듈 -
backtest/engine.py는 수정 금지 (Rule 1, 점선 표시) -
adapters/→engine출력만 참조 (엔진 내부 로직 접근 금지)
- Execution Lifecycle — 상태 전이 및 Job 생명주기
- Reproducibility — 저장 vs 파생 경계
- Infra Architecture — K8s 계층 구조
- Glossary — Rule, Strategy Preset, Adapter 등 용어 정의
- Testing Strategy — Adapter 테스트 범위
| ← Previous | Archive | Next → |
|---|---|---|
| Infra Architecture | Home | Execution-Lifecycle |