ํฐ์คํ ๋ฆฌ ๋ทฐ
๐ ํํธ ํฌ์ ๊ณต๋ถ 12๋จ๊ณ
— “์ํฌํฌ์๋(Walk-Forward) ๊ฒ์ฆ: ์ ๋ต์ด ‘์ง์ง’์ธ์ง ํ์ธํ๋ ์ค์ ๋ฐฉ๋ฒ”
11๋จ๊ณ์์ ์ฐ๋ฆฌ๋ ๊ณผ์ต์ ํ(Overfitting)๋ฅผ ๋ฐฐ์ ์ฃ .
“๋ฐฑํ
์คํธ๊ฐ ์์ ์ ๋ต์ด ์ค์ ์์ ๊นจ์ง๋ ์ด์ ”๋์.
๊ทธ๋ผ ์ด์ ๋จ๋ ์ง๋ฌธ์ ๋ฑ ํ๋์ ๋๋ค.
“์ด ์ ๋ต, ๊ณผ๊ฑฐ ๋ง๊ณ ๋ฏธ๋์์๋ ๋ฒํธ๊น?”
๊ทธ๊ฑธ ํ์ธํ๋ ๊ฐ์ฅ ํ์ค์ ์ธ ๋ฐฉ๋ฒ์ด
๋ฐ๋ก ์ํฌํฌ์๋(Walk-Forward) ๊ฒ์ฆ์
๋๋ค.
1๏ธโฃ ์ํฌํฌ์๋ ๊ฒ์ฆ์ ์ ํ์ํ๊ฐ?
๋ณดํต ์ด๋ณด์๋ ์ด๋ ๊ฒ ๊ฒ์ฆํฉ๋๋ค.
- 2010~2024 ์ ์ฒด ๊ธฐ๊ฐ ๋ฐฑํ ์คํธ
- ์์ต๋ฅ ๊ด์ฐฎ๋ค → ๋
ํ์ง๋ง ์์ฅ์
2010๋
๊ณผ 2024๋
์ด ์์ ํ ๋ค๋ฆ
๋๋ค.
- ๊ธ๋ฆฌ
- ์ ๋์ฑ
- ์ฐ์ ๊ตฌ์กฐ
- ํ ๋ง ์ํ
- ํฌ์์ ์ฑํฅ
๋ค ๋ฐ๋์ด์.
๊ทธ๋์ ํํธ๋ ์ด๋ ๊ฒ ์๊ฐํฉ๋๋ค.
“์ ๋ต์
ํ ๋ฒ ์ ๋๋ ๊ฒ ์๋๋ผ
์๊ฐ์ด ํ๋ฌ๋ ๊ณ์ ์ด์๋จ์์ผ ํ๋ค.”
2๏ธโฃ ์ํฌํฌ์๋์ ํต์ฌ ์์ด๋์ด
์ํฌํฌ์๋๋ ๊ฐ๋จํ ๋งํ๋ฉด ์ด๊ฒ๋๋ค.
“๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ก ์ ๋ต์ ๋ง๋ค๊ณ →
๊ทธ ๋ค์ ๊ตฌ๊ฐ์์ ๊ฒ์ฆํ๊ณ →
๋ค์ ์์ผ๋ก ์ด๋ํ๋ฉฐ ๋ฐ๋ณตํ๋ค.”
์ฆ, ์๊ฐ์ ์์ผ๋ก ๊ฑธ์ด๊ฐ๋ฉด์ ํ ์คํธํ๋ ๋ฐฉ์์ ๋๋ค.
3๏ธโฃ ์ํฌํฌ์๋ ๊ตฌ์กฐ ์์ (๊ฐ์ฅ ๋ง์ด ์ฐ๋ ํํ)
์๋ฅผ ๋ค์ด 10๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด:
- ํ์ต(Train): 3๋
- ๊ฒ์ฆ(Test): 1๋
- ๊ทธ๋ฆฌ๊ณ 1๋ ์ฉ ์์ผ๋ก ์ด๋
์ด๋ ๊ฒ ๋ฉ๋๋ค.
๋ฐ๋ณตํ์ต ๊ตฌ๊ฐ๊ฒ์ฆ ๊ตฌ๊ฐ
| 1 | 2014~2016 | 2017 |
| 2 | 2015~2017 | 2018 |
| 3 | 2016~2018 | 2019 |
| … | … | … |
์ด ๊ณผ์ ์ ๊ฑฐ์น๋ฉด
์ ๋ต์ด “ํน์ ๊ตฌ๊ฐ์์๋ง” ์ ๋๋์ง,
์๋๋ฉด ์ฌ๋ฌ ์๊ธฐ์๋ ์ด์๋จ๋์ง๊ฐ ๋๋ฌ๋ฉ๋๋ค.
4๏ธโฃ ์ํฌํฌ์๋๊ฐ ์๋ ค์ฃผ๋ ์ง์ง ๊ฒ๋ค
์ํฌํฌ์๋๋ ์์ต๋ฅ ์ ๋ณด์ฌ์ฃผ๋ ๊ฒ ์๋๋๋ค.
์ํฌํฌ์๋๊ฐ ์ง์ง๋ก ์๋ ค์ฃผ๋ ๊ฑด ์ด๊ฒ๋๋ค.
- ์ ๋ต์ด ์ ๋๋ ์๊ธฐ vs ๊นจ์ง๋ ์๊ธฐ
- MDD๊ฐ ํฌ๊ฒ ํฐ์ง๋ ๊ตฌ๊ฐ
- ๋ฆฌ๋ฐธ๋ฐ์ฑ ์ฃผ๊ธฐ ๋ณ๊ฒฝ์ด ํ์ํ ์์
- ์ ๋ต์ ์ฝ์ ์ด “์์ฅ ๊ตญ๋ฉด”์ธ์ง, “๊ตฌ์กฐ์ ๋ฌธ์ ”์ธ์ง
์ฆ, ์ ๋ต์ ์ฑ๊ฒฉ๊ณผ ์์กด์ฑ์ ๋ณด์ฌ์ค๋๋ค.
5๏ธโฃ ์ํฌํฌ์๋์์ ์ ๋ต์ด ‘์ข๋ค’๋ ๊ธฐ์ค
์ฌ๊ธฐ์๋ “์์ต๋ฅ ๋์ผ๋ฉด ์ข๋ค”๊ฐ ์๋๋๋ค.
์ข์ ์ํฌํฌ์๋ ๊ฒฐ๊ณผ๋ ์ด๋ฐ ํน์ง์ด ์์ต๋๋ค.
โ
์ฌ๋ฌ ๊ตฌ๊ฐ์์ ์ผ๊ด๋๊ฒ ํ๋ฌ์ค
โ
ํ๋ ๊ตฌ๊ฐ์์๋ง ํญ๋ฐํ์ง ์์
โ
MDD๊ฐ ํน์ ๊ตฌ๊ฐ์์๋ง ๊ณผ๋ํ๊ฒ ์ปค์ง์ง ์์
โ
์คํ/์๋ฅดํฐ๋
ธ๊ฐ ๊ตฌ๊ฐ๋ณ๋ก ํฌ๊ฒ ํ๋ค๋ฆฌ์ง ์์
ํ๋ง๋๋ก:
“์ด์ด ์๋๋ผ, ๊ตฌ์กฐ๋ก ๋์ ๋ฒ ๋๋”
์ด๊ฒ ๋์ต๋๋ค.
6๏ธโฃ (์คํ ๊ฐ๋ฅํ ์ฝ๋) ์ํฌํฌ์๋ ๋ฐฑํ ์คํธ ๋ผ๋
์๋ ์ฝ๋๋ ์ค์ ๋ก ์คํ๋๋ ํํ๋ก ์ํฌํฌ์๋ ๊ตฌ์กฐ๋ฅผ ๋ณด์ฌ์ค๋๋ค.
(์ฌ๊ธฐ์๋ ์ํ ๊ฐ๊ฒฉ ๋ฐ์ดํฐ + ๋ชจ๋ฉํ
์์ N ์ ๋ต์ ๊ทธ๋๋ก ์ฐ๊ฒ ์ต๋๋ค.)
import numpy as np
import pandas as pd
def make_sample_prices(n_assets=8, n_days=1500, seed=42):
rng = np.random.default_rng(seed)
rets = rng.normal(loc=0.00025, scale=0.012, size=(n_days, n_assets))
prices = 100 * np.exp(np.cumsum(rets, axis=0))
dates = pd.date_range("2018-01-01", periods=n_days, freq="B")
cols = [f"A{i+1}" for i in range(n_assets)]
return pd.DataFrame(prices, index=dates, columns=cols)
def backtest_mom_topn(prices, lookback=60, top_n=3, fee_bps=5):
returns = prices.pct_change().fillna(0.0)
month_ends = prices.resample("M").last().index
month_ends = [d for d in month_ends if d in prices.index]
weights = pd.Series(0.0, index=prices.columns)
equity = 1.0
curve = []
for dt in prices.index:
if dt in month_ends and prices.index.get_loc(dt) >= lookback:
mom = (prices.loc[dt] / prices.shift(lookback).loc[dt] - 1.0).sort_values(ascending=False)
pick = mom.index[:top_n]
target = pd.Series(0.0, index=prices.columns)
target.loc[pick] = 1.0 / top_n
turnover = (target - weights).abs().sum()
equity *= (1 - turnover * (fee_bps / 10_000))
weights = target
equity *= (1 + float((weights * returns.loc[dt]).sum()))
curve.append(equity)
return pd.Series(curve, index=prices.index, name="equity")
def stats(equity):
rets = equity.pct_change().dropna()
cagr = (equity.iloc[-1] / equity.iloc[0]) ** (252 / len(equity)) - 1
dd = equity / equity.cummax() - 1
mdd = dd.min()
sharpe = (rets.mean() * 252) / (rets.std() * np.sqrt(252)) if rets.std() > 0 else np.nan
return {"CAGR": float(cagr), "MDD": float(mdd), "Sharpe": float(sharpe)}
def walk_forward(prices, train_years=2, test_years=1, step_years=1, **bt_kwargs):
results = []
start = prices.index.min()
end = prices.index.max()
# ์ฐ ๋จ์ ์๋์ฐ ์์ฑ(๋๋ต 252์์
์ผ * years๋ก ๋จ์ํ)
train_len = int(252 * train_years)
test_len = int(252 * test_years)
step_len = int(252 * step_years)
idx = 0
while idx + train_len + test_len < len(prices):
train_slice = prices.iloc[idx: idx + train_len]
test_slice = prices.iloc[idx + train_len: idx + train_len + test_len]
# ์ฌ๊ธฐ์๋ "ํ์ต" ๊ตฌ๊ฐ์์ ํ๋ผ๋ฏธํฐ๋ฅผ ํ๋ํ๋ค๊ณ ๊ฐ์ ํ ์ ์์ง๋ง
# 12๋จ๊ณ์์๋ ์ฐ์ ๊ณ ์ ํ๋ผ๋ฏธํฐ๋ก '์์กด์ฑ'๋ง ๋ณธ๋ค.
eq_test = backtest_mom_topn(test_slice, **bt_kwargs)
s = stats(eq_test)
results.append({
"train_start": train_slice.index[0],
"train_end": train_slice.index[-1],
"test_start": test_slice.index[0],
"test_end": test_slice.index[-1],
**s
})
idx += step_len
return pd.DataFrame(results)
if __name__ == "__main__":
prices = make_sample_prices()
wf = walk_forward(prices, train_years=2, test_years=1, step_years=1, lookback=60, top_n=3, fee_bps=5)
print(wf.head())
print("\nSummary:")
print(wf[["CAGR","MDD","Sharpe"]].describe())
โ
์ด ์ฝ๋๋ “์ํฌํฌ์๋ ๊ฒ์ฆ ๊ฒฐ๊ณผํ”๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
๊ตฌ๊ฐ๋ณ CAGR/MDD/Sharpe๊ฐ ์ผ๋ง๋ ์ผ๊ด์ ์ธ์ง ํ์ธํ ์ ์์ต๋๋ค.
7๏ธโฃ ์ํฌํฌ์๋ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๋ ๋ฒ (์ง์ง ํฌ์ธํธ)
์ํฌํฌ์๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด
๊ตฌ๊ฐ๋ง๋ค ์ฑ๊ณผ๊ฐ ๋ค์ญ๋ ์ญํฉ๋๋ค.
๊ทธ๋ ์ด๋ณด์๋ ์ด๋ ๊ฒ ์๊ฐํฉ๋๋ค.
“์ด? ๊นจ์ง๋๋ฐ์? ์ด ์ ๋ต ๋ณ๋ก ๊ฐ?”
์๋์.
์ ๋ต์ ํญ์ ๊นจ์ง๋ ๊ตฌ๊ฐ์ด ์์ต๋๋ค.
์ค์ํ ๊ฑด ์ด๊ฒ๋๋ค.
- ๊นจ์ง๋ ๊ตฌ๊ฐ์ด ์งง๊ณ , ํ๋ณต์ด ๋น ๋ฅธ๊ฐ?
- ํน์ ๊ตญ๋ฉด(์: ํ๋ฝ์ฅ)์๋ง ๊นจ์ง๋๊ฐ?
- MDD๊ฐ ์ด๋ค ๊ตฌ๊ฐ์์๋ง ๋น์ ์์ ์ผ๋ก ์ปค์ง๋๊ฐ?
์ด๊ฑธ ๋ณด๋ฉด
์ ๋ต์ ์ฑ๊ฒฉ์ด ๋ณด์
๋๋ค.
8๏ธโฃ ์์ฃผ ์์ ์ค์ต (ํต์ฌ)
โ๏ธ ๊ณผ์ 12
๋น์ ์ ์ ๋ต(ํน์ ๋ชจ๋ฉํ
์์ N ์ ๋ต)์ ๊ธฐ์ค์ผ๋ก
์ํฌํฌ์๋ ๊ฒ์ฆ์ ํ๋ค๊ณ ๊ฐ์ ํ๊ณ
์๋ ์ง๋ฌธ์ ๋ตํด๋ณด์ธ์.
- ๊ฐ์ฅ ์ฑ๊ณผ๊ฐ ์ข์๋ ๊ตฌ๊ฐ์ ์ธ์ ์ธ๊ฐ?
- ๊ฐ์ฅ ํฌ๊ฒ ๊นจ์ง ๊ตฌ๊ฐ์ ์ธ์ ์ธ๊ฐ?
- ๊นจ์ง ๊ตฌ๊ฐ์ ์ด๋ค ์์ฅ ํ๊ฒฝ(ํ๋ฝ์ฅ/ํก๋ณด์ฅ)์ด์์๊น?
- ๊ทธ ๊ตฌ๊ฐ์์ ๋ด๊ฐ ํ ์ ์๋ ๋ฆฌ์คํฌ ์ฅ์น๋ ๋ฌด์์ธ๊ฐ?
- ํ๊ธ ๋น์ค ํ๋?
- ์์ ํธ๋ฆฌ๊ฑฐ?
- ์ ๋ณ๋์ฑ/์ฑ๊ถ ์๊ธฐ?
์ฌ๊ธฐ์๋ถํฐ ํํธ๋
“์ ๋ต ๊ฐ๋ฐ”์ด ์๋๋ผ
์ด์ฉ ์ค๊ณ๋ก ๋์ด๊ฐ๋๋ค.
๐ ์ ๋ฆฌ
- ์ํฌํฌ์๋๋ ์๊ฐ์ด ๋ฐ๋์ด๋ ์ ๋ต์ด ์ด์๋จ๋์ง ๋ณธ๋ค
- ํ ๋ฒ ์ ๋๋ ์ ๋ต๋ณด๋ค, ์ฌ๋ฌ ๊ตฌ๊ฐ์์ ๋ฒํฐ๋ ์ ๋ต์ด ๊ฐํ๋ค
- ๊ฒฐ๊ณผํ๋ “์์ต๋ฅ ”๋ณด๋ค “์ผ๊ด์ฑ/๊นจ์ง๋ ๊ตฌ๊ฐ/ํ๋ณต๋ ฅ”์ ์ฝ๋ ๋๊ตฌ๋ค
- ์ํฌํฌ์๋๋ ๊ณผ์ต์ ํ๋ฅผ ์ก๋ ๊ฐ์ฅ ํ์ค์ ์ธ ๊ฒ์ฆ์ด๋ค
์ํฌํฌ์๋,ํํธ๊ฒ์ฆ,๊ณผ์ต์ ํ๋ฐฉ์ง,๋ฐฑํ ์คํธ,์ ๋ต๊ฒ์ฆ,๋ฆฌ๋ฐธ๋ฐ์ฑ,์คํ์ง์,MDD,์์คํ ํฌ์,ํํธ๊ณต๋ถ
'์ฃผ์' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ๐ ํํธ ํฌ์ ๊ณต๋ถ 14๋จ๊ณ (0) | 2026.03.02 |
|---|---|
| ๐ ํํธ ํฌ์ ๊ณต๋ถ 13๋จ๊ณ (0) | 2026.02.27 |
| ๐ ํํธ ํฌ์ ๊ณต๋ถ 11๋จ๊ณ (0) | 2026.02.24 |
| ๐ ํํธ ํฌ์ ๊ณต๋ถ 10๋จ๊ณ (0) | 2026.02.13 |
| ๐ ํํธ ํฌ์ ๊ณต๋ถ 9๋จ๊ณ (1) | 2026.02.11 |
- Total
- Today
- Yesterday
- JWT
- Prisma
- SEO์ต์ ํ
- rag
- REACT
- ์ฃผ๋์ด๊ฐ๋ฐ์
- LangChain
- PostgreSQL
- ๋ฐฑ์๋๊ฐ๋ฐ
- seo ์ต์ ํ 10๊ฐ
- fastapi
- nodejs
- llm
- DevOps
- node.js
- ์ฟ ๋ฒ๋คํฐ์ค
- JAX
- kotlin
- Next.js
- Express
- Python
- NestJS
- ์น๊ฐ๋ฐ
- SpringBoot
- flax
- nextJS
- ์์ฑํAI
- ๊ฐ๋ฐ๋ธ๋ก๊ทธ
- CI/CD
- openai
| ์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |

