연금저축 · 리밸런싱 · 실측 재현 페이지
연금저축에 주식 ETF 와 채권 ETF 를 반반으로 담아 두셨다면, 그 반반은 시간이 지나면 저절로 무너집니다. 2009-08-03 ~ 2026-09-23(17.1년) 동안 KODEX 200 반 · KODEX 국고채3년 반으로 시작한 1,000만원을 그냥 둔 계좌, 매달 한 번 다시 반반으로 맞춘 계좌, 1년에 한 번 맞춘 계좌 열두 개(1~12월)로 나눠 돌렸습니다.
이 페이지는 연금저축, 일주일에 한 번 보고 통째로 옮기기(무엇을 담을지)와 다른 질문 — 이미 반반으로 담아 둔 계좌를 얼마나 자주 되돌리나 — 를 다룹니다. 섞으라고 권하는 페이지가 아닙니다.
| 항목 | 값 |
|---|---|
| 주식 ETF | KODEX 200 (069500) — 코스피200 지수 |
| 채권 ETF | KODEX 국고채3년 (114260) |
| 목표 비중 | 반반 |
| 언제 | 매달 날짜 하나를 정해 둔다 — 계산은 첫 거래일. 1~15번째 거래일 전부 거의 같다(아래 표) |
| 무엇을 | 그날 종가로 비중을 보고, 늘어난 쪽을 팔아 줄어든 쪽을 사서 다음 거래일에 반반으로 |
| 비용 가정 | 옮긴 금액에만 편도 0.1% (파는 쪽·사는 쪽 둘 다) — 실제 ETF 수수료보다 넉넉하게 |
| 계좌 | 끝에 남은 돈 | 가장 크게 줄어든 폭 | 끝 주식 비중 | 되돌린 횟수 | 수수료 합 |
|---|---|---|---|---|---|
| 그냥 둔다 | 4,452만원 | -35.24% | 82.9% | 0번 | 0원 |
| 1년에 한 번 (1월) | 3,904만원 | -28.37% | 63.7% | 17번 | 26,802원 |
| 매달 한 번 (첫 거래일) — 이 편 | 3,684만원 | -21.64% | 52.2% | 205번 | 78,149원 |
| 매일 | 3,662만원 | -22.15% | 50.0% | 4187번 | 340,386원 |
가장 크게 줄어든 폭은 네 계좌 모두 2026-06-22 → 2026-07-30 이었습니다(KODEX 200 -40.8%).
KODEX 200 의 2026년 최고 종가일(2026-06-22) → 그 뒤 최저 종가일(2026-07-30). 계좌마다 폭락 직전 잔고가 다르므로 1,000만원으로 맞춰 견줬습니다. 원금 1,000만원이 줄었다는 뜻이 아닙니다 — 실제 폭락 직전 잔고는 그냥 둔 계좌 5,587만원, 매달 맞춘 계좌 4,168만원였고, 폭락 바닥에서도 그냥 둔 계좌에 돈이 더 남아 있었습니다(3,618만원 대 3,266만원).
| 계좌 | 그날 주식 비중 | 1,000만원이 7월 30일에 |
|---|---|---|
| 그냥 둔다 | 86.5% | 648만원 |
| 매달 한 번 | 51.1% | 784만원 |
| 매일 | 50.0% | 778만원 |
1,000만원으로 시작한 계좌의 실제 체결. 6월 2일에 늘어난 주식을 덜어 냈고, 폭락 한가운데인 7월 2일에는 줄어든 주식을 다시 샀습니다. 그 뒤로도 주식은 7월 30일까지 더 떨어졌습니다 — 사고판 때가 좋았던 것이 아니라 비중이 반반으로 지켜진 것입니다.
| 체결일 | 체결 전 주식 비중 | 한 일 | 옮긴 금액 |
|---|---|---|---|
| 2026-01-05 | 53.7% | 주식 ETF 팔고 채권 ETF 삼 | 991,280원 |
| 2026-02-03 | 54.7% | 주식 ETF 팔고 채권 ETF 삼 | 1,395,440원 |
| 2026-03-04 | 49.2% | 채권 ETF 팔고 주식 ETF 삼 | 241,583원 |
| 2026-04-02 | 50.8% | 주식 ETF 팔고 채권 ETF 삼 | 249,686원 |
| 2026-05-06 | 59.3% | 주식 ETF 팔고 채권 ETF 삼 | 3,352,459원 |
| 2026-06-02 | 55.6% | 주식 ETF 팔고 채권 ETF 삼 | 2,281,542원 |
| 2026-07-02 | 46.5% | 채권 ETF 팔고 주식 ETF 삼 | 1,337,768원 |
| 2026-08-04 | 44.8% | 채권 ETF 팔고 주식 ETF 삼 | 1,814,234원 |
| 2026-09-02 | 50.8% | 주식 ETF 팔고 채권 ETF 삼 | 292,243원 |
되돌리는 달만 1~12월로 바꿔 열두 계좌를 돌렸습니다. 같은 폭락에서 결과가 8월 681만원 ~ 6월 793만원 로 갈렸습니다. 6월 계좌는 폭락 직전(6월 2일)에 맞췄을 뿐입니다. 한편 17년 끝 금액은 1년에 한 번 계좌 열두 개 모두가 매달 계좌보다 많았습니다.
| 맞추는 달 | 6월 22일 주식 비중 | 1,000만원이 7월 30일에 | 17년 가장 크게 줄어든 폭 | 17년 끝 금액 |
|---|---|---|---|---|
| 1월 | 69.8% | 716만원 | -28.37% | 3,904만원 |
| 2월 | 65.6% | 733만원 | -26.66% | 3,918만원 |
| 3월 | 66.4% | 730만원 | -26.97% | 3,875만원 |
| 4월 | 65.6% | 733만원 | -26.66% | 4,005만원 |
| 5월 | 56.7% | 770만원 | -23.01% | 4,250만원 |
| 6월 (가장 얕음) | 51.1% | 793만원 | -20.71% | 4,423만원 |
| 7월 | 78.6% | 743만원 | -25.68% | 4,130만원 |
| 8월 (가장 깊음) | 78.3% | 681만원 | -31.86% | 3,878만원 |
| 9월 | 78.1% | 682만원 | -31.82% | 3,855만원 |
| 10월 | 75.5% | 693만원 | -30.75% | 3,858만원 |
| 11월 | 72.4% | 706만원 | -29.43% | 3,818만원 |
| 12월 | 72.8% | 704만원 | -29.61% | 3,818만원 |
| 매달 한 번 (비교) | 51.1% | 784만원 | -21.64% | 3,684만원 |
맞습니다. 2025년 말까지만 잘라 같은 표를 다시 재면 갈림이 훨씬 작습니다 — 달마다 결과가 갈린 것은 반년에 주식이 두 배 넘게 오른 뒤 무너진 올해가 만든 것입니다.
| 2025-12-31 까지 | 가장 크게 줄어든 폭 |
|---|---|
| 그냥 둔다 | -23.86% |
| 1년에 한 번 (열두 달) | -20.95% ~ -19.93% |
| 매달 한 번 (1~15번째 거래일) | -20.68% ~ -20.43% |
| 맞추는 날 | 가장 크게 줄어든 폭 | 끝 금액 | 되돌린 횟수 | 수수료 합 |
|---|---|---|---|---|
| 1번째 거래일 | -21.64% | 3,684만원 | 205번 | 78,149원 |
| 2번째 거래일 | -21.39% | 3,668만원 | 206번 | 75,356원 |
| 3번째 거래일 | -21.58% | 3,651만원 | 206번 | 74,411원 |
| 4번째 거래일 | -22.22% | 3,644만원 | 206번 | 73,340원 |
| 5번째 거래일 | -22.19% | 3,634만원 | 206번 | 76,483원 |
| 6번째 거래일 | -22.39% | 3,670만원 | 206번 | 76,181원 |
| 7번째 거래일 | -22.27% | 3,616만원 | 206번 | 70,042원 |
| 8번째 거래일 | -22.39% | 3,623만원 | 206번 | 72,263원 |
| 9번째 거래일 | -22.08% | 3,628만원 | 206번 | 74,049원 |
| 10번째 거래일 | -21.89% | 3,640만원 | 206번 | 77,509원 |
| 11번째 거래일 | -21.84% | 3,665만원 | 205번 | 79,985원 |
| 12번째 거래일 | -21.53% | 3,734만원 | 205번 | 84,186원 |
| 13번째 거래일 | -21.61% | 3,776만원 | 205번 | 86,107원 |
| 14번째 거래일 | -21.55% | 3,763만원 | 205번 | 87,253원 |
| 15번째 거래일 | -22.22% | 3,758만원 | 205번 | 85,102원 |
돌리는 법: zip 을 풀고 pip install numpy pandas 뒤 python run_banban.py.
① 판 2009-08-03 ~ 2026-09-23 · 17.1년 · 4191거래일 · 원금 10,000,000원
종목: KODEX 200(069500) 반 + KODEX 국고채3년(114260) 반 · 편도 0.1% · 체결은 다음 거래일
② 세 가지 계좌
안함 끝 44,517,210원 · 연 9.10% · 최대낙폭 -35.24% (2026-06-22→2026-07-30, 55,873,011→36,183,353원) · 끝 주식비중 82.9% · 되돌린 0번 · 수수료 0원
1년에한번_1월 끝 39,042,312원 · 연 8.27% · 최대낙폭 -28.37% (2026-06-22→2026-07-30, 46,649,984→33,417,236원) · 끝 주식비중 63.7% · 되돌린 17번 · 수수료 26,802원
한달에한번_첫거래일 끝 36,840,050원 · 연 7.91% · 최대낙폭 -21.64% (2026-06-22→2026-07-30, 41,683,887→32,662,212원) · 끝 주식비중 52.2% · 되돌린 205번 · 수수료 78,149원
매일 끝 36,623,214원 · 연 7.87% · 최대낙폭 -22.15% (2026-06-22→2026-07-30, 40,981,972→31,902,978원) · 끝 주식비중 50.0% · 되돌린 4187번 · 수수료 340,386원
③ 1년에 한 번 — 되돌리는 달만 1~12월로 바꿔 본다
1월 끝 39,042,312원 · 최대낙폭 -28.37% (2026-06-22→2026-07-30)
2월 끝 39,180,886원 · 최대낙폭 -26.66% (2026-06-22→2026-07-30)
3월 끝 38,751,396원 · 최대낙폭 -26.97% (2026-06-22→2026-07-30)
4월 끝 40,049,683원 · 최대낙폭 -26.66% (2026-06-22→2026-07-30)
5월 끝 42,503,354원 · 최대낙폭 -23.01% (2026-06-22→2026-07-30)
6월 끝 44,229,965원 · 최대낙폭 -20.71% (2026-06-22→2026-07-30)
7월 끝 41,300,645원 · 최대낙폭 -25.68% (2026-06-22→2026-07-30)
8월 끝 38,784,981원 · 최대낙폭 -31.86% (2026-06-22→2026-07-30)
9월 끝 38,548,881원 · 최대낙폭 -31.82% (2026-06-22→2026-07-30)
10월 끝 38,578,653원 · 최대낙폭 -30.75% (2026-06-22→2026-07-30)
11월 끝 38,176,035원 · 최대낙폭 -29.43% (2026-06-22→2026-07-30)
12월 끝 38,179,920원 · 최대낙폭 -29.61% (2026-06-22→2026-07-30)
→ 가장 깊은 달 8월 -31.86% · 가장 얕은 달 6월 -20.71% · 폭 11.15
→ 끝금액 범위 38,176,035 ~ 44,229,965원
④ 한 달에 한 번 — 그 달 몇 번째 거래일에 되돌리나만 바꿔 본다(1~15번째)
1번째 끝 36,840,050원 · 최대낙폭 -21.64% · 되돌린 205번 · 수수료 78,149원
2번째 끝 36,676,922원 · 최대낙폭 -21.39% · 되돌린 206번 · 수수료 75,356원
3번째 끝 36,507,464원 · 최대낙폭 -21.58% · 되돌린 206번 · 수수료 74,411원
4번째 끝 36,438,597원 · 최대낙폭 -22.22% · 되돌린 206번 · 수수료 73,340원
5번째 끝 36,339,269원 · 최대낙폭 -22.19% · 되돌린 206번 · 수수료 76,483원
6번째 끝 36,703,636원 · 최대낙폭 -22.39% · 되돌린 206번 · 수수료 76,181원
7번째 끝 36,159,442원 · 최대낙폭 -22.27% · 되돌린 206번 · 수수료 70,042원
8번째 끝 36,229,620원 · 최대낙폭 -22.39% · 되돌린 206번 · 수수료 72,263원
9번째 끝 36,277,887원 · 최대낙폭 -22.08% · 되돌린 206번 · 수수료 74,049원
10번째 끝 36,401,910원 · 최대낙폭 -21.89% · 되돌린 206번 · 수수료 77,509원
11번째 끝 36,645,915원 · 최대낙폭 -21.84% · 되돌린 205번 · 수수료 79,985원
12번째 끝 37,343,719원 · 최대낙폭 -21.53% · 되돌린 205번 · 수수료 84,186원
13번째 끝 37,758,385원 · 최대낙폭 -21.61% · 되돌린 205번 · 수수료 86,107원
14번째 끝 37,633,250원 · 최대낙폭 -21.55% · 되돌린 205번 · 수수료 87,253원
15번째 끝 37,579,843원 · 최대낙폭 -22.22% · 되돌린 205번 · 수수료 85,102원
→ 최대낙폭 범위 -22.39 ~ -21.39% · 폭 1.00 · 끝금액 36,159,442 ~ 37,758,385원
⑤ 올여름 폭락 — KODEX 200 의 2026년 최고 종가일 → 그 뒤 최저 종가일
2026-06-22 → 2026-07-30 · KODEX 200 -40.8%
안함 그날 주식 86.5% · 그날 1,000만원이던 계좌가 → 6,480,000원
1년에한번_1월 그날 주식 69.8% · 그날 1,000만원이던 계좌가 → 7,160,000원
1년에한번_2월 그날 주식 65.6% · 그날 1,000만원이던 계좌가 → 7,330,000원
1년에한번_3월 그날 주식 66.4% · 그날 1,000만원이던 계좌가 → 7,300,000원
1년에한번_4월 그날 주식 65.6% · 그날 1,000만원이던 계좌가 → 7,330,000원
1년에한번_5월 그날 주식 56.7% · 그날 1,000만원이던 계좌가 → 7,700,000원
1년에한번_6월 그날 주식 51.1% · 그날 1,000만원이던 계좌가 → 7,930,000원
1년에한번_7월 그날 주식 78.6% · 그날 1,000만원이던 계좌가 → 7,430,000원
1년에한번_8월 그날 주식 78.3% · 그날 1,000만원이던 계좌가 → 6,810,000원
1년에한번_9월 그날 주식 78.1% · 그날 1,000만원이던 계좌가 → 6,820,000원
1년에한번_10월 그날 주식 75.5% · 그날 1,000만원이던 계좌가 → 6,930,000원
1년에한번_11월 그날 주식 72.4% · 그날 1,000만원이던 계좌가 → 7,060,000원
1년에한번_12월 그날 주식 72.8% · 그날 1,000만원이던 계좌가 → 7,040,000원
한달에한번_첫거래일 그날 주식 51.1% · 그날 1,000만원이던 계좌가 → 7,840,000원
매일 그날 주식 50.0% · 그날 1,000만원이던 계좌가 → 7,780,000원
⑥ 올해를 빼면? — 2025-12-31 까지만 잘라 같은 표를 다시 잰다
안함 최대낙폭 -23.86% (2022-09-30) · 끝 주식비중 72.2%
1년에 한 번 12달: -20.95 ~ -19.93% (폭 1.02)
한 달에 한 번 15날: -20.68 ~ -20.43% (폭 0.25)
⑦ 되돌리기 한 번 — 한 달에 한 번(첫 거래일) 계좌의 2026년 체결 기록
2026-01-05 체결 전 주식 14,405,859 · 채권 12,423,299 (주식 53.7%) → 주식 팔고 채권 삼 991,280원 · 수수료 1,983원
2026-02-03 체결 전 주식 16,163,191 · 채권 13,372,312 (주식 54.7%) → 주식 팔고 채권 삼 1,395,440원 · 수수료 2,791원
2026-03-04 체결 전 주식 14,315,576 · 채권 14,798,741 (주식 49.2%) → 주식 사고 채권 팜 241,583원 · 수수료 483원
2026-04-02 체결 전 주식 14,996,117 · 채권 14,496,745 (주식 50.8%) → 주식 팔고 채권 삼 249,686원 · 수수료 499원
2026-05-06 체결 전 주식 21,457,099 · 채권 14,752,182 (주식 59.3%) → 주식 팔고 채권 삼 3,352,459원 · 수수료 6,705원
2026-06-02 체결 전 주식 22,624,612 · 채권 18,061,528 (주식 55.6%) → 주식 팔고 채권 삼 2,281,542원 · 수수료 4,563원
2026-07-02 체결 전 주식 17,748,174 · 채권 20,423,710 (주식 46.5%) → 주식 사고 채권 팜 1,337,768원 · 수수료 2,676원
2026-08-04 체결 전 주식 15,532,071 · 채권 19,160,540 (주식 44.8%) → 주식 사고 채권 팜 1,814,234원 · 수수료 3,628원
2026-09-02 체결 전 주식 17,892,503 · 채권 17,308,018 (주식 50.8%) → 주식 팔고 채권 삼 292,243원 · 수수료 584원
→ 결과_banban.json
→ 수치표_banban.json
# -*- coding: utf-8 -*-
"""260927 연금저축 반반 계좌 — 되돌리는 주기 실측 (공개 코드).
계좌: KODEX 200(069500) 반 + KODEX 국고채3년(114260) 반으로 시작하는 1,000만원.
팔: 안함 · 1년에 한 번(1~12월 각각, 그 달 첫 거래일) · 한 달에 한 번(그 달 k번째 거래일, k=1..15) · 매일.
규약: 신호는 점검일 종가, 체결은 다음 거래일 종가(한 봉 지연) · 수수료는 **실제로 옮긴 금액에만**
편도 0.1%(파는 쪽·사는 쪽 둘 다) · 가격은 야후 수정주가(분배금 재투자). 단 국고채3년(114260)은 야후 분배 기록이 2018-12 에서 끊겨 그 뒤 분배금(예: 2025-12 807원)이 빠져 있다 — 채권을 더 든 계좌에 불리한 쪽이다.
기준일 ASOF 고정 — 이 날짜까지의 봉만 쓴다(영상과 같은 숫자가 나오게).
실행: python run_banban.py (같은 폴더의 data_banban/ 을 읽는다)
데이터: 야후 파이낸스 수정주가(분배금 재투자)를 ASOF 에서 잘라 얼린 것이다. 수정주가는 새 분배금이
나올 때마다 과거 값이 조금씩 바뀌므로, 영상과 같은 숫자를 내려고 파일째 함께 싣는다.
"""
import argparse
import json
import os
import numpy as np
import pandas as pd
ASOF = "2026-09-23"
START = "2009-08-01"
원금 = 10_000_000
편도 = 0.001
위험, 안전 = "069500_KS", "114260_KS"
HERE = os.path.dirname(os.path.abspath(__file__))
기본폴더 = os.path.join(HERE, "data_banban") # 함께 받은 zip 을 풀면 이 폴더가 생긴다
def 읽기(폴더, sym):
d = pd.read_csv(os.path.join(폴더, sym + "_1d.csv"), parse_dates=["date"])
d = d[["date", "close"]].dropna()
d = d[(d["close"] > 0) & (d["date"] <= pd.Timestamp(ASOF))]
return d.drop_duplicates("date").sort_values("date").reset_index(drop=True)
def 판(폴더):
a, b = 읽기(폴더, 위험), 읽기(폴더, 안전)
m = a.merge(b, on="date", suffixes=("_r", "_s")) # 두 종목이 모두 거래된 날만
m = m[m["date"] >= pd.Timestamp(START)].reset_index(drop=True)
return m
def 점검일(dates, 방식, 인자=None):
"""점검(신호)이 켜지는 날 = True. 체결은 그다음 거래일."""
n = len(dates)
on = np.zeros(n, dtype=bool)
if 방식 == "안함":
return on
if 방식 == "매일":
on[1:] = True
return on
ym = dates.dt.year.values * 100 + dates.dt.month.values
# 그 달에서 몇 번째 거래일인가 (1부터)
k = np.ones(n, dtype=int)
for i in range(1, n):
k[i] = k[i - 1] + 1 if ym[i] == ym[i - 1] else 1
if 방식 == "매월": # 인자 = 그 달 몇 번째 거래일
on = k == 인자
elif 방식 == "연1회": # 인자 = 몇 월 (그 달 첫 거래일)
on = (k == 1) & (dates.dt.month.values == 인자)
on[0] = False
return on
def 돌리기(m, 방식, 인자=None, w0=0.5, fee=편도):
r = m["close_r"].values
s = m["close_s"].values
n = len(r)
on = 점검일(m["date"], 방식, 인자)
r_amt, s_amt = 원금 * w0, 원금 * (1 - w0)
eq = np.empty(n)
wp = np.empty(n)
eq[0], wp[0] = 원금, w0
횟수, 수수료합, 옮긴합 = 0, 0.0, 0.0
대기 = False
for i in range(1, n):
r_amt *= r[i] / r[i - 1]
s_amt *= s[i] / s[i - 1]
if 대기:
tot = r_amt + s_amt
d = abs(tot * w0 - r_amt)
if d > 0:
c = d * fee * 2.0
tot -= c
수수료합 += c
옮긴합 += d
횟수 += 1
r_amt, s_amt = tot * w0, tot * (1 - w0)
대기 = False
tot = r_amt + s_amt
eq[i] = tot
wp[i] = r_amt / tot
대기 = bool(on[i])
return eq, wp, 횟수, 수수료합, 옮긴합
def 요약(m, eq, wp, 횟수, 수수료합, 옮긴합):
dates = m["date"]
yrs = (dates.iloc[-1] - dates.iloc[0]).days / 365.25
peak = np.maximum.accumulate(eq)
dd = eq / peak - 1
j = int(dd.argmin())
i = int(np.argmax(eq[: j + 1]))
return {
"끝금액": int(round(eq[-1])),
"연수익률": round(((eq[-1] / eq[0]) ** (1 / yrs) - 1) * 100, 2),
"최대낙폭": round(float(dd.min()) * 100, 2),
"낙폭_고점일": str(dates.iloc[i].date()),
"낙폭_바닥일": str(dates.iloc[j].date()),
"낙폭_고점잔고": int(round(eq[i])),
"낙폭_바닥잔고": int(round(eq[j])),
"끝주식비중": round(float(wp[-1]) * 100, 1),
"주식비중_최대": round(float(wp.max()) * 100, 1),
"되돌린횟수": 횟수,
"수수료합": int(round(수수료합)),
"옮긴금액합": int(round(옮긴합)),
}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--csv-dir", default=기본폴더)
ap.add_argument("--json", default=os.path.join(HERE, "결과_banban.json"))
a = ap.parse_args()
m = 판(a.csv_dir)
yrs = (m["date"].iloc[-1] - m["date"].iloc[0]).days / 365.25
out = {"구간": [str(m["date"].iloc[0].date()), str(m["date"].iloc[-1].date())],
"연수": round(yrs, 1), "봉": len(m), "원금": 원금, "편도수수료": 편도, "팔": {}}
print(f"① 판 {out['구간'][0]} ~ {out['구간'][1]} · {out['연수']}년 · {len(m)}거래일 · 원금 {원금:,}원")
print(" 종목: KODEX 200(069500) 반 + KODEX 국고채3년(114260) 반 · 편도 0.1% · 체결은 다음 거래일\n")
def 기록(이름, 방식, 인자=None):
g = 요약(m, *돌리기(m, 방식, 인자))
out["팔"][이름] = g
return g
print("② 세 가지 계좌")
for 이름, 방식, 인자 in [("안함", "안함", None), ("1년에한번_1월", "연1회", 1),
("한달에한번_첫거래일", "매월", 1), ("매일", "매일", None)]:
g = 기록(이름, 방식, 인자)
print(f" {이름:<18} 끝 {g['끝금액']:>12,}원 · 연 {g['연수익률']:5.2f}% · 최대낙폭 {g['최대낙폭']:6.2f}% "
f"({g['낙폭_고점일']}→{g['낙폭_바닥일']}, {g['낙폭_고점잔고']:,}→{g['낙폭_바닥잔고']:,}원) · "
f"끝 주식비중 {g['끝주식비중']}% · 되돌린 {g['되돌린횟수']}번 · 수수료 {g['수수료합']:,}원")
print("\n③ 1년에 한 번 — 되돌리는 달만 1~12월로 바꿔 본다")
연 = {}
for 달 in range(1, 13):
g = 기록(f"1년에한번_{달}월", "연1회", 달)
연[달] = g
print(f" {달:>2}월 끝 {g['끝금액']:>12,}원 · 최대낙폭 {g['최대낙폭']:6.2f}% ({g['낙폭_고점일']}→{g['낙폭_바닥일']})")
lo = min(연, key=lambda d: 연[d]["최대낙폭"])
hi = max(연, key=lambda d: 연[d]["최대낙폭"])
print(f" → 가장 깊은 달 {lo}월 {연[lo]['최대낙폭']}% · 가장 얕은 달 {hi}월 {연[hi]['최대낙폭']}% · "
f"폭 {연[hi]['최대낙폭'] - 연[lo]['최대낙폭']:.2f}")
끝s = [연[d]["끝금액"] for d in 연]
print(f" → 끝금액 범위 {min(끝s):,} ~ {max(끝s):,}원")
print("\n④ 한 달에 한 번 — 그 달 몇 번째 거래일에 되돌리나만 바꿔 본다(1~15번째)")
월 = {}
for k in range(1, 16):
g = 기록(f"한달에한번_{k}번째", "매월", k)
월[k] = g
print(f" {k:>2}번째 끝 {g['끝금액']:>12,}원 · 최대낙폭 {g['최대낙폭']:6.2f}% · 되돌린 {g['되돌린횟수']}번 · 수수료 {g['수수료합']:,}원")
mdds = [월[k]["최대낙폭"] for k in 월]
끝m = [월[k]["끝금액"] for k in 월]
print(f" → 최대낙폭 범위 {min(mdds)} ~ {max(mdds)}% · 폭 {max(mdds) - min(mdds):.2f} · 끝금액 {min(끝m):,} ~ {max(끝m):,}원")
print("\n⑤ 올여름 폭락 — KODEX 200 의 2026년 최고 종가일 → 그 뒤 최저 종가일")
r = m["close_r"].values
y26 = np.where(m["date"].dt.year.values == 2026)[0]
p = int(y26[np.argmax(r[y26])])
t = int(p + np.argmin(r[p:]))
print(f" {m['date'].iloc[p].date()} → {m['date'].iloc[t].date()} · KODEX 200 {r[t] / r[p] * 100 - 100:.1f}%")
폭락 = {"고점일": str(m["date"].iloc[p].date()), "바닥일": str(m["date"].iloc[t].date()),
"KODEX200": round(r[t] / r[p] * 100 - 100, 1), "팔": {}}
for 이름, 방식, 인자 in ([("안함", "안함", None)] + [(f"1년에한번_{d}월", "연1회", d) for d in range(1, 13)]
+ [("한달에한번_첫거래일", "매월", 1), ("매일", "매일", None)]):
eq, wp, *_ = 돌리기(m, 방식, 인자)
g = {"고점날_주식비중": round(float(wp[p]) * 100, 1),
"천만원이": int(round(원금 * eq[t] / eq[p], -4))}
폭락["팔"][이름] = g
print(f" {이름:<18} 그날 주식 {g['고점날_주식비중']:5.1f}% · 그날 1,000만원이던 계좌가 → {g['천만원이']:,}원")
out["폭락"] = 폭락
print("\n⑥ 올해를 빼면? — 2025-12-31 까지만 잘라 같은 표를 다시 잰다")
m25 = m[m["date"] <= pd.Timestamp("2025-12-31")].reset_index(drop=True)
앞 = {}
for 이름, 방식, 인자 in ([("안함", "안함", None)] + [(f"1년에한번_{d}월", "연1회", d) for d in range(1, 13)]
+ [(f"한달에한번_{k}번째", "매월", k) for k in range(1, 16)]):
앞[이름] = 요약(m25, *돌리기(m25, 방식, 인자))
연25 = [앞[f"1년에한번_{d}월"]["최대낙폭"] for d in range(1, 13)]
월25 = [앞[f"한달에한번_{k}번째"]["최대낙폭"] for k in range(1, 16)]
print(f" 안함 최대낙폭 {앞['안함']['최대낙폭']}% ({앞['안함']['낙폭_바닥일']}) · 끝 주식비중 {앞['안함']['끝주식비중']}%")
print(f" 1년에 한 번 12달: {min(연25)} ~ {max(연25)}% (폭 {max(연25) - min(연25):.2f})")
print(f" 한 달에 한 번 15날: {min(월25)} ~ {max(월25)}% (폭 {max(월25) - min(월25):.2f})")
out["2025까지"] = {"안함": 앞["안함"], "1년에한번_낙폭범위": [min(연25), max(연25)],
"한달에한번_낙폭범위": [min(월25), max(월25)]}
print("\n⑦ 되돌리기 한 번 — 한 달에 한 번(첫 거래일) 계좌의 2026년 체결 기록")
r_, s_ = m["close_r"].values, m["close_s"].values
on = 점검일(m["date"], "매월", 1)
r_amt, s_amt = 원금 * 0.5, 원금 * 0.5
기록들 = []
대기 = False
for i in range(1, len(m)):
r_amt *= r_[i] / r_[i - 1]
s_amt *= s_[i] / s_[i - 1]
if 대기:
tot = r_amt + s_amt
d = tot * 0.5 - r_amt # + 면 주식을 산다 · − 면 판다
c = abs(d) * 편도 * 2.0
if m["date"].iloc[i].year == 2026:
기록들.append({"체결일": str(m["date"].iloc[i].date()), "체결전_주식": int(round(r_amt)),
"체결전_채권": int(round(s_amt)), "주식_사고판금액": int(round(d)),
"체결전_주식비중": round(r_amt / tot * 100, 1), "수수료": int(round(c))})
tot -= c
r_amt, s_amt = tot * 0.5, tot * 0.5
대기 = False
대기 = bool(on[i])
for g in 기록들:
방향 = "주식 팔고 채권 삼" if g["주식_사고판금액"] < 0 else "주식 사고 채권 팜"
print(f" {g['체결일']} 체결 전 주식 {g['체결전_주식']:>11,} · 채권 {g['체결전_채권']:>11,} "
f"(주식 {g['체결전_주식비중']}%) → {방향} {abs(g['주식_사고판금액']):,}원 · 수수료 {g['수수료']:,}원")
out["되돌리기_2026"] = 기록들
out["요약"] = {
"1년에한번_가장깊은달": lo, "1년에한번_가장얕은달": hi,
"1년에한번_낙폭범위": [연[lo]["최대낙폭"], 연[hi]["최대낙폭"]],
"한달에한번_낙폭범위": [min(mdds), max(mdds)],
"한달에한번_끝금액범위": [min(끝m), max(끝m)],
"1년에한번_끝금액범위": [min(끝s), max(끝s)],
}
with open(a.json, "w", encoding="utf-8") as f:
json.dump(out, f, ensure_ascii=False, indent=1)
print(f"\n→ {os.path.basename(a.json)}")
# 수치표 — 대본·화면에 들어가는 숫자는 전부 여기서 나온다(손으로 적지 않는다).
만 = lambda v: int(round(v / 10_000)) # noqa: E731
폭 = out["폭락"]["팔"]
표 = {
"설정": {"주식ETF": "KODEX 200 (069500)", "채권ETF": "KODEX 국고채3년 (114260)",
"목표비중_%": 50, "편도수수료_%": 0.1, "원금_만원": 만(원금),
"체결": "점검일 종가 신호 → 다음 거래일 종가", "매달_몇번째거래일_잰범위": [1, 15]},
"구간": {"시작": out["구간"][0], "끝": out["구간"][1], "연수": out["연수"], "거래일": out["봉"]},
"폭락": {"고점일": out["폭락"]["고점일"], "바닥일": out["폭락"]["바닥일"],
"KODEX200_하락_%": abs(out["폭락"]["KODEX200"]),
"그냥둠_고점날_주식비중_%": 폭["안함"]["고점날_주식비중"],
"매달_고점날_주식비중_%": 폭["한달에한번_첫거래일"]["고점날_주식비중"],
"1월_고점날_주식비중_%": 폭["1년에한번_1월"]["고점날_주식비중"],
"그냥둠_천만원이_만원": 만(폭["안함"]["천만원이"]),
"매달_천만원이_만원": 만(폭["한달에한번_첫거래일"]["천만원이"]),
"매일_천만원이_만원": 만(폭["매일"]["천만원이"]),
"1년에한번_달별_천만원이_만원": {f"{d}월": 만(폭[f"1년에한번_{d}월"]["천만원이"])
for d in range(1, 13)}},
"끝": {"그냥둠_끝금액_만원": 만(out["팔"]["안함"]["끝금액"]),
"매달_끝금액_만원": 만(out["팔"]["한달에한번_첫거래일"]["끝금액"]),
"그냥둠_끝주식비중_%": out["팔"]["안함"]["끝주식비중"],
"매달_수수료합_원": out["팔"]["한달에한번_첫거래일"]["수수료합"],
"매달_수수료합_만원_상한": int(np.ceil(out["팔"]["한달에한번_첫거래일"]["수수료합"] / 10_000)),
"매달_되돌린횟수": out["팔"]["한달에한번_첫거래일"]["되돌린횟수"]},
"매달_몇번째거래일": {"최대낙폭_범위_%": out["요약"]["한달에한번_낙폭범위"]},
"2025까지": out["2025까지"]["1년에한번_낙폭범위"] + out["2025까지"]["한달에한번_낙폭범위"],
"되돌리기_2026": [{"체결일": g["체결일"], "주식_사고판금액_원": g["주식_사고판금액"],
"체결전_주식비중_%": g["체결전_주식비중"]} for g in 기록들],
"대본용_기타": {"폭락까지_주": 3, "달": list(range(1, 13))},
}
with open(os.path.join(HERE, "수치표_banban.json"), "w", encoding="utf-8") as f:
json.dump(표, f, ensure_ascii=False, indent=1)
print("→ 수치표_banban.json")
if __name__ == "__main__":
main()
알고랩 Strategy Lab — 전략을 검증하고, 검증한 것만 자동매매로 옮깁니다. 주식이 한창 오를 때 매달 주식을 덜어 내는 일은 사람 손으로는 자꾸 미뤄집니다. 알고랩 자동매매 살펴보기 · 연금저축 주 1회 옮기기 · 전략 검증 전체 보기
과거 데이터를 돌려 본 결과이며 미래 수익을 보장하지 않습니다. 투자 판단과 책임은 본인에게 있습니다.