본문으로 건너뛰기
JSL107's Tech Note
돌아가기

자동 파라미터 탐색 이후에 필요한 검증

7분 분량이 글 고치기
목차 · 6

백테스트 조건이 바뀌면 기존 최적값을 뒷받침하던 근거도 사라져요. 자동 탐색으로 새 후보를 찾은 뒤에도 비용과 체결에 관한 가설은 따로 검증해야 했어요.

손절 조건이 바뀌자 기존 최적값의 근거도 사라졌다

백테스트에 장중 손절을 반영하자 기존 청산 밴드 +10/-5의 근거가 흔들렸어요. 과거 조건에서 고른 최적값이 현재 조건에서도 최적이라고 보장할 수 없었기 때문이에요.

영향은 작지 않았어요. 다른 조건은 그대로 둔 채 장중 손절 여부만 바꿨는데도 최종 수익률이 -46.23%에서 +59.04%로 벌어졌어요. 운영 중인 값은 현재의 최적값이 아니라, 정확히 말하면 “당시 조건에서의 최적값”이었어요.

재측정 방식에도 문제가 있었어요. 이전 탐색에서는 63회를 수동으로 실행했지만 데이터 왜곡을 발견해 결과를 모두 폐기했어요. 이후 252회를 다시 실행해 결론을 얻었으나 백테스트의 체결 조건이 바뀌면서 이 결과도 전제를 잃었어요. 청산 밴드만 다시 측정하더라도 과거 방식대로라면 수동 재생을 28회 반복해야 했어요.

표본 밖 구간을 한 번만 열어 확인하는 검증 방식도 매주 반복하는 탐색에는 맞지 않았어요. 같은 검증 구간을 되풀이해 사용하면 더는 표본 밖 데이터라고 보기 어렵고, 시간이 지날수록 그 구간에 맞는 값이 선택될 가능성도 커져요.

결국 값을 다시 고르기에 앞서, 조건이 바뀔 때마다 같은 절차를 재현할 수 있도록 탐색 과정부터 자동화해야 했어요.

walk-forward 탐색을 CLI로 자동화했다

청산 밴드, 거래대금 하한, 종목당 비중 같은 전략 파라미터를 자동으로 순회하는 파라미터 탐색 CLI를 구현했어요.

탐색에는 walk-forward 방식을 적용했어요. 각 창에서 후보 파라미터를 재생해 성능 순위를 종합하고, 앞선 창들에서 선택한 값이 다음 창에서도 현행값을 이기는지 판정해요. 고정된 표본 밖 구간을 계속 재사용하지 않고 시간 창을 앞으로 옮기면서 선택과 검증을 반복하는 구조예요.

이 방식으로 수동 실행 횟수를 줄이면서 특정 검증 구간을 반복해서 들여다보다가 값이 그 구간에 맞춰질 위험도 낮췄어요.

탐색기는 운영 값을 직접 수정하지 않아요. 현재 적용 중인 파라미터 데이터는 읽기만 하고 후보와 판정 결과만 제시해요. 자동 탐색과 실제 값 변경 사이에 검토 단계를 둬서 탐색 결과가 곧바로 운영 변경으로 이어지지 않게 했어요.

첫 실행에서는 장중 손절을 반영한 조건으로 청산 밴드를 다시 측정했어요. 그 결과 중기 매매 전략의 선택 결과가 기존 값에서 뒤집혔고, 새로운 후보로 +30/-0.2가 제안됐어요.

하지만 자동 탐색이 제안한 값이라고 해서 곧바로 채택할 수는 없었어요. 이 후보가 현행값보다 훨씬 많은 거래를 일으켰기 때문이에요.

슬리피지를 모델링하는 대신 손익분기를 계산했다

중기 매매 전략의 후보 +30/-0.2는 창마다 657회 체결됐어요. 현행값의 356회와 비교하면 약 1.9배예요.

백테스트에는 기존 비용 계산기를 통해 수수료와 거래세가 반영돼 있었지만 슬리피지는 빠져 있었어요. 회전율이 높은 후보일수록 누락된 비용만큼 이익이 더 크게 잡혀요. 탐색기의 성능 지표에서 후보가 우수하더라도 실제 체결 비용을 반영했을 때 순위가 뒤집힐 가능성은 확인해야 했어요.

그렇다고 별도의 슬리피지 모델을 추가하지는 않았어요. 임의로 만든 체결 모델이 실제 시장을 제대로 재현하는지 검증하기 어렵고, 향후 실거래 체결 로직과 별개인 또 하나의 체결 구현을 만들게 되기 때문이에요.

대신 질문을 바꿨어요.

슬리피지가 얼마까지 커지면 새 제안과 현행값의 순위가 뒤집히는가?

기존 탐색에서 얻은 980회 원자료를 다시 집계해 순위가 역전되는 슬리피지 손익분기를 구한 뒤, 실제 호가 단위에서 관측한 스프레드와 비교했어요. 새로운 백테스트 재생은 하지 않았어요.

계산해 보니 제안의 순위가 뒤집히는 손익분기는 55.6bp였고 실측 스프레드는 6.4bp였어요. 회전 비용을 고려해야 한다는 가설 자체는 타당했지만, 관측한 스프레드 수준으로는 새 제안의 우위를 뒤집지 못했어요.

이 과정에서 탐색기의 한계도 드러났어요. 현재 판정 지표만으로는 회전 비용을 구조적으로 평가할 수 없어요. 이번에는 별도의 손익분기 분석으로 보완했지만, 거래 횟수가 크게 다른 후보를 비교할 때는 성능 순위만 보고 결론을 내려서는 안 돼요.

슬리피지는 채택을 막을 요인이 아니었어요. 대신 더 얇은 손절 폭이 새로운 의문으로 남았어요.

-0.2% 손절의 1.6틱 우려를 실제 발동일로 검증했다

실측한 1틱의 중앙값은 주가 대비 0.127%였어요. -0.2% 손절선은 약 1.6틱에 불과했어요.

백테스트는 일봉 저가가 손절선을 통과했는지를 기준으로 장중 손절 발동 여부를 판단해요. 일봉 저가는 단 한 번의 낮은 체결만으로도 기록될 수 있어요. 이 때문에 1.6틱 정도로 얇은 손절선은 정상적인 가격 움직임이 아니라 한 틱만 스쳐도 거의 항상 발동한 것처럼 재생될 수 있다는 우려가 생겼어요.

이 가설을 검증하려고 손절이 실제로 발동한 날의 저가와 손절선 사이 간격을 확인했어요. 1틱 스침이 원인이었다면 저가가 손절선 바로 아래에 몰려 있어야 했어요.

실제 결과는 반대였어요. 발동일 저가는 손절선보다 5.58~7.59%p 아래까지 내려가 있었어요. 손절선 주변을 한 틱 스쳐 발동한 사례가 아니라 가격이 손절선을 큰 폭으로 통과한 날들이었어요.

관측 데이터를 보면 “-0.2%는 1.6틱에 불과하므로 한 틱 스침 때문에 발동한다”는 우려는 기각할 수 있었어요.

잘못된 반올림 계획은 구현 전에 철회했다

처음에는 백테스트 체결가를 실제 호가 단위에 맞춰 반올림하는 방안도 검토했어요. 하지만 구현에 앞서 체결가로 사용하는 조정 시가 값의 가격 계열을 확인해 보니 전제 자체가 성립하지 않았어요.

재생에 쓰는 값은 실제 주문 가격과 같은 원시 가격이 아니라 조정 계열이었어요. 일부 구간을 실측한 결과 원 단위 정수인 비율은 100%였지만, 호가 단위의 배수인 비율은 99.26%였어요. 조정 가격에 실제 시장의 호가 규칙을 그대로 적용하면 서로 다른 가격 체계가 뒤섞여요.

결국 체결가 반올림은 정확도를 높이기는커녕 새로운 왜곡을 만들 수 있었어요. 이 계획은 코드로 옮기지 않고 철회했어요.

자동 제안보다 가설 검증이 먼저였다

수동으로 반복하던 파라미터 탐색을 walk-forward CLI로 옮겨서 조건이 바뀌어도 같은 절차로 다시 측정할 수 있게 했어요. 회전 비용이 빠진 성능 지표는 임의의 슬리피지 모델을 추가하는 대신, 순위가 뒤집히는 손익분기와 실측 스프레드를 비교해 보완했어요. -0.2% 손절의 1틱 스침 우려는 실제 발동일 저가를 확인해 기각했어요.

검증 결과, 실측 스프레드 수준의 슬리피지로는 새 제안의 순위가 뒤집히지 않았고 손절도 한 틱 스침으로 발동한 것이 아니었어요. 반면 체결가를 호가 단위로 반올림하려던 계획은 가격 계열에 관한 전제가 틀렸음을 확인한 뒤 철회했어요.

자동 탐색기는 후보를 빠르게 찾지만 그 후보를 신뢰할 수 있는지는 별개의 문제예요. 특히 거래 횟수, 슬리피지, 호가 단위처럼 탐색 지표 밖에 있는 조건은 단계별 가설로 나눠 검증해야 해요. 제안값을 먼저 채택하지 않고 비용 가설과 체결 가정을 차례로 확인한 덕분에 필요한 검증은 남기면서 잘못된 구현은 시작 전에 멈출 수 있었어요.


이 글 고치기
이 글 공유하기:

이전 글
조용히 멈춘 크롤러를 찾는 세 겹의 탐지망