같은 폭락에서 누구는 −55% 반토막이 났고, 누구는 −23%만 잃고 버텼습니다. 종목을 잘 골라서도, 타이밍을 맞혀서도 아닙니다. 규칙 하나 — 자산배분 40/30/30 + 1년에 한 번 리밸런싱. 지난 21년 시장 데이터로 검증했고, 그 검증 코드를 전부 무료로 공개합니다.
범인은 전략이 아니라 감정입니다.
S&P500은 지난 21년, 가만히 들고만 있었어도 원금이 아홉 배가 됐습니다. 문제는 그 안에 계좌가 반토막(−55%) 나는 구간이 있었다는 것. 자산이 절반으로 쪼그라드는 걸 눈앞에서 보면, 대부분은 결국 버티지 못하고 팝니다. 그리고 다 오른 뒤에, 꼭대기에서 다시 삽니다 — 정확히 반대로.
예측은 없습니다. 미래를 한 번도 안 맞혀도 됩니다.
미국 S&P500(SPY)에 40, 금(GLD)에 30, 미국 장기국채(TLT)에 30. 한 곳에 몰지 않고 셋으로 나눕니다. 서로 다르게 움직이는 자산이 폭락의 충격을 나눠 받습니다.
1년에 딱 한 번, 흐트러진 비율을 원래대로 되돌립니다. 많이 오른 걸 팔아서 싸진 걸 삽니다. 자동으로 "비싸게 팔고 싸게 사는" 구조가 됩니다. 1년에 5분이면 끝.
저희를 믿지 마세요. 직접 돌려보세요.
아래 코드는 이 페이지의 모든 숫자를 만든 백테스트 원본 그대로입니다. 야후 파이낸스 무료 데이터(배당 재투자 포함)로 누구나 같은 결과를 재현할 수 있습니다. 비율·자산·기간을 바꿔서 여러분만의 실험도 해보세요.
# -*- coding: utf-8 -*-
# =============================================================================
# AlgoLab · 리밸런싱 봇(자산배분 + 리밸런싱) 백테스트 — 공개용
# "폭락에 팔지 않는 규칙"(40/30/30 + 연 1회 리밸런싱)의 숫자를 그대로 재현합니다.
# 믿지 말고 직접 돌려보세요. 비율/자산/기간을 바꿔 여러분만의 실험을 해보세요.
#
# 필요: python 3, numpy (설치: pip install numpy)
# 실행: python discipline_bot_backtest.py
# 데이터: 야후 파이낸스 무료 API(배당·이자 재투자 반영 adjclose = 총수익 기준).
# (교육·검증용, 투자 권유 아님)
# =============================================================================
import urllib.request
import json
import datetime
import numpy as np
# ── 여기만 바꿔서 실험하세요 ────────────────────────────────────────────────
TICKERS = {"미국S&P500": "SPY", "금": "GLD", "미국장기국채": "TLT"}
WEIGHTS = [0.40, 0.30, 0.30] # 위 순서대로의 목표 비중 (합 1.0)
START = (2005, 1, 1) # 시작일
END = (2026, 7, 17) # 종료일(as-of 고정 → 언제 돌려도 같은 결과 재현). None이면 오늘까지.
REBAL_YEARS = 1 # 리밸런싱 주기(년)
PANIC_DD = -0.20 # 감정적 인간: 주식이 고점 대비 이만큼 빠지면 패닉셀
# ───────────────────────────────────────────────────────────────────────────
def fetch_total_return(sym, start, end=None):
"""야후 adjclose(배당 재투자 반영) 일별 시계열."""
p1 = int(datetime.datetime(*start).timestamp())
p2 = int(datetime.datetime(*end).timestamp()) if end else int(datetime.datetime.now().timestamp())
url = (f"https://query1.finance.yahoo.com/v8/finance/chart/{sym}"
f"?period1={p1}&period2={p2}&interval=1d")
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
d = json.loads(urllib.request.urlopen(req, timeout=30).read().decode())
r = d["chart"]["result"][0]
ts = r["timestamp"]
adj = r["indicators"]["adjclose"][0]["adjclose"]
return {datetime.datetime.utcfromtimestamp(t).date(): v
for t, v in zip(ts, adj) if v is not None}
def metrics(eq, years):
dret = np.diff(eq) / eq[:-1]
peak = np.maximum.accumulate(eq)
return {
"총수익": eq[-1] - 1,
"CAGR": eq[-1] ** (1 / years) - 1,
"MDD": float(np.min(eq / peak - 1)),
"샤프": (np.mean(dret) * 252) / (np.std(dret) * np.sqrt(252)) if np.std(dret) > 0 else 0,
}
def main():
# 데이터 정렬(공통 날짜)
series = {k: fetch_total_return(v, START, END) for k, v in TICKERS.items()}
common = set.intersection(*[set(s) for s in series.values()])
dates = sorted(common)
mat = np.array([[series[k][d] for k in TICKERS] for d in dates])
n, years = len(dates), len(dates) / 252
w = np.array(WEIGHTS)
rets = np.zeros_like(mat)
rets[1:] = mat[1:] / mat[:-1] - 1
STK = 0
print(f"기간 {dates[0]} ~ {dates[-1]} ({years:.1f}년, 배당·이자 포함)\n")
# 기준: 주식 100% 보유
eq_bh = np.cumprod(1 + rets[:, STK])
# 리밸런싱 포트폴리오(annual=True) / 방치(annual=False)
def portfolio(annual):
h = w.copy(); ly = dates[0].year; eq = [1.0]
for i in range(1, n):
h = h * (1 + rets[i])
pv = h.sum()
if annual and dates[i].year - ly >= REBAL_YEARS:
h = w * pv; ly = dates[i].year
eq.append(pv)
return np.array(eq)
eq_bot = portfolio(annual=True) # (A) 자동 리밸런싱 봇
eq_drift = portfolio(annual=False) # (B) 방치
# (C) 감정적 인간: 주식 고점 대비 PANIC_DD 넘게 빠지면 전량 채권으로, 고점 회복 시 재매수
BND = len(TICKERS) - 1
h = w.copy(); ly = dates[0].year; eqC = [1.0]; peak = mat[0, STK]; in_stock = True
for i in range(1, n):
peak = max(peak, mat[i, STK])
if in_stock and mat[i, STK] / peak - 1 < PANIC_DD:
h[BND] += h[STK]; h[STK] = 0.0; in_stock = False
elif (not in_stock) and mat[i, STK] >= peak * 0.999:
move = h[BND] * (w[STK] / (w[STK] + w[BND]))
h[BND] -= move; h[STK] += move; in_stock = True
h = h * (1 + rets[i]); pv = h.sum()
if dates[i].year - ly >= REBAL_YEARS:
tgt = w.copy()
if not in_stock:
tgt = w.copy(); tgt[BND] += tgt[STK]; tgt[STK] = 0.0
h = tgt * pv; ly = dates[i].year
eqC.append(pv)
eq_panic = np.array(eqC)
print(f"{'방식':22} {'총수익':>9} {'CAGR':>7} {'MDD':>7} {'샤프':>6}")
for name, eq in [("주식 100% 보유", eq_bh), ("리밸런싱 봇(분산+리밸런싱)", eq_bot),
("방치(드리프트)", eq_drift), ("감정적 인간(패닉셀)", eq_panic)]:
m = metrics(eq, years)
print(f"{name:22} {m['총수익']*100:>8,.0f}% {m['CAGR']*100:>6.1f}% {m['MDD']*100:>6.0f}% {m['샤프']:>6.2f}")
print("\n핵심: 리밸런싱 봇은 조금 덜 벌지만(CAGR↓) 낙폭은 절반 이하, 샤프(위험대비수익)는 크게 개선.")
print(" 그리고 같은 전략인데 '감정적 인간'이 봇보다 한참 뒤처짐 = 자동화(규율 실행)의 진짜 값.")
if __name__ == "__main__":
main()
파이썬 설치 — python.org에서 다운로드 (설치 시 "Add to PATH" 체크). 이미 있다면 넘어가세요.
numpy 설치 — 터미널(명령 프롬프트)에서 pip install numpy 한 줄.
실행 — 코드를 저장한 폴더에서 python discipline_bot_backtest.py. 몇 초 뒤 아래 표와 같은 숫자가 그대로 출력됩니다.
실험 — 상단의 WEIGHTS(비중), TICKERS(자산), REBAL_YEARS(주기), PANIC_DD(패닉 기준)를 바꿔서 직접 확인해보세요. 코드에는 '방치(리밸런싱 안 함)' 시나리오도 들어 있습니다.
기간 2004-12-31 ~ 2026-07-16 (21.5년) · 배당·이자 재투자 포함(adjclose) · 야후 파이낸스
| 방식 | 총수익 | 연복리 | 최대낙폭 | 샤프 |
|---|---|---|---|---|
| S&P500 100% (SPY) | +820% | 10.9% | −55% | 0.64 |
| 규칙 40/30/30 + 연 1회 리밸런싱 무료 코드 | +568% | 9.2% | −23% | 0.96 |
| 감정적 인간 (−20% 패닉셀 후 회복 시 재매수) 가정 | +495% | — | −27% | — |
매달 30만 원 × 21년 7개월(260회 납입) = 총 7,800만 원 기준 (연 3% 적금 가정과 비교)
같은 돈, 같은 기간인데 적금과는 두 배 넘게 벌어집니다. S&P 100%가 더 큰 것도 그대로 보여드립니다 — 단, 그 3억 9천은 −55% 반토막을 두 번 버텨야 손에 쥐는 숫자입니다. 적립식이면 폭락은 손실이 아니라 더 싸게 담는 기회가 됩니다.
시장을 이기는 봇이 아닙니다. 당신이 시장을 끝까지 못 들고 있는 그 문제를 없애는 봇입니다.
무료 코드의 3자산(40/30/30) 규칙을 한 단계 다듬었습니다. 채권을 장기(TLT)+중기(IEF)로 나눠 2022년 같은 금리충격 해에 덜 무너지게 한 채권분산 구성 — 낙폭 −23%→−21%, 샤프 0.96→0.98. "나눈다"는 규칙을 채권 만기까지 확장한 것뿐, 비밀은 없습니다. 아래는 널리 공개된 자산배분 방식(주식/금/채권 비중 조합) 네 가지를 같은 기간·같은 방법으로 나란히 검증해 본 예시입니다. 이 중에서 골라도 되고, 안 골라도 됩니다 — 배분 비중을 직접 정해서 운용할 수 있고, 어떤 규칙을 쓸지는 이용자 본인이 선택·설정합니다. 리밸런싱봇은 그 규칙을 기계적으로 실행하는 도구입니다.
공개 배분 방식 검증 예시 — 2005~2026.7, 배당 포함, 같은 수식으로 일괄 계산 (추천 아님)
※ 예시 이름은 주식 비중을 그대로 적은 것입니다 (성향 분류가 아닙니다). 네 가지 모두 널리 공개된 자산배분 방식(주식/금/채권 비중 조합)을 과거 데이터로 검증해 담아둔 예시 구성이며, 특정인에 대한 투자권유·추천이 아닙니다. 어느 구성을 쓸지·비중을 어떻게 바꿀지는 본인이 판단해 직접 설정합니다. 예시와 다른 구성이 궁금하면 위 무료 코드로, 리밸런싱봇 안에서는 [최신 데이터로 직접 검증] 기능으로 직접 백테스트해볼 수 있습니다.
파는 건 "비밀 전략"이 아니라, 감정을 눌러주는 규율 + 손 안 가는 편의입니다.
한국투자증권(KIS) 해외주식 API로 본인 계좌의 보유 자산·비중을 자동 인식합니다. 자금을 맡기는 게 아니라, 내 계좌에서 내가 직접 운용하는 도구입니다.
리밸런싱 날이 오면 알려주고, 목표 비중과의 차이를 계산해 필요한 주문을 자동 실행합니다. 기본은 연 1회 확인 클릭 몇 번(약 5분) — 그마저 생략하고 싶으면 완전 자동 예약(본인이 미리 내려두는 실행 지시, 언제든 해제)을 켤 수 있습니다. 주기(연·반기·분기·월)와 다음 실행 날짜도 본인이 직접 정합니다. 적립식이라면 입금 자동 투입을 켜두세요 — 계좌에 입금(환전)만 하면 새 예수금을 부족한 자산부터 목표 비중대로 매수합니다(매도 없음, 기준 금액·해제 자유).
급락장에 팔고 싶어지는 그 순간 — 매도 전에 이용자가 미리 적어둔 규칙(서약)을 그대로 다시 보여드립니다. 잠깐 멈추는 쿨다운도 본인이 선택합니다. 최종 결정은 언제나 본인 몫입니다.
내 구성의 최대낙폭·샤프를 숨기지 않고 그대로 보여줍니다. 지금 비중이 목표에서 얼마나 흐트러졌는지도 한눈에.
리밸런싱 일정, 주문 체결 결과, 그리고 직접 설정한 하락률 조건(예: 고점 대비 −20%) 도달 알림까지 휴대폰으로 받아보세요.
주식 40 / 60 / 70 / 70·기술틸트 프리셋 중 고르거나, 배분 비중을 직접 수정해 나만의 규칙으로 운용하세요. 어떤 구성이든 프로그램 안 [최신 데이터로 직접 검증] 버튼으로 오늘까지 데이터를 받아 직접 백테스트할 수 있습니다(무료 코드와 같은 방법) — 시작 연도 선택과 연도별 수익률 확인까지. 어느 쪽이든 선택과 판단은 본인 몫 — 리밸런싱봇은 정한 규칙을 실행할 뿐입니다.
가격, 계좌·API 준비 방법, 설치와 사용법 — 채팅으로 물어보시면 바로 안내드립니다.
단, 특정 모드·종목·시점을 추천하는 상담은 하지 않습니다. 투자판단은 본인 몫이며,
저희는 각 프리셋의 공개된 과거 데이터와 도구 사용법을 설명드릴 뿐입니다.
무료 코드만으로 충분한 분께는, 충분하다고 말씀드립니다.
※ 도구 제공 고지 — 리밸런싱봇은 이용자가 스스로 선택·설정한 규칙(자산 비중·리밸런싱 주기·알림 조건)을 기계적으로
실행하는 소프트웨어 도구입니다. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며,
특정 종목·비중·매매 시점에 관한 개별 자문이나 추천을 제공하지 않습니다.
본 페이지의 프리셋 구성과 백테스트 수치는 널리 공개된 자산배분 방식을 과거 데이터로 검증한
교육·검증 목적의 예시 자료이며, 특정인에 대한 투자권유·매수·매도 권유가 아닙니다.
※ 수익을 보장하지 않으며, 과거 백테스트 성과가 미래 수익을 보장하지 않습니다. 백테스트에는 세금·거래비용이 제외되어 있습니다.
모든 투자 판단과 그에 따른 책임은 이용자 본인에게 있습니다.
자산은 이용자 본인 증권사 계좌에서 본인 명의 API 로 직접 운용되며, 알고랩은 자금을 예치·수탁·운용하지 않고
이용자의 계좌 정보에 접근하지 않습니다.