ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

๐Ÿ“˜ ํ€€ํŠธ ํˆฌ์ž ๊ณต๋ถ€ 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 ์ „๋žต)์„ ๊ธฐ์ค€์œผ๋กœ
์›Œํฌํฌ์›Œ๋“œ ๊ฒ€์ฆ์„ ํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ 
์•„๋ž˜ ์งˆ๋ฌธ์— ๋‹ตํ•ด๋ณด์„ธ์š”.

  1. ๊ฐ€์žฅ ์„ฑ๊ณผ๊ฐ€ ์ข‹์•˜๋˜ ๊ตฌ๊ฐ„์€ ์–ธ์ œ์ธ๊ฐ€?
  2. ๊ฐ€์žฅ ํฌ๊ฒŒ ๊นจ์ง„ ๊ตฌ๊ฐ„์€ ์–ธ์ œ์ธ๊ฐ€?
  3. ๊นจ์ง„ ๊ตฌ๊ฐ„์€ ์–ด๋–ค ์‹œ์žฅ ํ™˜๊ฒฝ(ํ•˜๋ฝ์žฅ/ํšก๋ณด์žฅ)์ด์—ˆ์„๊นŒ?
  4. ๊ทธ ๊ตฌ๊ฐ„์—์„œ ๋‚ด๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์Šคํฌ ์žฅ์น˜๋Š” ๋ฌด์—‡์ธ๊ฐ€?
    • ํ˜„๊ธˆ ๋น„์ค‘ ํ™•๋Œ€?
    • ์†์ ˆ ํŠธ๋ฆฌ๊ฑฐ?
    • ์ €๋ณ€๋™์„ฑ/์ฑ„๊ถŒ ์„ž๊ธฐ?

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ํ€€ํŠธ๋Š”
“์ „๋žต ๊ฐœ๋ฐœ”์ด ์•„๋‹ˆ๋ผ
์šด์šฉ ์„ค๊ณ„๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.


๐Ÿ“Œ ์ •๋ฆฌ

  • ์›Œํฌํฌ์›Œ๋“œ๋Š” ์‹œ๊ฐ„์ด ๋ฐ”๋€Œ์–ด๋„ ์ „๋žต์ด ์‚ด์•„๋‚จ๋Š”์ง€ ๋ณธ๋‹ค
  • ํ•œ ๋ฒˆ ์ž˜ ๋˜๋Š” ์ „๋žต๋ณด๋‹ค, ์—ฌ๋Ÿฌ ๊ตฌ๊ฐ„์—์„œ ๋ฒ„ํ‹ฐ๋Š” ์ „๋žต์ด ๊ฐ•ํ•˜๋‹ค
  • ๊ฒฐ๊ณผํ‘œ๋Š” “์ˆ˜์ต๋ฅ ”๋ณด๋‹ค “์ผ๊ด€์„ฑ/๊นจ์ง€๋Š” ๊ตฌ๊ฐ„/ํšŒ๋ณต๋ ฅ”์„ ์ฝ๋Š” ๋„๊ตฌ๋‹ค
  • ์›Œํฌํฌ์›Œ๋“œ๋Š” ๊ณผ์ตœ์ ํ™”๋ฅผ ์žก๋Š” ๊ฐ€์žฅ ํ˜„์‹ค์ ์ธ ๊ฒ€์ฆ์ด๋‹ค

 

์›Œํฌํฌ์›Œ๋“œ,ํ€€ํŠธ๊ฒ€์ฆ,๊ณผ์ตœ์ ํ™”๋ฐฉ์ง€,๋ฐฑํ…Œ์ŠคํŠธ,์ „๋žต๊ฒ€์ฆ,๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ,์ƒคํ”„์ง€์ˆ˜,MDD,์‹œ์Šคํ…œํˆฌ์ž,ํ€€ํŠธ๊ณต๋ถ€

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2026/07   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
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
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•