기술적 분석 1장: 지표는 미래 수익률을 구분하는가?
기술적 분석에서 먼저 답해야 할 질문은 “이 지표가 유명한가?”가 아니라 “신호가 발생한 뒤의 수익률이 신호가 없을 때와 달라지는가?”입니다. 한 종목의 차트에서 그럴듯해 보이는 패턴만으로는 이 질문에 답할 수 없습니다.
이번 분석은 같은 표본과 같은 규칙으로 이동평균, RSI, MACD, 볼린저 밴드, 거래량, OBV 신호를 만들고, 신호 발생 뒤 5·20·60거래일 수익률을 비교합니다. 이 장에서는 분석에 사용할 데이터와 계산 규칙을 고정한 뒤, 전체 지표 결과를 먼저 훑어봅니다. 이후 각 장에서 지표를 하나씩 정의하고, 이 결과가 어떤 조건에서 유지되거나 무너지는지 확인합니다.
따라서 이 글의 결론은 “데이터를 공개했다”가 아닙니다. 어떤 신호가 이후 수익률과 더 강하게 연결됐고, 어떤 신호는 평균수익률만 좋아 보였는지를 구분하는 것이 이 장의 목적입니다.
이번 분석의 데이터 기준
Section titled “이번 분석의 데이터 기준”분석 기준일은 2026년 9월 4일입니다. 기준일 거래대금이 큰 일반 주식 300개를 표본으로 선정하고, 2024년 1월 1일부터 2026년 9월 4일까지의 일봉을 사용했습니다. ETF, ETN, 인버스, 레버리지, 채권형 상품은 제외했습니다.
| 항목 | 값 |
|---|---|
| 표본 종목 수 | 300개 |
| OHLCV 원자료 행 수 | 185,309개 |
| 120일 워밍업 이후 지표 행 수 | 150,419개 |
| 가격 데이터 기간 | 2024-01-01 ~ 2026-09-04 |
| 학습 구간 | 2024-01-01 ~ 2025-12-31 |
| 검증 구간 | 2026-01-01 ~ 2026-09-04 |
표본은 모든 상장 종목을 대표하지 않습니다. 거래대금 상위 종목을 사용했기 때문에 유동성이 낮은 종목의 체결 문제를 줄이는 대신, 대형주와 관심 종목에 유리한 표본 편향이 생깁니다. 이 한계를 숨기지 않고 원자료와 조건을 함께 공개합니다.
OHLCV를 읽고 수익률을 계산하기
Section titled “OHLCV를 읽고 수익률을 계산하기”import pandas as pd
df = pd.read_csv( "ohlcv.csv", dtype={"code": str}, parse_dates=["date"],)df = df.sort_values(["code", "date"])df["return_1d"] = df.groupby("code")["close"].pct_change()df["return_20d"] = df.groupby("code")["close"].pct_change(20)
print(df.shape)print(df[["code", "date", "close", "return_20d"]].tail())이 코드의 핵심은 종목별로 날짜를 정렬한 뒤 수익률을 계산하는 것입니다. 전체 데이터를 날짜만 기준으로 정렬하면 서로 다른 회사의 가격이 이어지는 오류가 생깁니다. 실제 지표 계산과 신호 정의는 공개한 Python 파일에서 재현할 수 있고, 이 장에서는 그 결과 파일을 같은 기준으로 읽어 비교합니다.
지표 신호와 20일 수익률의 관계
Section titled “지표 신호와 20일 수익률의 관계”먼저 중간 기간인 20거래일을 기준으로 비교합니다. 검증 구간은 2026년 1월 1일부터 9월 4일까지이며, 신호가 발생한 날의 종가부터 20거래일 뒤 종가까지의 수익률을 계산했습니다. 신호 당일의 수익률을 사용하지 않았기 때문에 신호 이후의 결과만 측정합니다.
import pandas as pd
results = pd.read_csv( "event_results.csv", encoding="utf-8-sig",)
validation_20d = ( results.query( "sample == '검증' and horizon_days == 20 " "and signal != '복합 신호 진입'" ) .assign( positive_rate=lambda x: x["positive_rate"] * 100, mean_return=lambda x: x["mean_return"] * 100, median_return=lambda x: x["median_return"] * 100, ) .loc[:, [ "signal", "events", "positive_rate", "mean_return", "median_return", ]] .sort_values("mean_return", ascending=False))
print(validation_20d.round(2).to_string(index=False))실행 결과는 다음과 같습니다.
| 신호 | 발생 횟수 | 양수 비율 | 평균 수익률 | 중앙값 수익률 |
|---|---|---|---|---|
| 거래량 돌파 | 2,726 | 53.4% | +9.75% | +2.51% |
| MACD 골든크로스 | 1,868 | 55.1% | +6.98% | +2.72% |
| OBV 확인 | 2,653 | 53.8% | +6.02% | +2.03% |
| 볼린저 하단 반등 | 1,015 | 49.4% | +4.94% | -0.27% |
| RSI 회복 | 1,258 | 47.1% | +2.14% | -0.91% |
결과에서 읽을 수 있는 것
Section titled “결과에서 읽을 수 있는 것”이 표에서 가장 먼저 보이는 것은 평균수익률만으로 신호의 질을 판단하면 안 된다는 점입니다.
- 거래량 돌파는 평균 수익률이 +9.75%로 가장 높고 중앙값도 +2.51%였습니다. 일부 대박 사례만으로 평균이 올라간 것이 아니라, 전형적인 신호에서도 수익률이 양수 쪽에 놓였다는 뜻입니다.
- MACD 골든크로스는 양수 비율이 55.1%로 가장 높고 중앙값도 +2.72%였습니다. 이번 검증 구간에서는 추세 전환 신호 중 결과의 방향성이 가장 안정적이었습니다.
- OBV 확인도 평균 +6.02%, 중앙값 +2.03%를 기록했습니다. 거래량 돌파와 함께 거래량을 확인하는 신호가 가격 반등만 보는 신호보다 일관된 결과를 보였다는 해석이 가능합니다.
- RSI 회복과 볼린저 하단 반등은 평균수익률은 플러스였지만 중앙값이 각각 -0.91%, -0.27%였습니다. 소수의 큰 상승 사례가 평균을 끌어올렸을 가능성이 있어, “과매도 후 반등”을 반복적으로 기대하기에는 근거가 약합니다.
다만 이 결과가 신호가 발생하면 무조건 매수해야 한다는 뜻은 아닙니다. 가장 높은 양수 비율도 55.1%에 그쳤고, 모든 신호의 하위 25% 수익률은 마이너스였습니다. 즉 기술적 지표는 방향을 맞히는 단독 예언 도구라기보다, 어떤 상황에서 상대적으로 유리한 후보를 좁히는 필터로 해석하는 편이 적절합니다.
또한 이 표는 신호별 사건을 독립적으로 집계한 결과입니다. 신호가 겹치는 종목을 하나의 포트폴리오로 운용했을 때의 거래비용, 보유 비중, 최대낙폭은 아직 반영하지 않았습니다. 그래서 다음 장부터는 개별 지표의 신호 정의를 확인하고, 마지막 장에서 실제 전략 성과와 리스크를 별도로 비교합니다.
이어서 확인할 질문
Section titled “이어서 확인할 질문”이번 결과는 다음 순서를 정하는 기준이 됩니다.
- 이동평균과 MACD처럼 추세를 따라가는 신호는 왜 상대적으로 안정적이었는가
- RSI와 볼린저 밴드의 반등 신호는 어떤 시장 국면에서 실패했는가
- 거래량 돌파와 OBV 확인은 같은 정보를 반복해서 보는 것인가, 서로 보완하는가
- 개별 신호의 관계가 실제 종목 스크리닝과 포트폴리오 백테스트에서도 유지되는가
다음 장에서는 이동평균선 신호를 전략으로 바꿔, 단순히 수익률이 높았다는 사실을 넘어 보유 시점과 최대낙폭까지 확인합니다.
원본은 OHLCV CSV, 종목 유니버스, 전체 데이터 설명에서 확인할 수 있습니다. 전체 재현 코드는 technical_analysis.py로 제공합니다.