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

๋ฐ˜์‘ํ˜•

๐Ÿ“˜ ํ€€ํŠธ ํˆฌ์ž ๊ณต๋ถ€ 13๋‹จ๊ณ„

— “ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ‘์ •๋‹ต’์„ ์ฐพ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ‘์•ˆ์ „ํ•œ ๋ฒ”์œ„’๋ฅผ ์ •ํ•˜๋Š” ๊ฒƒ: Robust ์ „๋žต ๋งŒ๋“ค๊ธฐ”

12๋‹จ๊ณ„์—์„œ ์›Œํฌํฌ์›Œ๋“œ ๊ฒ€์ฆ์„ ํ–ˆ์ฃ .
์ด์ œ ์ „๋žต์ด “์–ด๋–ค ์‹œ๊ธฐ์— ๊นจ์ง€๋Š”์ง€”๊นŒ์ง€ ๋ณด์ด๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ๋‹ค์Œ์— ์ดˆ๋ณด์ž๋“ค์ด ๋ฐ”๋กœ ํ•˜๋Š” ์ƒ๊ฐ์ด ์ด๊ฑฐ์˜ˆ์š”.

“๊ทธ๋Ÿผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋” ํŠœ๋‹ํ•ด์„œ
๊นจ์ง€๋Š” ๊ตฌ๊ฐ„์„ ์—†์• ๋ฉด ๋˜์ง€ ์•Š์„๊นŒ?”

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ๊ณผ์ตœ์ ํ™”๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

13๋‹จ๊ณ„์˜ ํ•ต์‹ฌ์€ ์ด๊ฒ๋‹ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ •๋‹ต(์ตœ์ ๊ฐ’)์„ ์ฐพ์ง€ ๋ง๊ณ ,
์กฐ๊ธˆ ๋ฐ”๊ฟ”๋„ ์„ฑ๊ณผ๊ฐ€ ์œ ์ง€๋˜๋Š” ‘์•ˆ์ „ํ•œ ๋ฒ”์œ„(robust range)’๋ฅผ ์ฐพ์•„๋ผ.

์ด๊ฒŒ ์‹ค์ „ ํ€€ํŠธ์™€ ๋ฐฑํ…Œ์ŠคํŠธ ๋†€์ด๋ฅผ ๊ฐ€๋ฅด๋Š” ๊ฒฝ๊ณ„์„ ์ž…๋‹ˆ๋‹ค.


1๏ธโƒฃ “์ตœ์  ํŒŒ๋ผ๋ฏธํ„ฐ”๊ฐ€ ์œ„ํ—˜ํ•œ ์ด์œ 

์˜ˆ๋ฅผ ๋“ค์–ด ์ด๋Ÿฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค.

  • lookback = 63์ผ์ผ ๋•Œ CAGR 21%
  • lookback = 60์ผ์ผ ๋•Œ CAGR 12%
  • lookback = 66์ผ์ผ ๋•Œ CAGR 9%

์ด๊ฑด ๋ฌด์Šจ ๋œป์ผ๊นŒ์š”?

63์ด๋ผ๋Š” ์ˆซ์ž๊ฐ€ ํŠน๋ณ„ํ•œ ๊ฒŒ ์•„๋‹ˆ๋ผ,
์šฐ์—ฐํžˆ ๋”ฑ ๋งž์•„๋–จ์–ด์ง„ ์ง€์ ์„ ์žก์•˜๋‹ค๋Š” ๋œป
์ž…๋‹ˆ๋‹ค.

์ง„์งœ ์›๋ฆฌ๋ฅผ ์žก์€ ์ „๋žต์ด๋ผ๋ฉด
60~70 ์ •๋„์—์„œ
๋น„์Šทํ•œ ์„ฑ๊ณผ๊ฐ€ ๋‚˜์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.


2๏ธโƒฃ Robust(ํŠผํŠผํ•œ) ์ „๋žต์˜ ํŠน์ง•

ํŠผํŠผํ•œ ์ „๋žต์€ ์ด๋Ÿฐ ํŠน์ง•์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

โœ… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐ๊ธˆ ๋ฐ”๊ฟ”๋„ ๊ฒฐ๊ณผ๊ฐ€ ํฌ๊ฒŒ ํ”๋“ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค
โœ… ํŠน์ • ๊ตฌ๊ฐ„์—์„œ๋งŒ ํญ๋ฐœํ•˜์ง€ ์•Š๋Š”๋‹ค
โœ… ์›Œํฌํฌ์›Œ๋“œ ๊ตฌ๊ฐ„๋งˆ๋‹ค ๊ฒฐ๊ณผ๊ฐ€ ๊ณ ๋ฅด๊ฒŒ ๋‚˜์˜จ๋‹ค
โœ… ๊ฑฐ๋ž˜ ํšŸ์ˆ˜/๋น„์šฉ์ด ํ•ฉ๋ฆฌ์ ์ด๋‹ค
โœ… “์™œ ๋˜๋Š”์ง€” ์„ค๋ช…์ด ๊ฐ€๋Šฅํ•˜๋‹ค

ํ•ต์‹ฌ์€ “์˜ˆ์˜๊ฒŒ ์ž˜ ๋จ”์ด ์•„๋‹ˆ๋ผ

๋œ ๋ง๊ฐ€์ง์ž…๋‹ˆ๋‹ค.


3๏ธโƒฃ Robust Range๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ• (๋ฏผ๊ฐ๋„ ๋ถ„์„)

๋ฏผ๊ฐ๋„ ๋ถ„์„์€ ๋ง์ด ์–ด๋ ค์šด๋ฐ,
ํ•˜๋Š” ์ผ์€ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐ’์œผ๋กœ ๋Œ๋ ค๋ณด๊ณ 
์„ฑ๊ณผ๊ฐ€ ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€๋˜๋Š” ๊ตฌ๊ฐ„์„ ์ฐพ๋Š”๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋ชจ๋ฉ˜ํ…€ lookback์„
40~120์ผ๊นŒ์ง€ 10์ผ ๋‹จ์œ„๋กœ ๋Œ๋ ค๋ด…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ด…๋‹ˆ๋‹ค.

  • 40~60: ์„ฑ๊ณผ ๋‚˜์จ
  • 60~90: ์„ฑ๊ณผ ์•ˆ์ •์ ์œผ๋กœ ์ข‹์Œ
  • 90~120: ์„ฑ๊ณผ ๋‹ค์‹œ ๋‚˜๋น ์ง

์ด๋ ‡๊ฒŒ ๋‚˜์˜ค๋ฉด
์ •๋‹ต์€ “73์ผ”์ด ์•„๋‹ˆ๋ผ

โœ… 60~90์ผ ๊ตฌ๊ฐ„์ด robustํ•˜๋‹ค
์ž…๋‹ˆ๋‹ค.


4๏ธโƒฃ Robust ํŒŒ๋ผ๋ฏธํ„ฐ ์„ ํƒ์˜ ๊ธฐ์ค€

๋ฐ˜์‘ํ˜•

ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฒ”์œ„๋ฅผ ์ •ํ•  ๋•Œ
ํ€€ํŠธ๋Š” ์ด๋Ÿฐ ๊ธฐ์ค€์œผ๋กœ ๊ณ ๋ฆ…๋‹ˆ๋‹ค.

โœ… 1) ์„ฑ๊ณผ ์ƒ์œ„ “๊ตฌ๊ฐ„”์„ ๊ณ ๋ฅธ๋‹ค

  • ์ตœ๊ณ ์  1๊ฐœ๊ฐ€ ์•„๋‹ˆ๋ผ
  • ์ƒ์œ„ 20% ์ •๋„๊ฐ€ ๋ญ‰์ณ์žˆ๋Š” ๊ตฌ๊ฐ„

โœ… 2) ๊ฑฐ๋ž˜๋น„์šฉ์ด ํ•ฉ๋ฆฌ์ ์ธ ๊ตฌ๊ฐ„

  • ์„ฑ๊ณผ๋Š” ์กฐ๊ธˆ ๋‚ฎ์•„๋„
  • turnover(ํšŒ์ „์œจ)๊ฐ€ ๋‚ฎ์€ ๊ตฌ๊ฐ„์ด ์‹ค์ „์—์„œ ๋” ์ข‹์Šต๋‹ˆ๋‹ค.

โœ… 3) ์›Œํฌํฌ์›Œ๋“œ์—์„œ ์ผ๊ด€๋œ ๊ตฌ๊ฐ„

  • ํŠน์ • ์‹œ๊ธฐ์—๋งŒ ์ข‹์€ ๊ตฌ๊ฐ„์€ ์ œ์™ธ

5๏ธโƒฃ (์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ) ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฏผ๊ฐ๋„ ๋ถ„์„ ์˜ˆ์‹œ

์•„๋ž˜ ์ฝ”๋“œ๋Š” 12๋‹จ๊ณ„์—์„œ ๋งŒ๋“  ๋ฐฑํ…Œ์ŠคํŠธ ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•ด์„œ
lookback ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ”๊พธ๋ฉฐ ์„ฑ๊ณผํ‘œ๋ฅผ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
(์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜์ด๋ผ ๊ทธ๋Œ€๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.)

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, mdd, sharpe

if __name__ == "__main__":
    prices = make_sample_prices()

    rows = []
    for lb in range(40, 121, 10):  # 40~120, 10์ผ ๊ฐ„๊ฒฉ
        eq = backtest_mom_topn(prices, lookback=lb, top_n=3, fee_bps=5)
        cagr, mdd, sharpe = stats(eq)
        rows.append({"lookback": lb, "CAGR": cagr, "MDD": mdd, "Sharpe": sharpe})

    df = pd.DataFrame(rows).sort_values("Sharpe", ascending=False)
    print(df)

โœ… ์ด ๊ฒฐ๊ณผ์—์„œ “1๋“ฑ”์„ ๊ณ ๋ฅด์ง€ ๋งˆ์„ธ์š”.
๋Œ€์‹ , ์ƒ์œ„๊ถŒ์— ๋ญ‰์ณ ์žˆ๋Š” ๊ตฌ๊ฐ„์„ ์ฐพ์œผ์„ธ์š”.


6๏ธโƒฃ Robust ์ „๋žต์„ ๋งŒ๋“œ๋Š” ์‹ค์ „ ํŒ 3๊ฐœ

โœ… ํŒ 1) ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋‘ฅ๊ธ€๊ฒŒ ๊ณ ๋ฅธ๋‹ค

63์ผ์ด ์ตœ๊ณ ๋ฉด
60์ผ์ด๋‚˜ 65์ผ ๊ฐ™์€ “๋‘ฅ๊ทผ ๊ฐ’”์„ ๊ณ ๋ฆ…๋‹ˆ๋‹ค.

→ ์šฐ์—ฐ์˜ ์ตœ์ ์ ์„ ํ”ผํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

โœ… ํŒ 2) ์ตœ๊ณ  ์„ฑ๊ณผ๋ณด๋‹ค, ์ตœ์•… ์„ฑ๊ณผ๋ฅผ ๋ณธ๋‹ค

  • ํ‰๊ท ์ด ์•„๋‹ˆ๋ผ
  • ํ•˜์œ„ 20% ๊ตฌ๊ฐ„์—์„œ ๋ฒ„ํ‹ฐ๋Š”์ง€

์ด๊ฒŒ ์‹ค์ „์ž…๋‹ˆ๋‹ค.

โœ… ํŒ 3) ๊ฑฐ๋ž˜ ํšŸ์ˆ˜๊ฐ€ ๋Š˜๋ฉด, ์„ฑ๊ณผ๋Š” ์ค„์–ด๋“ ๋‹ค

ํŠนํžˆ ๊ฐœ์ธ ํˆฌ์ž์ž์—๊ฒŒ
์ˆ˜์ˆ˜๋ฃŒ์™€ ์Šฌ๋ฆฌํ”ผ์ง€๋Š” ์ƒ๊ฐ๋ณด๋‹ค ํฌ๊ฒŒ ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค.


7๏ธโƒฃ ์•„์ฃผ ์ž‘์€ ์‹ค์Šต (ํ•ต์‹ฌ ๊ณผ์ œ)

โœ๏ธ ๊ณผ์ œ 13

๋‹น์‹ ์ด ๋งŒ๋“  ์ „๋žต์—์„œ
ํŒŒ๋ผ๋ฏธํ„ฐ ํ•˜๋‚˜๋งŒ ๊ณจ๋ผ์„œ

  • ±10~20% ๋ฒ”์œ„๋กœ ๋ฐ”๊ฟ”๋ณด๊ณ 
  • ์„ฑ๊ณผ๊ฐ€ ์œ ์ง€๋˜๋Š” ๊ตฌ๊ฐ„์„ ์ฐพ์œผ์„ธ์š”.

๊ทธ๋ฆฌ๊ณ  ์ด ๋ฌธ์žฅ์„ ์“ฐ์„ธ์š”.

“๋‚ด ์ „๋žต์˜ ์ตœ์ ๊ฐ’์€ ___์ด์ง€๋งŒ,
~ ๋ฒ”์œ„์—์„œ๋„ ๋น„์Šทํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค.
๊ทธ๋ž˜์„œ ๋‚˜๋Š” ___๋ฅผ ์„ ํƒํ•œ๋‹ค.”

์ด ๋ฌธ์žฅ์„ ์“ธ ์ˆ˜ ์žˆ๋‹ค๋ฉด
๋‹น์‹ ์€ “๋ฐฑํ…Œ์ŠคํŠธ ๋†€์ด”๋ฅผ ๋ฒ—์–ด๋‚˜
์‹ค์ „ ํ€€ํŠธ ์„ค๊ณ„์ž๋กœ ๋„˜์–ด์˜จ ๊ฒ๋‹ˆ๋‹ค.


๐Ÿ“Œ ์ •๋ฆฌ

  • ์ตœ์ ๊ฐ’์€ ์œ„ํ—˜ํ•˜๋‹ค (์šฐ์—ฐ์ผ ๊ฐ€๋Šฅ์„ฑ)
  • ๋ชฉํ‘œ๋Š” ์ •๋‹ต์ด ์•„๋‹ˆ๋ผ ์•ˆ์ „ํ•œ ๋ฒ”์œ„(robust range)
  • ๋ฏผ๊ฐ๋„ ๋ถ„์„์œผ๋กœ “๋‘”๊ฐํ•œ ๊ตฌ๊ฐ„”์„ ์ฐพ์•„๋ผ
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋‘ฅ๊ธ€๊ฒŒ, ๋ณด์ˆ˜์ ์œผ๋กœ
  • ๊ฑฐ๋ž˜๋น„์šฉ๊นŒ์ง€ ํฌํ•จํ•ด ์‹ค์ „์„ฑ์„ ํ™•๋ณดํ•ด์•ผ ํ•œ๋‹ค

 

ํŒŒ๋ผ๋ฏธํ„ฐํŠœ๋‹,๋ฏผ๊ฐ๋„๋ถ„์„,Robust์ „๋žต,ํ€€ํŠธ๊ฒ€์ฆ,๊ณผ์ตœ์ ํ™”๋ฐฉ์ง€,์›Œํฌํฌ์›Œ๋“œ,๋ฐฑํ…Œ์ŠคํŠธ,์ƒคํ”„์ง€์ˆ˜,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
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•