자동매매 종목 수, 3개와 20개로 10년 돌려 본 결과

2026-11-12 알고랩

같은 매매 신호를 두고 동시에 들고 가는 종목 수만 바꿔서 10년을 돌려 봤습니다. 한도가 3개일 때 최대 낙폭(MDD)은 -70.7%, 20개일 때는 -41.1%였습니다. 연 수익률(CAGR)은 3개가 -1.9%, 20개가 5.0%였습니다. 종목 수가 적으면 변동이 커진다는 건 예상한 대로였는데, 돌려 보고 나서 더 눈에 띈 건 다른 쪽이었습니다. 한도가 적으면 그날 뜬 신호 중에 무엇을 샀느냐에 따라 결과가 크게 갈립니다. 3개 한도에서 사는 순서를 무작위로 100번 바꿔 돌렸더니 연 수익률이 -13.8%에서 3.5%까지 퍼졌습니다.

제작 상담을 하다 보면 진입 조건과 청산 조건은 꼼꼼히 정해 오시는데 "신호가 열 개 뜨면 몇 개를 사나요, 무엇부터 사나요"는 비어 있는 경우가 있습니다. 프로그램은 이걸 정해 주지 않으면 만들 수 없어서 꼭 여쭙게 됩니다. 그 질문에 숫자를 붙여 두고 싶어서 돌렸습니다.

어떻게 돌렸나

매매 규칙은 손절 후 재진입 대기 실측에서 쓴 것과 같습니다. 종가가 직전 20거래일 고가를 넘으면 다음 날 시가에 사고, 진입가에서 5% 내려가면 손절하고, 종가가 직전 10거래일 저가를 깨면 다음 날 시가에 팝니다. 그때는 종목 하나하나를 따로 돌렸고 이번에는 계좌 하나에 묶었습니다.

종목기준일 시가총액 상위에서 2015년 이전에 상장한 국내 100종목 (KOSPI 95, KOSDAQ 5)
기간2016-01-04 ~ 2026-11-10, ASOF = "2026-11-10" 고정
데이터FinanceDataReader 일봉(수정주가), 배당 미포함
보유 한도동시에 최대 N종목. 살 때 한 종목에 전날 평가금액의 1/N
자리가 없을 때그날 신호는 버림. 다음 날로 미루지 않음
사는 순서최근 60거래일 수익률이 높은 종목부터 (뒤에서 바꿔 봅니다)
비용왕복 0.3% 가정 (수수료, 세금, 슬리피지를 뭉뚱그림)
환경Python 3.10, pandas 2.2.3, numpy 2.0.2, FinanceDataReader 0.9.202

미리 말씀드릴 게 있습니다. 종목을 지금 시가총액 순으로 골랐기 때문에 10년 동안 살아남아 커진 회사만 들어 있습니다. 수익률은 실제보다 좋게 나옵니다. 이 글의 숫자는 한도끼리 비교하는 데만 쓰고, 이 규칙으로 이만큼 번다는 뜻으로 읽지 말아 주세요. 상장폐지 종목을 빼면 결과가 얼마나 달라지는지는 생존 편향 글에 따로 적었습니다.

계좌에 묶는 부분은 이렇게 생겼습니다. 전체 코드는 max_positions_backtest.py로 받을 수 있습니다.

max_positions_backtest.py (일부)

for j in buys:                                   # 어제 고른 종목을 오늘 시가에 매수
    if not ok[t, j] or j in pos or len(pos) >= n_slots:
        continue
    amt = min(eq_prev / n_slots, cash)           # 한 종목에 전날 평가금액의 1/N
    pos[j] = [amt * (1 - COST / 2) / o[t, j], o[t, j], False, nsig]
    cash -= amt

# 장 마감 뒤: 오늘 돌파한 종목 중 빈자리만큼만 고른다
sig = [j for j in np.where(ok[t] & (c[t] > hi20[t]))[0] if j not in pos]
free = n_slots - sum(1 for p in pos.values() if not p[2])
sig.sort(key=lambda j: -np.nan_to_num(mom[t, j], nan=-9))
buys = sig[:max(free, 0)]

한도별 결과

slots   CAGR     MDD    vol   trades  win%   avg_held  full%  cash%  take%
   3   -1.9%  -70.7%  36.7%     507   23%      2.7    82%     9%     4%
   5    1.0%  -70.6%  31.3%     814   24%      4.5    73%    10%     6%
  10    2.4%  -58.3%  25.7%    1556   24%      8.7    54%    12%    13%
  20    5.0%  -41.1%  20.4%    2708   26%     16.1    29%    17%    29%
  40    7.4%  -32.6%  16.0%    4114   28%     25.5     4%    32%    67%
 100    4.0%  -16.4%   8.4%    4767   28%     29.6     0%    67%   100%

take%는 뜬 신호 중에 실제로 산 비율입니다. 한도가 3개면 10년 동안 뜬 신호의 4%만 샀습니다. 나머지 96%는 자리가 없어서 지나갔습니다. full%는 자리가 꽉 차 있던 날의 비율이고 3개 한도는 82%의 날이 만석이었습니다.

같은 기간 코스피 지수(KS11)는 CAGR 9.7%, MDD -44.9%였습니다. 어느 한도도 지수의 수익률을 넘지 못했습니다. 살아남은 종목만 넣었는데도 그렇습니다. 규칙이 좋은지를 따지려고 돌린 건 아니지만, 표를 보고 오해하실까 봐 적어 둡니다.

MDD는 3개와 5개가 -70%대로 거의 같고 10개부터 줄어듭니다. 저점은 네 한도 모두 2023년 5월이었습니다. 3개 한도는 2016년 3월 고점을 2023년 5월까지 회복하지 못했습니다. 7년 동안 계좌가 고점 아래에 있었다는 뜻입니다. 20개 한도는 고점이 2021년 6월이라 물려 있던 기간이 2년쯤입니다. 낙폭의 깊이만큼 이 기간 차이도 큽니다.

한도 40개와 100개는 표에서 MDD가 가장 작지만 사정이 다릅니다. 100종목에서 신호가 동시에 40개씩 뜨는 날은 거의 없어서 자리가 늘 비어 있습니다. 한도 100개는 평균 29.6종목을 들었고 계좌의 67%가 현금이었습니다. MDD -16.4%는 분산 덕이라기보다 돈을 3분의 1만 썼기 때문입니다. 한도를 정할 때는 평소에 신호가 몇 개쯤 떠 있는지를 먼저 세어 봐야 합니다. 이 규칙과 이 종목군에서는 20개 한도가 평균 16.1종목으로 자리를 거의 채웠습니다.

수익이 몇 건에 몰려 있었습니다

한도 100개는 뜬 신호를 전부 산 경우입니다. 거래는 4,767건이고 거래당 평균 수익은 1.00%였습니다. 여기서 수익이 큰 순으로 위에서 1%, 47건을 빼면 평균이 -0.29%로 내려갑니다. 승률이 28%인 추세 추종 규칙이라 놀랄 일은 아닙니다. 대부분은 작게 잃고 드물게 크게 버는 구조입니다.

문제는 한도가 적을 때입니다. 3개 한도는 신호의 4%만 삽니다. 4,767건 중에 그 47건이 들어오느냐는 그날 자리가 비어 있었느냐, 같이 뜬 다른 종목보다 순서가 앞이었느냐에 달려 있습니다. 규칙의 실력과는 상관없는 일입니다.

처음에는 다르게 짐작했습니다. 신호가 한꺼번에 많이 뜨는 날이 시장이 강한 날이고, 그런 날은 자리가 모자라서 좋은 신호를 많이 놓쳤을 거라고 봤습니다. 실제로 신호가 같이 뜬 수로 나누면 차이가 납니다.

same-day signals  trades  share   win%    exp
         1-2      1665    35%    26%   0.22%
         3-5      1960    41%    27%   0.44%
        6-10       917    19%    32%   3.40%
       11-20       202     4%    29%   1.20%
         21+        23     0%    26%   7.24%

신호가 6개 이상 같이 뜬 날의 거래가 거래당 수익이 높습니다(21개 이상은 23건뿐이라 믿기 어렵습니다). 그런데 한도별로 잡은 거래 중에 이런 날 거래가 차지하는 비중을 세어 보니 3개 한도 24%, 5개 22%, 10개 22%, 20개 20%, 100개 24%로 비슷했습니다. 한도가 적다고 좋은 날을 유독 놓친 건 아니었습니다. 제 짐작이 틀렸습니다. 남는 설명은 표본입니다. 500건을 뽑으면 그 47건 중 몇 건이 들어올지가 뽑을 때마다 크게 달라집니다.

무엇부터 사느냐

그래서 사는 순서를 바꿔 봤습니다. 최근 60거래일 수익률이 높은 순(mom), 낮은 순(weak), 돌파 폭이 큰 순(brk)입니다. 앞이 CAGR, 뒤가 MDD입니다.

slots      mom              weak             brk
   3   -1.9% / -70.7%   -5.4% / -74.9%    4.5% / -57.1%
   5    1.0% / -70.6%   -3.5% / -65.0%   -1.6% / -56.0%
  10    2.4% / -58.3%   -0.7% / -46.9%    2.9% / -47.3%
  20    5.0% / -41.1%    5.0% / -41.3%    6.3% / -39.6%

3개 한도에서는 돌파 폭 순이 4.5%로 가장 좋았는데 5개 한도에서는 같은 기준이 -1.6%로 가장 나쁜 쪽입니다. 한도가 하나 바뀔 때마다 순위가 뒤집히는 기준을 좋은 기준이라고 하기는 어렵습니다. 20개 한도로 가면 세 기준이 5.0%에서 6.3% 사이로 모입니다.

기준 없이 무작위로 고르게 하고 시드를 100개 돌리면 이 흩어짐이 얼마나 큰지 보입니다.

slots   CAGR 5% / 중앙 / 95% 분위        폭       MDD 중앙 / 최악
   3   -13.8% /  -5.0% /   3.5%     17.3%p    -74.0% / -88.7%
   5    -8.9% /  -4.7% /   3.9%     12.8%p    -67.6% / -82.5%
  10    -4.2% /   0.1% /   3.9%      8.1%p    -52.7% / -65.2%
  20     3.0% /   4.7% /   6.7%      3.7%p    -42.9% / -47.3%

3개 한도는 같은 규칙, 같은 종목, 같은 10년인데 운이 나쁜 5%는 해마다 13.8%씩 잃었고 운이 좋은 5%는 3.5%씩 벌었습니다. 20개 한도는 그 폭이 3.7%p입니다. 백테스트에서 한도 3개로 좋은 숫자가 나왔다면 그게 규칙 덕인지 순서 덕인지 가릴 방법이 마땅치 않습니다. 저라면 사는 순서를 몇 가지로 바꿔 돌려 보고, 결과가 크게 출렁이면 그 숫자는 믿지 않겠습니다. 같은 돌파 규칙에 진입 필터를 붙여 본 결과는 ADX 필터 실측에 있습니다.

늘린 만큼 줄지는 않습니다

한도를 3개에서 20개로 늘리면 연 변동성이 36.7%에서 20.4%로 줄었습니다. 종목들이 서로 무관하게 움직였다면 14%쯤까지 내려갔어야 합니다. 그만큼 안 내려간 건 종목들이 같이 움직이기 때문입니다. 100종목의 일간 수익률 상관계수를 쌍마다 구해 평균하면 전체 기간은 0.24입니다. 2020년 2월 20일부터 3월 19일까지는 0.61, 2024년 7월 11일부터 8월 5일까지는 0.53이었습니다. 빠질 때는 같이 빠집니다.

연도별로 보면 더 분명합니다. 2022년 수익률은 3개 한도 -29.0%, 5개 -43.1%, 10개 -38.2%, 20개 -28.2%였습니다. 종목 수를 늘려도 그해는 피하지 못했습니다. 종목 수로 줄일 수 있는 건 종목 하나가 사고를 치는 위험이고, 시장 전체가 내려가는 위험은 그대로 남습니다. 이 부분은 분산투자 몇 종목이면 되는지 계산한 글에서 길게 다뤘습니다. 시장 전체가 내릴 때를 대비하려면 지수가 200일선 아래일 때 신규 진입을 쉬는 식의 장치가 따로 있어야 하는데, 이번에는 넣지 않았습니다.

프로그램으로 만들 때 정해야 하는 것

백테스트에서는 한 줄이던 보유 한도가 실제 봇에서는 몇 가지 결정으로 나뉩니다.

우선 지금 몇 종목을 들고 있는지를 어디서 세느냐입니다. 봇이 기억하는 숫자만 믿으면 재시작하거나 사람이 HTS에서 손으로 판 날 어긋납니다. 주문 전에 계좌 잔고를 조회해서 셉니다. 한국투자증권 KIS API는 주식잔고조회(TTTC8434R), 키움 REST API는 계좌평가잔고내역요청(kt00018)이 그 용도입니다. 응답 형태는 키움 kt00018 글과 KIS 잔고조회 글에 있습니다.

잔고만 세면 한 가지를 놓칩니다. 매수 주문을 냈는데 아직 체결되지 않은 종목은 잔고에 안 나옵니다. 이걸 빼고 세면 빈자리가 실제보다 많아 보여서 한도를 넘겨 삽니다. 지정가로 주문하는 봇이면 생길 수 있는 문제입니다. 미체결 매수도 자리 하나로 쳐야 합니다. 키움은 미체결요청(ka10075)으로 조회합니다.

그다음이 이 글에서 본 것들입니다. 신호가 빈자리보다 많을 때 무엇부터 살지, 못 산 신호를 버릴지 다음 날까지 들고 갈지, 한 종목에 넣는 금액을 한도로 나눈 고정 비율로 할지 남은 현금 기준으로 할지를 정해야 합니다. 설정 파일로 빼면 이런 모양이 됩니다.

config.yaml (예시)

portfolio:
  max_positions: 20          # 동시 보유 한도
  slot_ratio: 0.05           # 한 종목에 평가금액의 5%
  count_pending_buys: true   # 미체결 매수도 자리로 센다
  signal_priority: momentum_60d
  carry_unfilled_signals: false

한 종목에 얼마를 넣을지, 호가 단위에 맞춰 수량을 어떻게 자를지는 주문 수량 계산 글에 적었습니다.

이 실측으로 말할 수 없는 것

규칙 하나, 종목군 하나로 돌린 결과입니다. 평균 회귀처럼 승률이 높고 거래당 수익이 고른 규칙이라면 수익이 몇 건에 몰리지 않으니 한도가 적어도 덜 흔들릴 수 있습니다. 그건 돌려 보지 않았습니다.

일봉으로 돌렸기 때문에 같은 날 손절가와 고가 중 어느 쪽에 먼저 닿았는지는 모릅니다. 진입한 날 저가가 손절가 아래면 그날 손절한 것으로 쳤습니다. 다음 날 시가에 원하는 수량이 전부 체결된다고 가정했고 거래정지일은 건너뛰었습니다. 한도가 1개일 때는 CAGR 10.6%에 MDD -77.8%가 나왔는데 거래가 160건뿐이라 표에서 뺐습니다.

기준일을 고정한 이유는 재현 때문입니다. ASOF 없이 돌리면 실행하는 날까지의 데이터를 받아서 이 글과 다른 숫자가 나옵니다. 최신 데이터로 보시려면 ASOF = None으로 바꾸면 됩니다.

자주 받는 질문

자동매매는 동시에 몇 종목을 들고 가는 게 좋나요?

정답인 숫자는 없습니다. 이 실측에서는 한도가 3개나 5개일 때 최대 낙폭이 -70%대였고 20개일 때 -41.1%였습니다. 한도가 적을수록 어떤 신호를 잡았느냐에 따라 결과가 크게 갈렸습니다. 추세 추종처럼 큰 거래 몇 건에 수익이 몰리는 규칙이라면 한도를 넉넉히 두는 쪽이 결과가 덜 흔들립니다. 다른 규칙이나 다른 종목군에서는 달라질 수 있습니다.

종목 수를 늘리면 MDD가 그만큼 줄어드나요?

줄어들지만 종목 수만큼은 아닙니다. 한도를 3개에서 20개로 늘렸을 때 연 변동성은 36.7%에서 20.4%로 줄었습니다. 종목들이 서로 무관하게 움직였다면 14%쯤까지 내려갔을 겁니다. 100종목의 평균 상관계수는 평소 0.24였고 2020년 3월 급락 구간에는 0.61이었습니다. 2022년에는 한도와 상관없이 전부 -28%에서 -43% 손실이었습니다.

신호가 한꺼번에 여러 개 뜨면 무엇부터 사야 하나요?

이 실측에서는 일관되게 이긴 기준이 없었습니다. 최근 60일 수익률이 높은 순, 낮은 순, 돌파 폭이 큰 순을 비교했는데 한도 3개에서는 연 수익률이 -5.4%에서 4.5%까지 벌어졌고 순위도 한도마다 바뀌었습니다. 한도 20개에서는 5.0%에서 6.3%로 차이가 작았습니다. 기준을 고르는 것보다 기준을 하나로 정해 두고 바꾸지 않는 것이 중요합니다.

종목 수와 자금 배분을 아직 못 정하셨다면

진입과 청산 규칙만 정해서 오셔도 됩니다. 한도를 바꿔 가며 먼저 돌려 보고 숫자를 같이 보면서 정합니다.

채팅으로 문의하기

2026년 11월 10일까지의 일봉으로 계산했습니다. 지금 살아 있는 시가총액 상위 종목만 넣은 결과라 수익률이 실제보다 높게 나옵니다. 배당과 실제 체결 조건은 반영하지 않았습니다. 과거 결과는 미래 수익을 보장하지 않으며 특정 종목이나 매매 방법을 권하는 글이 아닙니다. 알고랩은 프로그램 제작 도구를 제공하며 투자자문업과 투자일임업을 영위하지 않습니다.