주가 예측모델이 실전에서 실패하는 이유, 데이터 누출 방지 분할법

주가 예측 모델은 정확도보다 미래 정보가 학습 과정에 섞이지 않았는지 확인하는 것이 중요하며, 데이터 분할·전처리·Feature 생성·검증까지 시간 순서를 지켜야 신뢰할 수 있습니다. Random Split보다 TimeSeriesSplit이나 Walk-Forward처럼 실제 투자 환경에 가까운 검증 방식을 활용하고, 최종 Test 데이터는 모델 선택에 반복 사용하지 않는 것이 핵심입니다.
주가 예측 모델을 만들다 보면 이상할 정도로 결과가 잘 나오는 순간이 있습니다. 정확도도 높고, 백테스트 수익 곡선도 매끄럽습니다. 그런데 막상 새로운 데이터나 실제 시장에 적용하면 성능이 갑자기 떨어집니다. 이때 많은 분이 가장 먼저 생각하는 것은 비슷합니다. “모델이 너무 단순한 건가?”
“변수를 더 넣어야 하나?”
“LSTM 대신 Transformer를 써야 하나?”
하지만 문제는 모델 자체가 아니라 데이터를 나누고 전처리한 방법에 있을 수도 있습니다.
주가 데이터는 일반적인 이미지나 표 형태의 독립 데이터와 달리 시간의 순서가 존재하는 시계열 데이터입니다.
오늘까지 알고 있는 정보로 내일을 예측해야 하는데, 학습이나 검증 과정에서 자신도 모르게 미래 데이터가 조금이라도 들어갔다면 백테스트 결과는 실제 예측력을 제대로 보여주지 못할 수 있습니다.
이것이 바로 **데이터 누출(Data Leakage)**입니다.
Scikit-learn 공식 문서에서도 데이터 누출은 실제 예측 시점에는 사용할 수 없는 정보가 모델 구축 과정에 사용되는 경우를 의미하며, 이러한 누출은 평가 결과를 지나치게 낙관적으로 만들 수 있다고 설명합니다. 특히 전처리 과정에서 테스트 데이터를 fit에 사용하지 않아야 한다고 강조합니다. (scikit-learn.org)
따라서 주가 예측에서 중요한 질문은 단순히
“모델 정확도가 얼마나 높은가?”
가 아닙니다.
먼저 이렇게 물어야 합니다.
“이 모델은 정말 과거만 보고 미래를 예측했는가?”

랜덤 분할이 주가 예측에서 위험할 수 있는 이유
일반적인 머신러닝에서는 데이터를 무작위로 섞은 뒤 Train과 Test로 나누는 방법을 많이 사용합니다.
Scikit-learn의 train_test_split() 역시 기본 설정에서는 데이터를 섞은 뒤 Train/Test를 나누는 구조입니다. (scikit-learn.org)
일반적인 문제에서는 유용한 방법입니다.
하지만 과거에서 미래를 예측해야 하는 주가 데이터라면 상황이 달라집니다.
예를 들어 여러 해의 주가 데이터를 무작위로 섞으면 뒤쪽 시점 데이터가 Train에 들어가고 그보다 앞선 시점 데이터가 Test에 들어갈 수도 있습니다.
실제 투자에서는 과거 시점에 앉아서 이후 시장 데이터를 미리 확인할 수 없습니다.
그런데 모델 검증에서는 그런 상황이 만들어질 수 있는 것입니다.
결과적으로 테스트 점수가 높더라도
실제 미래 예측 환경을 제대로 재현한 점수인지 다시 확인해야 합니다.
데이터 누출은 정답을 변수에 넣어야만 발생하는 게 아닙니다
많은 분이 데이터 누출이라고 하면 미래 종가를 실수로 Feature에 넣는 상황만 떠올립니다.
하지만 실제로는 훨씬 사소한 부분에서 발생할 수 있습니다.
대표적인 것이 정규화 순서입니다.
예를 들어 전체 기간에 먼저 StandardScaler.fit_transform()을 적용하고 그다음 Train/Test를 나눴다고 생각해보겠습니다.
이 경우 정규화에 필요한 평균과 표준편차를 계산할 때 테스트 기간 데이터도 함께 사용됩니다.
미래 가격 자체를 직접 Feature로 넣은 것은 아니지만 미래 데이터의 분포 정보가 전처리에 반영된 것입니다.
Scikit-learn 공식 가이드 역시 데이터를 먼저 Train/Test로 분리한 뒤, 전처리의 fit은 Train 데이터에만 수행하고 Test 데이터에는 이미 학습된 변환기의 transform만 적용하도록 권고합니다. StandardScaler뿐 아니라 결측치 처리, PCA, Feature Selection 등에서도 같은 원칙이 적용됩니다. (scikit-learn.org)
따라서 흐름은 이렇게 가져가는 편이 좋습니다.
주의가 필요한 방식
전체 데이터
→ Scaling
→ Train/Test 분리
→ 모델 학습
기본적으로 권장되는 방식
시간순 Train/Test 분리
→ Train 데이터로 Scaler 학습
→ Train 변환
→ 동일한 Scaler로 Test 변환
→ 모델 학습 및 평가
순서 하나가 별것 아닌 것처럼 보여도 백테스트에서는 큰 차이를 만들 수 있습니다.
이동평균이나 RSI를 사용하면 모두 누출일까?
그렇지는 않습니다.
오늘까지 확인할 수 있는 과거 가격만 이용해 이동평균이나 RSI를 계산했다면 지표를 사용했다는 사실 자체가 데이터 누출을 의미하지는 않습니다.
문제는 계산 시점입니다.
예를 들어 중심 이동평균처럼 현재 시점 앞뒤 데이터를 함께 사용하는 계산 방법을 사용하거나, 미래 데이터를 이용한 보간이 이뤄지거나, shift()를 반대로 적용해 미래 수익률이 현재 Feature에 연결되면 문제가 발생할 수 있습니다.
그래서 모든 변수마다 한 가지 질문을 던져보는 것이 좋습니다.
“실제로 이 날짜에 매매 결정을 내렸다면 이 값을 이미 알고 있었을까?”
답이 명확하지 않은 변수는 다시 확인해야 합니다.
Feature Selection에서도 미래 정보가 새어 나올 수 있습니다
변수 선택도 마찬가지입니다.
전체 데이터에서 타깃과 상관관계가 높은 변수만 골라놓고 그다음 Train/Test를 분리한다면 테스트 데이터의 정보가 어떤 변수를 선택할지 결정하는 과정에 이미 영향을 미쳤을 수 있습니다.
Scikit-learn은 데이터 누출 예제에서 무작위로 생성한 데이터임에도 전체 데이터를 이용해 Feature Selection을 먼저 수행할 경우 예상보다 높은 평가 결과가 나타날 수 있음을 보여줍니다. (scikit-learn.org)
따라서 모델만 Train 데이터에서 학습하는 것으로 끝나지 않습니다.
Scaler, PCA, 결측값 처리, 변수 선택처럼 데이터로부터 어떤 값을 학습하는 과정도 Train 영역 안에서 이루어져야 합니다.
이런 이유로 Pipeline 구조를 활용하면 교차검증 과정에서 전처리와 모델 학습을 묶어 관리하는 데 도움이 됩니다. (scikit-learn.org)
주가 예측에서는 TimeSeriesSplit을 어떻게 사용할까?
주가 예측에서는 시간이 과거에서 미래 방향으로 흘러가는 검증 구조가 필요합니다.
대표적인 방법이 TimeSeriesSplit입니다.
개념적으로 보면 이런 구조입니다.
1차 과거 → 다음 기간 검증
2차 더 길어진 과거 → 그다음 기간 검증
3차 다시 길어진 과거 → 이후 기간 검증
Scikit-learn의 TimeSeriesSplit은 일반적인 교차검증과 달리 각 분할에서 과거 영역을 Train으로, 그 이후 영역을 Test로 사용하도록 설계되어 있으며, max_train_size, test_size, gap 같은 설정도 지원합니다. (scikit-learn.org)
결국 모델에게 묻는 질문이 달라지는 것입니다. 랜덤 분할은 “섞여 있는 데이터 중 일부를 얼마나 잘 맞힐 수 있느냐?” 를 보는 성격이 강합니다.
시계열 분할에서는 “현재까지 알고 있는 것만으로 다음 구간을 얼마나 잘 예측하느냐?” 를 확인합니다. 주가 모델을 실제로 사용하려는 사람에게 더 중요한 질문은 후자에 가깝습니다.
조금 더 실전에 가까운 Walk-Forward Validation
실제 운용 환경을 보다 직접적으로 재현하려면 Walk-Forward Validation을 고려할 수 있습니다.
과거 일정 기간을 이용해 모델을 학습하고 바로 다음 기간을 예측합니다.
그리고 시간이 지나 데이터가 새로 들어오면 다시 학습해 그다음 기간을 예측합니다.
이 과정을 계속 반복합니다.
대표적으로 두 가지 형태가 있습니다.
Expanding Window
과거 데이터를 계속 누적합니다. 처음 사용했던 학습 데이터를 유지하면서 새로운 데이터를 추가해 모델을 다시 학습합니다.
Rolling Window
최근 일정 기간만 사용합니다. 새로운 데이터가 추가되면 오래된 데이터를 제외하면서 학습 구간의 길이를 일정하게 유지합니다.
어떤 방식이 더 유리한지는 데이터와 전략에 따라 달라질 수 있습니다.
오래된 시장 데이터가 여전히 의미가 있다면 Expanding 구조가 유용할 수 있지만 시장 체제가 빠르게 변한다면 최근 데이터를 더 중요하게 보는 Rolling 구조가 적절할 수도 있습니다.
다만 학습 기간을 지나치게 짧게 잡으면 데이터가 부족해 모델의 변동성이 커질 수 있습니다.
따라서 학습 기간도 하나의 중요한 모델 가정으로 봐야 합니다.
예측 기간이 겹친다면 gap도 확인해야 합니다
하루 뒤 가격이 아니라 향후 여러 거래일의 누적수익률을 예측한다고 생각해보겠습니다.
이 경우 Train의 마지막 Sample이 참조하는 미래 기간과 Test 구간이 서로 겹칠 수 있습니다.
시간순으로 데이터를 나눴다는 이유만으로 정보가 완전히 분리됐다고 판단하기 어려운 상황입니다.
이럴 때 Train과 Test 사이에 일정한 간격을 두는 방법을 검토할 수 있습니다.
Scikit-learn의 TimeSeriesSplit에서도 Train 구간의 끝부분과 Test 구간 사이에서 일정 Sample을 제외할 수 있도록 gap 옵션을 지원합니다. (scikit-learn.org)
중요한 것은 gap을 임의로 크게 설정하는 것이 아닙니다.
내 타깃이 미래 몇 거래일까지 참조하는지 확인한 뒤 그 구조에 맞게 분리해야 합니다.
Random Split vs TimeSeriesSplit vs Walk-Forward
| 검증 방법 | 특징 | 장점 | 주가 예측에서 주의할 점 |
|---|---|---|---|
| Random Split | 무작위 Train/Test 분리 | 빠르고 구현이 간단함 | 미래 시점 데이터가 Train에 들어갈 수 있음 |
| TimeSeriesSplit | 과거 → 미래 순차 검증 | 여러 시점 성능 확인 가능 | 실제 재학습 구조와 차이가 있을 수 있음 |
| Walk-Forward | 시간 이동에 따라 반복 학습·검증 | 실제 운용 환경을 비교적 잘 재현 | 계산량과 설계 복잡도가 증가 |
| Walk-Forward + Gap/Embargo | 학습·검증 사이 일부 영역 제외 | 중첩된 정보 영향 완화에 도움 | 예측 Horizon에 맞게 설정 필요 |
어떤 방법이 모든 상황에서 가장 좋다고 단정할 수는 없습니다.
중요한 판단 기준은 이것입니다.
“실제로 투자할 때와 비슷한 조건으로 모델을 시험하고 있는가?”
Test 데이터도 반복해서 보면 Test가 아닙니다
여기에서도 많이 놓치는 문제가 있습니다.
Train과 Test를 제대로 분리했다고 가정해보겠습니다.
모델을 돌립니다.
Test 결과를 봤는데 마음에 들지 않습니다.
파라미터를 바꿉니다.
다시 Test 결과를 봅니다.
변수를 추가합니다.
또 Test 결과를 봅니다.
그리고 Test에서 가장 잘 나온 모델을 선택합니다.
Test 데이터를 모델 학습에 직접 사용하지는 않았습니다.
하지만 Test 결과를 보고 모델을 계속 수정했다면 그 데이터가 모델 선택에 영향을 준 것입니다.
Scikit-learn의 교차검증 문서에서도 테스트 세트를 반복적으로 참고해 하이퍼파라미터를 조정하면 테스트 정보가 모델 선택 과정으로 누출돼 일반화 성능 평가를 왜곡할 수 있다고 설명합니다. (scikit-learn.org)
그래서 중요한 모델이라면 데이터 역할을 나눠보는 것이 좋습니다.
Train
모델 학습
Validation 또는 Time-Series CV
Feature, 하이퍼파라미터, 모델 선택
Final Out-of-Sample Test
모든 선택이 끝난 뒤 마지막 성능 확인
마지막 Test를 확인한 후 다시 모델을 수정하고 같은 Test에서 재평가하기 시작하면 그 구간 역시 더 이상 완전히 새로운 데이터라고 보기 어려워집니다.
백테스트가 너무 잘 나오면 오히려 무엇부터 확인해야 할까?
백테스트가 잘 나왔다고 잘못된 모델이라는 뜻은 아닙니다.
하지만 예상보다 지나치게 좋은 결과가 나온다면 다음 순서대로 확인해볼 가치가 있습니다.
첫째, 시간 순서
Train보다 Test가 실제 시간상 뒤에 있는가?
둘째, 전처리
Scaler와 PCA, 결측값 처리, Feature Selection을 Train에만 fit했는가?
셋째, Feature 생성
Rolling, Shift, 보간 과정에서 미래 값이 섞이지 않았는가?
넷째, Target 생성
미래 여러 기간을 참조하는 Target과 Train/Test 구간이 겹치지 않는가?
다섯째, 하이퍼파라미터 튜닝
최종 Test 결과를 반복해서 확인하면서 모델을 선택하지 않았는가?
여섯째, 체결 시점
종가를 확인한 뒤 만든 신호로 같은 종가에 이미 체결했다고 가정하고 있지는 않은가?
여기까지 확인했는데도 성능이 유지된다면 그때부터 모델 자체의 예측력을 조금 더 진지하게 평가할 수 있습니다.
LSTM을 사용하면 데이터 누출에서 안전할까?
아닙니다.
LSTM은 시계열 구조를 학습할 수 있는 모델이지만 데이터가 어떻게 만들어졌는지까지 자동으로 판단해주지는 않습니다.
Transformer도 마찬가지이고 XGBoost, Random Forest도 마찬가지입니다.
미래 정보가 Feature나 전처리에 들어갔다면 어떤 알고리즘을 사용하든 문제가 발생할 수 있습니다.
따라서
“시계열 모델을 사용했다”
와
“시계열에 맞게 검증했다”
는 전혀 다른 이야기입니다.
실제 연구에서도 정보 누출 문제가 확인되고 있습니다
2026년 공개된 Mir와 Shrestha의 금융 머신러닝 연구에서는 전체 데이터 기반 Scaling과 전역적으로 계산한 Rolling Statistics 등을 정보 누출 사례로 설정하고, 단일 Train/Test 방식과 Walk-Forward, Embargo를 결합한 검증 방식의 차이를 비교했습니다.
해당 연구는 하나의 자산과 특정 딥러닝 모델을 대상으로 했기 때문에 결과 수치를 전체 시장이나 모든 모델에 일반화해서는 안 됩니다.
다만 중요한 시사점은 분명합니다.
정보 누출 여부와 검증 구조에 따라 백테스트 성과 평가가 크게 달라질 수 있다는 점입니다. (papers.ssrn.com)
즉 하나의 백테스트 숫자만 보고 모델의 실전 가치를 판단하기보다 여러 시간 구간과 보다 엄격한 검증 구조에서 결과가 얼마나 유지되는지 확인하는 과정이 필요합니다.
검증을 제대로 했더니 성능이 떨어졌다면 실패일까?
오히려 중요한 사실을 발견한 것일 수 있습니다.
랜덤 분할에서는 굉장히 잘 나왔는데 TimeSeriesSplit으로 바꾸자 성능이 떨어질 수 있습니다.
Walk-Forward까지 적용하니 더 낮아질 수도 있습니다.
당황스러운 결과지만 이것이 꼭 나쁜 것은 아닙니다.
실전 투자에 들어간 뒤 발견할 문제를 백테스트 단계에서 먼저 발견한 것이기 때문입니다.
물론 성능이 낮아진 모든 원인을 데이터 누출로 해석해서는 안 됩니다.
시장 환경 변화, 과최적화, 거래비용, 슬리피지, 종목 선택 편향, 데이터 품질, 낮은 신호대잡음비 등 여러 문제가 영향을 줄 수 있습니다.
그래서 검증은 점수를 높이기 위한 작업이 아니라
모델이 어디에서 무너지는지를 찾아내는 작업에 가깝습니다.
정확도가 높다고 좋은 투자 모델인 것도 아닙니다
주가 예측에서 Accuracy 하나만 보고 모델을 선택하는 것도 주의해야 합니다.
실제 매매로 연결한다면 기간별 성능 안정성뿐 아니라 거래 횟수, 손실 구간, 최대 낙폭, 거래비용, 슬리피지, 회전율, 체결 가능성 등을 함께 검토해야 합니다.
예측 모델은 방향을 맞혔는데 수익성이 없을 수도 있고, 정확도는 상대적으로 낮지만 중요한 구간에서 손익비가 더 나을 수도 있습니다.
결국 머신러닝 모델 성능과 실제 투자 전략의 성능은 같은 개념이 아닙니다.
그렇다면 어떤 검증 방식을 선택해야 할까?
복잡하게 생각하기 전에 이 질문부터 해보면 됩니다.
“실제 투자하는 날에도 이 데이터와 이 학습 방식이 가능했는가?”
과거를 이용해 미래를 예측한다면 시간순 분할이 기본입니다.
초기 모델 개발과 비교 단계에서는 TimeSeriesSplit을 활용할 수 있습니다.
실제 운용하면서 일정 주기로 재학습할 계획이라면 Walk-Forward 구조를 검토해볼 수 있습니다.
예측 Horizon 때문에 정보 범위가 겹친다면 Gap, Purging, Embargo 같은 추가적인 분리 방법도 살펴볼 필요가 있습니다.
그리고 어떤 분할 방법을 선택하더라도
Scaler, 변수 선택, PCA, 하이퍼파라미터 튜닝까지 검증 데이터와 분리하는 것이 핵심입니다.
좋은 모델보다 먼저 필요한 것은 믿을 수 있는 검증입니다
주가 예측 모델을 만들다 보면 더 복잡한 알고리즘을 계속 찾게 됩니다.
LSTM에서 Transformer로 넘어가고, 새로운 기술적 지표를 추가하고, Feature를 수십 개씩 늘립니다.
하지만 그전에 확인해야 할 것이 있습니다.
지금 보고 있는 백테스트 결과를 정말 믿어도 되는가?
백테스트 성능이 조금 낮아지더라도 데이터 누출 가능성을 줄이고 실제 시간 흐름에 맞춰 검증한 결과라면 오히려 모델의 현실적인 한계를 더 정확하게 보여줄 수 있습니다.
좋아 보이는 숫자가 목적이 아닙니다.
앞으로 들어올 완전히 새로운 데이터에서도 모델이 버틸 수 있는지 확인하는 것이 검증의 목적입니다.
따라서 자신의 모델을 점검할 때는 모델 종류부터 바꾸기보다
데이터 분할 → 전처리 → Feature/Target 생성 → 시계열 검증 → 최종 Out-of-Sample Test
순서로 다시 확인해보시길 권합니다.
내 모델도 데이터 누출이 있는지 모르겠다면
코드는 정상적으로 실행되는데 백테스트와 실전 성능 차이가 너무 크거나,
TimeSeriesSplit을 적용했지만 제대로 분리한 것인지 확신이 없거나,
Rolling·Shift·Scaler·Target 생성 과정에서 미래 데이터가 들어가는지 헷갈린다면 모델 정확도만 보기보다 전체 데이터 파이프라인을 시간 순서대로 점검하는 것이 먼저입니다.
특히 누출은 에러 메시지를 만들지 않습니다.
오히려 모델 성능을 더 좋아 보이게 만들 수 있기 때문에 발견하기 어렵습니다.
무작정 더 복잡한 모델로 바꾸기보다 현재 사용 중인 데이터와 검증 구조부터 확인해보는 것이 시행착오를 줄이는 출발점이 될 수 있습니다.
주가 예측모델이 실전에서 실패하는 이유, 데이터 누출 방지 분할법
마지막 체크리스트
내 모델을 다시 열어보고 아래 질문에 답해보세요.
- Train보다 Validation과 Test가 시간상 뒤에 있는가?
- 전체 데이터를 먼저 Scaling하지 않았는가?
- Feature Selection에 Validation·Test 정보가 들어가지 않았는가?
- Rolling Feature는 현재와 과거 정보만 사용하는가?
- Target 생성 과정에서 날짜가 한 칸씩 밀리지 않았는가?
- 예측 Horizon과 Train/Test 데이터 범위가 겹치지 않는가?
- 하이퍼파라미터를 고를 때 Final Test를 반복해서 보지 않았는가?
- 백테스트 체결 가격을 실제 해당 시점에 사용할 수 있었는가?
- 거래비용과 슬리피지를 고려했는가?
- 특정 기간에서만 우연히 잘 작동하는 모델은 아닌가?
한두 항목이라도 정확히 답하기 어렵다면 모델을 폐기할 필요가 있다는 뜻은 아닙니다.
다만 성과 숫자를 믿기 전에 검증 구조를 한 번 더 확인할 이유는 충분합니다.
좋은 주가 예측 모델은 미래를 완벽하게 맞히는 모델이 아니라,
미래를 미리 보지 않은 상태에서도 새로운 데이터에서 성능을 검증할 수 있는 모델에서 시작합니다.
참고 자료
Scikit-learn, Common pitfalls and recommended practices. 데이터 누출과 전처리 시 Train/Test 분리에 관한 공식 가이드. (scikit-learn.org)
Scikit-learn, Cross-validation: evaluating estimator performance. 테스트 세트 과적합과 시계열 교차검증에 관한 공식 문서. (scikit-learn.org)
Scikit-learn, TimeSeriesSplit. max_train_size, test_size, gap 등을 포함한 시계열 분할 방법 공식 문서. (scikit-learn.org)
Mir, Zulfiqar Ali & Shrestha, Dipesh, Quantifying Backtest Overfitting from Information Leakage: A Walk-Forward and Embargo-Based Diagnostic Framework Applied to Deep Learning Return Forecasts, 2026. 특정 자산과 모델을 사용한 개별 연구이므로 결과의 일반화보다는 검증 구조 참고 목적으로 활용하는 것이 적절합니다. (papers.ssrn.com)
금알남
댓글 0
첫 댓글을 남겨보세요.