미국주식 자동매매 봇 만들기 · 14편 | 포트폴리오 설계
지난 몇 편에 걸쳐 “거래 빈도를 버려라”는 결론까지 왔습니다. 그런데 결론이 났다고 일이 끝나는 게 아닙니다. 실제 계좌에 있는 돈을 어떤 비율로, 무엇에, 어떤 주기로 나눠 담을지는 또 다른 문제입니다.
이번 편은 그 배분 설계를 다룹니다. 하나의 증권 계좌를 성격이 다른 여러 칸으로 쪼개고, 자본이 늘어나면 새 칸이 저절로 켜지도록 만든 구조입니다.
그리고 설계도만 보여주면 반쪽이라, 그 설계가 제 실제 계좌에서 어떻게 굴러가고 있는지를 나란히 놓았습니다. 결과부터 말하면 설계대로 되는 부분도 있고, 예상 못 한 방식으로 삐걱대는 부분도 있었습니다.
🧱 계좌 하나를 성격이 다른 세 칸으로 쪼갰다
배분의 기준을 수익률로 잡지 않았습니다. 매매 빈도로 잡았습니다. 앞선 검증에서 빈도가 비용을 결정하고 비용이 생사를 가른다는 걸 확인했으니, 칸을 나누는 축도 거기에 맞추는 게 일관됩니다.
모멘텀 코어 50% — 사서 들고 있기
방어 로테이션 30% — 월 1회 갈아타기
레버리지 추세 20% — 월 1회 노출 조절
세 칸 모두 가장 잦은 게 월 1회입니다. 하루에 몇 번씩 사고파는 칸은 하나도 없습니다. 각 칸을 왜 그 비중으로 뒀는지는 앞선 백테스트 숫자가 근거입니다.
표를 세로로 읽으면 배분 원리가 보입니다. 가장 성적이 좋은 칸에 가장 큰 비중을 준 게 아닙니다. 연 26.6%로 수익률이 가장 높은 레버리지 추세에는 20%만 줬고, 19.8%짜리 모멘텀 코어에 절반을 줬습니다. 기준은 낙폭입니다. 낙폭 −59.6%짜리를 절반 담으면 계좌 전체가 −30%씩 흔들리고, 그 구간을 사람이 못 버팁니다. 자동매매에서 제일 자주 깨지는 건 알고리즘이 아니라 그걸 지켜보는 사람 쪽입니다.
🪜 자본이 늘면 새 칸이 스스로 켜진다
소액 계좌의 진짜 제약은 수익률이 아니라 1주 단위입니다. $3,000을 여섯 칸으로 쪼개면 한 칸이 $500이고, 거기서 다시 20종목을 사려면 종목당 $25 — 대부분 1주도 못 삽니다. 그래서 칸의 개수를 계좌 크기에 연동시켰습니다. 설정 파일이 아니라 함수로 짜뒀기 때문에, 자본이 경계를 넘으면 다음 평가 때 새 칸이 자동으로 붙습니다.
def tier_for_capital(total_usd: float) -> dict:
if total_usd < 3000:
# 시드 이전 구간: 가장 저비용·저낙폭인 방어 로테이션 하나만
return {"gem": 1.00}
if total_usd < 5000:
return {"spmo": 0.50, "gem": 0.30, "trend": 0.20}
if total_usd < 10000:
return {"spmo": 0.40, "mtum": 0.10, "qual": 0.10,
"gem": 0.20, "trend": 0.20}
# $10,000+ : 6칸, 최대 분산
return {"spmo": 0.30, "mtum": 0.10, "qual": 0.10,
"clenow": 0.20, "tqqq_sma": 0.10,
"gem": 0.15, "trend": 0.05}
경계값을 저 세 개로 잡은 이유는 각각 다릅니다.
$3,000 — 한 칸에 ETF 1주가 의미 있어지는 선
세 칸으로 나누면 칸당 약 $600입니다. 이 정도면 ETF를 몇 주씩 담을 수 있어 목표 비중을 어지간히 맞출 수 있습니다. 이 아래에서는 칸을 늘려봐야 쓰지 못하고 노는 현금만 늘어납니다.
$5,000 — 코어의 쏠림을 희석하는 선
코어로 쓰는 모멘텀 ETF는 이름만 보면 S&P 500 분산 상품 같지만 실제로는 상당히 쏠려 있습니다. 최근 구성을 보면 상위 10종목이 자산의 52%, 정보기술 섹터 하나가 54%이고, 단일 종목 비중도 마이크론 10.1%, 엔비디아 8.2%, 브로드컴 6.9%로 큽니다. 보유 종목 수는 102개인데 실질 위험은 반도체 몇 개에 걸려 있는 셈입니다. 그래서 이 구간부터 성격이 다른 팩터 ETF 두 개를 붙여 쏠림을 희석합니다.
$10,000 — 다종목 랭킹 전략이 비로소 가능해지는 선
주식을 순위로 골라 25종목쯤 담는 방식은 $2,500 칸이 생겨야 종목당 $100이 됩니다. 그래야 S&P 500 대부분의 종목을 최소 1주씩 살 수 있습니다. 그 전까지는 아무리 좋은 전략이어도 실행 자체가 안 되므로 잠가둡니다.
📟 설계도 옆에 실제 계좌를 놓아봤다
여기까지가 설계도입니다. 실제 계좌는 어떨까요. 2026년 8월 6일 자 상태 파일을 그대로 옮기면 이렇습니다. 계좌 총액이 $3,107.68이니 두 번째 티어에 해당하고, 세 칸이 켜져 있습니다.
여기서 눈여겨볼 건 방어 칸이 미국 주식이 아니라 미국 외 주식(VEU)을 들고 있다는 점입니다. 이 칸은 미국 주식 · 미국 외 주식 · 채권 셋을 놓고 최근 12개월 성적을 비교해 매달 한 번 갈아타는 방식인데, 지난 신호일에 미국 외 주식이 미국을 앞섰다고 판정한 겁니다. 제가 어느 쪽이 낫다고 판단한 게 아니라 규칙이 그렇게 고른 것이고, 다음 달에 순서가 바뀌면 알아서 되돌아갑니다.
🟡 이 방식의 약점도 같이 알고 쓴다
이 방어 로테이션은 2014년부터 2022년 사이 S&P 500 매수보유에 뒤졌습니다. 이유는 분명합니다. 그 기간 미국 강세장이 얕은 조정만 반복해 방어 장치가 발동할 일이 거의 없었고, 정작 미국 외 주식으로 옮겨간 구간에서는 그쪽 성적이 미국보다 나빴습니다. 방향이 자주 바뀌는 구간에서 신호가 뒤집히며 손실이 쌓이는 약점도 잘 알려져 있습니다. 그래서 이 칸에는 30%만 줬습니다. 수익 담당이 아니라 보험 담당이라고 보면 정확합니다.
🔁 경계선에 걸린 계좌에서 티어가 아홉 번 튀었다
여기서 설계할 때 생각 못 한 일이 벌어졌습니다. 제 계좌 잔고가 하필 $3,000 경계선 바로 위에서 오르내리고 있었습니다. 티어 판정은 매일 돌고, 조건은 딱 한 줄 total_usd < 3000 입니다. 결과는 예상대로였습니다.
두 달 사이 아홉 번입니다. 계좌가 몇십 달러 움직였을 뿐인데 목표 배분이 "한 칸 100%"와 "세 칸 50·30·20"을 왕복한 겁니다. 원인은 명확합니다. 임계값이 딱 한 점이고 되돌아갈 때 여유를 두는 장치가 없기 때문입니다. 올라갈 때는 $3,000, 내려올 때는 $2,850처럼 문턱을 다르게 두면 이런 왕복은 대부분 사라집니다.
티어가 한 칸으로 떨어진 날에는 상태 파일의 이탈률이 −69%까지 찍혔습니다. 계산식은 (실제 − 목표) ÷ 목표인데, 티어가 떨어지는 순간 방어 칸의 목표가 계좌 전액으로 커지기 때문입니다. 실제로 돈이 빠져나간 게 아니라 분모가 세 배로 뛴 것이었습니다.
🛡️ 그런데 그 아홉 번이 만든 매매는 0건이었다
목표 배분이 아홉 번 뒤집혔으면 계좌도 아홉 번 뒤집혔어야 할 것 같은데, 실제 체결은 한 건도 없었습니다. 상태 파일의 세 칸 모두 마지막 리밸런스 날짜가 여전히 비어 있습니다. 목표를 다시 계산하는 일과 실제로 주문을 내는 일이 분리돼 있기 때문입니다.

🔁 다이어그램 요약: 목표에서 벗어난 걸 감지해도 곧바로 사고팔지 않는다 — 분기 마지막 거래일이면서 이탈이 10%를 넘을 때만 주문이 나가고, 둘 중 하나라도 아니면 아무것도 하지 않는다.
이 두 겹의 조건이 티어 진동을 통째로 흡수했습니다. 티어가 하루 만에 갔다가 돌아오면 분기 마지막 날에는 어차피 원래 자리에 있고, 그 사이 계산된 목표가 아무리 요동쳐도 체결로 이어지지 않으니 비용이 0입니다. 앞선 편에서 확인한 "빈도를 줄이면 비용에 면역이 된다"는 원리가 전략 안이 아니라 전략 바깥의 배분 계층에서 한 번 더 작동한 셈입니다.
참고로 자산 배분 쪽에는 이미 비슷한 발상의 관행이 있습니다. 목표 비중에서 절대 5%포인트 또는 상대 25% 중 더 좁은 쪽을 벗어날 때만 손대는 이른바 5/25 방식이 대표적입니다. 달력을 정해놓고 기계적으로 맞추는 방식과 밴드를 넘을 때만 손대는 방식 사이의 절충인데, 제 봇은 상대 10% 밴드에 분기말이라는 달력 조건을 곱한 형태라 둘 중 더 보수적인 쪽에 가깝습니다.
다만 이건 우연히 막힌 게 아니라는 점만 확인된 것이지, 설계가 옳았다는 증명은 아닙니다. 만약 리밸런스 조건이 "이탈 10% 이상"만이었다면 아홉 번의 왕복이 그대로 매매가 됐을 겁니다. 지금 구조에서는 달력 조건 하나가 안전장치 노릇을 하고 있는데, 안전이 부수 효과에 기대고 있다면 그건 아직 설계가 아닙니다.
🧭 이 구조에서 다음에 손볼 것
① 올라갈 때와 내려올 때 문턱을 다르게
지금은 $3,000 한 점입니다. 승격은 $3,000, 강등은 $2,850처럼 벌려두거나 "며칠 연속 미달일 때만 강등"을 붙이면 왕복이 사라집니다.
② 티어가 바뀔 때 이탈률을 다시 읽는 방법
목표가 통째로 바뀐 날의 −69%는 계산식대로의 값이지만 사람이 보면 사고로 읽힙니다. 티어 전환 직후에는 이탈률에 표시를 달거나 하루 유예를 주는 편이 낫습니다.
③ 설명과 계산식이 어긋난 부분 정리
코드 주석에는 이탈률이 "전체 자산 대비 퍼센트포인트"라고 적혀 있는데 실제 식은 각 칸의 목표 대비 비율입니다. 숫자는 맞지만 설명이 틀렸고, 이런 어긋남은 나중에 남이 이 코드를 읽을 때 그대로 사고가 됩니다.
📌 세 줄 정리
▶ 계좌를 수익률이 아니라 낙폭과 매매 빈도 기준으로 세 칸으로 나눴고, 칸의 개수는 자본이 $3,000·$5,000·$10,000을 넘을 때마다 자동으로 늘어난다.
▶ 계좌가 경계선에 걸려 있던 두 달 동안 티어가 아홉 번 왕복했다. 한 점짜리 임계값에 되돌림 여유가 없던 탓이다.
▶ 그런데도 실제 매매는 0건이었다. 목표 계산과 주문 실행을 분리하고 분기말·이탈 10%라는 두 겹 조건을 걸어둔 덕분이다.
▶ $1,000으로 26개 매매법을 돌려 찾은 답 — 각 칸의 수익률·낙폭 근거
▶ CAGR 36% 백테스트, 생존편향까지 직접 감사했다 — 레버리지 칸을 작게 둔 이유
▶ 수수료가 좋은 전략을 죽인 기록 — 인트라데이 칸이 사라진 경위
📚 참고 자료
▶ 모멘텀 ETF에 숨은 기술 섹터 쏠림 (Trefis)
▶ 이중 모멘텀 전략의 취약성 사례 분석 (Newfound Research)
▶ 5/25 리밸런싱 밴드 규칙 (A Wealth of Common Sense)