์ ๋ฆฝ์ ํฌ์์๋ฅผ ์ํ ๊ฐ์ธํ๋ ETF/์ฃผ์/์ฝ์ธ ํฌํธํด๋ฆฌ์ค ๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ณ์ฐ๊ธฐ์ ๋๋ค. ๋งค์ ํฌ์ํ ์์ฐ๊ณผ ํ์ฌ ๋ณด์ ์ข ๋ชฉยท๋ชฉํ ๋น์จ์ ์ ๋ ฅํ๋ฉด ์ด๋ค ์ข ๋ชฉ์ ์ผ๋ง๋ ๋งค์ํด์ผ ํ๋์ง ์๋์ผ๋ก ๊ณ์ฐํด ์ค๋๋ค.
์ง์ ์์ฅ: ํ๊ตญ ETF/์ฃผ์ (Naver Finance) ยท ๋ฏธ๊ตญ ์ฃผ์/ETF (Yahoo Finance) ยท ์ํธํํ (Upbit)
-
๐ ๋ค์ค ์์ฅ ์ง์
- KR (ํ๊ตญ): Naver Finance, 6์๋ฆฌ ์์ซ์ ํฐ์ปค (์:
069500) - US (๋ฏธ๊ตญ): Yahoo Finance, 1~10์ ํฐ์ปค (์:
AAPL,ES=F,GC=F) - CRYPTO (์ฝ์ธ): Upbit KRW ๋ง์ผ, 2~10์ ๋๋ฌธ์ ํฐ์ปค (์:
BTC,ETH) - ํฐ์ปค ์ ๋ ฅ ์ ํ์ฌ๊ฐ ์๋ ์กฐํ (debounce 600ms)
- US ์์ฐ์ Yahoo Finance ์ค์๊ฐ ํ์จ(USDโKRW)๋ก ์๋ ํ์ฐ, 10๋ถ ์บ์
- US ํฐ์ปค์ Yahoo Finance ๊ณผ๊ฑฐ ์ข ๊ฐ๋ฅผ ์กฐํํด ๋์ ๋ชฉํ๋น์ค ๊ณ์ฐ์ ํ์ฉ
- KR (ํ๊ตญ): Naver Finance, 6์๋ฆฌ ์์ซ์ ํฐ์ปค (์:
-
๐งญ ๋์ ๋ชฉํ๋น์ค ํจ๋
- VT ๊ฐ์ Yahoo Finance ์ง์ ํฐ์ปค์ ๊ณผ๊ฑฐ ๊ฐ๊ฒฉ์ผ๋ก ์ํ์์ฐ/ํ๊ธ์ฑ ์์ฐ์ ์ด๋ก ์ ๋ฐฐ๋ถ ์ฐธ๊ณ ๊ฐ์ ๊ณ์ฐํฉ๋๋ค.
- ์ง์ ๋ฐฉ์: ๋ณ๋์ฑ ๋ชฉํํ, ํ๊ท -๋ถ์ฐยท๋จธํผ, VaRยทES ์ํ์์ฐ, CPPIยท์์คํ๋.
- ETF ํ๋ณ ์์ฐ ์ ํ(
VT,S&P500,Nasdaq100,KOSPI200,KRX300,ํ๊ธ์ฑ ์์ฐ,๊ธฐํ)๊ณผ ํ์๋น์ค์ ์กฐ์ ํ ์ ์์ต๋๋ค. - ๊ณ์ฐ๊ฐ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก๋ง ํ์๋๋ฉฐ, ์ฌ์ฉ์๊ฐ ๋ชฉํ ๋น์จ์ ์ ์ฉ์ ๋๋ฅด๊ธฐ ์ ์๋ ๊ธฐ์กด ๋ชฉํ ๋น์จ์ ๋ฎ์ด์ฐ์ง ์์ต๋๋ค.
-
๐ ์๋ยท๊ธ์ก ๊ธฐ์ค ๋งค์ ์ ํ
- ์์ฐ๋ณ๋ก ์๋ ๊ธฐ์ค(์ฃผ) ๋๋ ๊ธ์ก ๊ธฐ์ค(์) ๋งค์ ๋ฐฉ์์ ํ ๊ธ๋ก ์ ํ ๊ฐ๋ฅ
- ์๋ ๊ธฐ์ค: ์ ์ ์ฃผ ๋จ์๋ก ๊ณ์ฐ (ํ๊ตญ ETF ๊ธฐ๋ณธ๊ฐ)
- ๊ธ์ก ๊ธฐ์ค: ์์์ ์๋ ๊ณ์ฐ โ ์ฝ์ธยท์์์ ์ฃผ์์ ์ ํฉ (CRYPTO ๊ธฐ๋ณธ๊ฐ)
-
๐ 100% ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ์ฐ์ฐ (Privacy-First)
- ๋ชจ๋ ๊ณ์ฐ๊ณผ ๋ฐ์ดํฐ ๊ด๋ฆฌ๊ฐ ๋ธ๋ผ์ฐ์ ๋ด๋ถ์์ ์ํ๋ฉ๋๋ค.
- ์๋ฒ๋ ์ธ๋ถ ๊ธ์ต API์ CORS ํ๋ก์ ์ญํ ๋ง ๋ด๋นํ๋ฉฐ, ์์ฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ์ง ์์ต๋๋ค.
- ๋ค์์ ์ฌ์ฉ์๊ฐ ๋์์ ์ ์ํด๋ ์์ ํ ๋ ๋ฆฝ์ ์ผ๋ก ๋์ํฉ๋๋ค.
-
๐พ JSON ๋ฐ์ดํฐ Import / Export (v3)
- ํฌํธํด๋ฆฌ์ค ์ํ(์์ฅยทํตํยท๋งค์๋ฐฉ์ยท๋ณด์ ๋ ๋ฑ)๋ฅผ
.jsonํ์ผ๋ก ์ ์ฅํ๊ณ ์ธ์ ๋ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค. - ๋์ ๋ชฉํ๋น์ค ์ค์ , ์์ฐ ์ ํ, ํ์๋น์ค์ ํจ๊ป ์ ์ฅํฉ๋๋ค.
- v1/v2 ํ์ ์๋ ๋ง์ด๊ทธ๋ ์ด์ ์ง์.
- ํฌํธํด๋ฆฌ์ค ์ํ(์์ฅยทํตํยท๋งค์๋ฐฉ์ยท๋ณด์ ๋ ๋ฑ)๋ฅผ
-
๐ฏ ์ค๋งํธ ๋ฆฌ๋ฐธ๋ฐ์ฑ ์๊ณ ๋ฆฌ์ฆ
- ๋ชฉํ ์ด ์์ฐ(ํ์ฌ ์์ฐ + ์ด๋ฒ ๋ฌ ์์ฐ)์ ๊ธฐ์ค์ผ๋ก ๊ฐ ์ข ๋ชฉ์ ๋ชฉํ ๊ธ์ก์ ์ฐ์ถํฉ๋๋ค.
- ์์ฐ ์ด๊ณผ ์ ๋น๋ก ์ถ์ ๋ก์ง์ด ์๋์ผ๋ก ์ ์ฉ๋ฉ๋๋ค.
- ์ต์ข ๋น์จ์ ๋จ์ ์์ฐ์ ํ๊ธ์ผ๋ก ํฌํจํ ์ด์์ฐ ๊ธฐ์ค์ผ๋ก ํ์ํด ๋ชฉํ ๋น์จ๊ณผ ์ง์ ๋น๊ตํ ์ ์์ต๋๋ค.
- 0% ๋ชฉํ ๋น์จ ์ง์: ๋ ์ด์ ์ถ๊ฐ ๋งค์ํ์ง ์๊ณ ๋ณด์ ๋ง ์ ์งํ ์ข ๋ชฉ๋ ํฌํธํด๋ฆฌ์ค์ ๋จ๊ฒจ๋ ์ ์์ต๋๋ค.
-
๐ ์๊ฐํ ๋ฐ ์ง๊ด์ ์ธ UI/UX
- ๋งค์ ์ /ํ์ ๋น์จ์ ๋น๊ตํ ์ ์๋ ์ธํฐ๋ํฐ๋ธ ๋ฐ ์ฐจํธ ์ ๊ณต
- ๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ์์๋ ์๋ฒฝํ๊ฒ ์๋ํ๋ ๋ฐ์ํ(Responsive) ์น ๋์์ธ (Light Theme)
- HTML5 / CSS3 (CSS Variables, Flex/Grid ๊ธฐ๋ฐ ๋ฐ์ํ ๋ ์ด์์)
- Vanilla JavaScript (ES2022+) (ํ๋ ์์ํฌ ์์ด ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ๋์)
- Node.js ๋ด์ฅ test runner (๋ฆฌ๋ฐธ๋ฐ์ฑ/๋์ ๋ชฉํ๋น์ค ์์ ํจ์ ํ ์คํธ)
- Python 3.13 / FastAPI / Jinja2 (์ ์ ํ์ผ ๋ฐ CORS ํ๋ก์)
- httpx (๋น๋๊ธฐ HTTP ํด๋ผ์ด์ธํธ)
- uv (ํจํค์ง ๊ด๋ฆฌ ๋ฐ ๊ฐ์ํ๊ฒฝ)
- ruff (Lint & Format) / pyright (ํ์ ์ฒดํฌ)
- Docker (๋ฉํฐ ์คํ ์ด์ง ์ปจํ ์ด๋)
- Fly.io (ํด๋ผ์ฐ๋ ๋ฐฐํฌ, Auto-stop/start ์ ์ฉ์ผ๋ก ๋ฆฌ์์ค ์ต์ ํ)
# ์ ์ฅ์ ํด๋ก
git clone <repository-url>
cd ETF_Rebalancer
# ๊ฐ๋ฐ ์๋ฒ ์์ (hot-reload)
make dev๋ธ๋ผ์ฐ์ ์์ http://localhost:8080์ผ๋ก ์ ์ํฉ๋๋ค.
๊ธฐํ make ๋ช ๋ น:
make test # Python API ํ
์คํธ + JavaScript ์๊ณ ๋ฆฌ์ฆ ํ
์คํธ ์คํ
make lint # ruff check + pyright + app.js ๊ตฌ๋ฌธ ๊ฒ์ฌ
make build # Docker ์ด๋ฏธ์ง ๋น๋
make clean # ์ปจํ
์ด๋/์ด๋ฏธ์ง ์ ๋ฆฌ๊ณต์ ๊ฐ๋ฐยท๊ฒ์ฆ ๊ฒฝ๋ก๋ Docker ๊ธฐ๋ฐ make ๋ช
๋ น์
๋๋ค. ์๋ ๋ฐฉ์์ ๋น ๋ฅธ ๋ก์ปฌ ํ์ธ์ฉ์ด๋ฉฐ, PR/๋ฐฐํฌ ์ ์๋ ๋ฐ๋์ make test์ make lint๋ฅผ ์คํํฉ๋๋ค.
git clone <repository-url>
cd ETF_Rebalancer
# ์์กด์ฑ ์ค์น (uv๊ฐ Python 3.13 + ๊ฐ์ํ๊ฒฝ์ ์๋ ์์ฑ)
uv sync
# FastAPI ์๋ฒ ์คํ
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000๋ธ๋ผ์ฐ์ ์์ http://localhost:8000์ผ๋ก ์ ์ํฉ๋๋ค.
# ์ด๋ฏธ์ง ๋น๋
docker build -t etf-rebalancer .
# ์ปจํ
์ด๋ ์คํ (ํฌํธ 8080 ๋งคํ)
docker run -p 8080:8080 etf-rebalancer์ด ํ๋ก์ ํธ๋ Fly.io ๋ฐฐํฌ์ ์ต์ ํ๋ fly.toml์ ํฌํจํ๊ณ ์์ต๋๋ค. (๋์ฟ nrt ๋ฆฌ์ ๊ธฐ์ค)
fly auth login
fly deployETF_Rebalancer/
โโโ app/
โ โโโ main.py # FastAPI ์ฑ
โ โ # GET /api/price/{ticker}?market=KR|US|CRYPTO
โ โ # GET /api/history/{ticker}?market=US&range=1y&interval=1d
โ โ # GET /api/rate/USDKRW
โ โโโ static/
โ โ โโโ app.js # ๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ณ์ฐ + UI ๋ก์ง (์ ๋ถ ํด๋ผ์ด์ธํธ ์ฌ์ด๋)
โ โ โโโ style.css # UI ์คํ์ผ๋ง (Light Theme)
โ โโโ templates/
โ โโโ index.html # SPA ์ง์
์
โโโ tests/
โ โโโ test_main.py # pytest API ํ
์คํธ
โ โโโ js/ # Node.js ๊ธฐ๋ฐ ํ๋ก ํธ์๋ ์๊ณ ๋ฆฌ์ฆ ํ
์คํธ
โโโ .github/
โ โโโ workflows/
โ โโโ fly-deploy.yml # GitHub Actions CI/CD
โโโ Dockerfile # ๋ฉํฐ ์คํ
์ด์ง ๋น๋ (production, dev)
โโโ docker-compose.yml # ๊ฐ๋ฐ/ํ
์คํธ ์ปจํ
์ด๋ ์ค์
โโโ Makefile # ๊ฐ๋ฐ ์์
๋จ์ถ ๋ช
๋ น
โโโ fly.toml # Fly.io ๋ฐฐํฌ ์ค์ ํ์ผ
โโโ pyproject.toml # ํ๋ก์ ํธ ์ค์ ๋ฐ ์์กด์ฑ
โโโ uv.lock # ์์กด์ฑ ์ ๊ธ ํ์ผ
- ์์ฐ ์ค์ : ํ๋ฉด ์๋จ์ ์ด๋ฒ ๋ฌ ํฌ์ ์์ฐ(โฉ)์ ์ ๋ ฅํฉ๋๋ค.
- ํฌํธํด๋ฆฌ์ค ๊ตฌ์ฑ:
- [ETF ์ถ๊ฐ] ๋ฒํผ์ ๋๋ฌ ์ข ๋ชฉ์ ์ถ๊ฐํฉ๋๋ค.
- ์์ฅ ์ ํ โ ์ข ๋ชฉ์ฝ๋ ์ ๋ ฅ ์ ํ์ฌ๊ฐ ์๋ ์กฐํ
- ์ข ๋ชฉ๋ช , ๋ณด์ ์๋, ๋ชฉํ ๋น์จ(%) ์ ๋ ฅ (ํฉ๊ณ 100%)
- ๋ณด์ ์๋ ์ค๋ฅธ์ชฝ์
์ฃผ/์๋ฒํผ์ผ๋ก ๋งค์ ๋ฐฉ์ ์ ํ:์ฃผโ ์๋ ๊ธฐ์ค (์ ์ ์ฃผ ๋จ์, ํ๊ตญ ETF ๊ธฐ๋ณธ)์โ ๊ธ์ก ๊ธฐ์ค (์์์ ์๋, ์ฝ์ธยท์์์ ์ฃผ์ ๊ธฐ๋ณธ)
- ๋์ ๋ชฉํ๋น์ค ํ์ธ(์ ํ): [๋์ ๋ชฉํ๋น์ค] ํจ๋์์ ๊ณ์ฐ ๋ฐฉ์์ ์ ํํ๊ณ , ETF ์ ํ๋ณ ๋ชฉํ๋น์ค ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ํ์ธํฉ๋๋ค.
- ๋ชฉํ ๋น์จ ์ ์ฉ(์ ํ): ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐ์ด ์ ์ ํ ๋๋ง [๋ชฉํ ๋น์จ์ ์ ์ฉ]์ ๋๋ฌ ETF ๋ชฉ๋ก์ ๋ชฉํ ๋น์จ ์ ๋ ฅ๊ฐ์ ๋ฐ์ํฉ๋๋ค.
- ๊ณ์ฐํ๊ธฐ: [๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ณ์ฐํ๊ธฐ] ๋ฒํผ(๋๋ Enter)์ ๋๋ฆ ๋๋ค.
- ๊ฒฐ๊ณผ ํ์ธ: ํ์ ์ฐจํธ์์ ๋งค์ ์๋ยท๊ธ์ก ๋ฐ ๋จ์ ์์ฐ์ ํ๊ธ์ผ๋ก ํฌํจํ ์ต์ข ๋น์จ์ ํ์ธํฉ๋๋ค.
- ์ ์ฅ: [๋ด๋ณด๋ด๊ธฐ]๋ก ํ์ฌ ์ํ๋ฅผ
.json์ผ๋ก ์ ์ฅํ๊ณ , ๋ค์ ๋ฌ์ [๋ถ๋ฌ์ค๊ธฐ]๋ก ์ด์ด์ ์ฌ์ฉํฉ๋๋ค.
๋ณธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ณ์ฐ ๊ฒฐ๊ณผ์ ๋์ ๋ชฉํ๋น์ค์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋ฐ์ดํฐ์ ๊ณผ๊ฑฐ ๊ฐ๊ฒฉ์ ๋ฐํ์ผ๋ก ํ ์ํ์ ์ฐ์ถ ๊ฒฐ๊ณผ์ผ ๋ฟ์ด๋ฉฐ, ํฌ์ ๊ถ์ ๋ ์ฌ๋ฌด์ ์กฐ์ธ์ ๋ชฉ์ ์ผ๋ก ํ์ง ์์ต๋๋ค. ๊ณผ๊ฑฐ ๋ณ๋์ฑ, VaR, ES, CPPI ์ ๋ ฅ๊ฐ์ ๋ฏธ๋ ์์ต์ด๋ ์์ค์ ๋ณด์ฅํ์ง ์์ต๋๋ค. ์ค์ ๋งค๋งค ์์๋ ์์ฅ ์ํฉ, ํธ๊ฐ ๋จ์, ์์๋ฃ, ์ธ๊ธ, ํ์จ, ๊ฐ์ธ์ ์ฌ๋ฌด ์ํฉ ๋ฑ์ ์ํด ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง ์ ์์ผ๋ฏ๋ก ๋ฐ๋์ ํฌ์ ํ๋จ์ ์ฐธ๊ณ ์ฉ์ผ๋ก๋ง ์ฌ์ฉํ์๊ธฐ ๋ฐ๋๋๋ค.