모의투자 계좌에서 별다른 근거 없이 쓰던 청산 기준을 운영과 같은 조건으로 백테스트하고, 변경 이력과 적용 대상을 추적할 수 있는 실험 변수로 바꿨어요.
운영 규칙을 백테스트가 재현하지 못했다
모의투자 계좌는 보유 종목의 평가 손익률이 +2%에 이르면 익절하고, -0.2% 아래로 떨어지면 손절하는 방식으로 운영했어요. 장 마감 종가를 기준으로 조건을 판단한 뒤 다음 거래일 시가에 전량 매도했어요.
하지만 이 두 수치의 근거는 코드와 설계 문서, 커밋 기록 어디에도 없었어요. 실제 운영일에 이뤄진 여러 매도 가운데 상당수가 손절이었고, 일부는 체결 당시 손실률이 -3%를 넘었어요. 종가로 조건을 판단하고 다음 거래일 시가에 체결하는 구조라서 손절 기준을 좁게 잡는다고 실제 손실까지 작아지는 것은 아니었어요.
과거 데이터를 재생하며 밴드 값을 바꿔 볼 필요가 있었지만 기존 백테스트에는 청산 밴드가 아예 없었어요. 운영에서는 손익률에 따라 매도한 반면, 백테스트에서는 정해진 보유 거래일 수가 지나야 전량 청산했어요. 매수 전략이 같아도 매도 조건이 달랐기 때문에 기존 백테스트 결과만으로는 운영 청산 규칙을 평가할 수 없었어요.
운영 청산 함수를 백테스트에서 그대로 호출했다
첫 번째 원칙은 백테스트만을 위한 청산 규칙을 따로 만들지 않는 것이었어요. 같은 개념을 두 벌로 구현하면 시간이 지나면서 운영과 백테스트의 경계값이나 예외 처리, 주문 생성 조건이 다시 달라질 가능성이 컸거든요.
백테스트 재생 루프에 청산 밴드 적용 단계를 추가하고, 운영에서 쓰던 청산 주문 결정 함수와 포지션 평가액 계산 함수를 그대로 호출했어요. 하루 동안 처리하는 순서도 운영 흐름에 맞춰 정리했어요.
- 이전 주문을 당일 시가에 체결한다.
- 당일 종가를 기준으로 보유 포지션의 손익률을 평가한다.
- 청산 밴드에 도달하면 다음 거래일에 실행할 매도 주문을 만든다.
- 그다음 새로운 추천을 처리한다.
대기 중인 매도 주문이 있는 종목에는 청산 주문을 다시 만들지 않도록 했어요. 체결 전까지는 보유 수량이 남아 있어서 이 조건이 없으면 같은 종목의 매도 주문이 날마다 중복으로 생길 수 있거든요. 운영과 마찬가지로 pending SELL 주문을 기준으로 중복 생성을 막았어요.
CLI에는 익절 기준 옵션과 손절 기준 옵션을 추가했어요. 두 값은 함께 지정하거나 모두 생략해야 하며, 손절 기준에는 음수만 입력할 수 있어요. 옵션을 지정하지 않으면 기존 백테스트와 완전히 똑같이 실행되도록 밴드는 기본적으로 비활성화했어요. 기존 방식은 회귀 대조군이자 실험의 무밴드 대조군으로 남겨 뒀어요.
표본 내에서 고르고 표본 밖에서 확인했다
두 전략을 두 기간에 걸쳐 측정했어요.
- 전략:
단기 전략,장기 전략 - 구간: 표본 내 선정 구간, 표본 밖 확인 구간
- 후보: 무밴드 대조군과 밴드 6종
- 지표: 최종 수익률, 사이클 평균 수익률
총 28회, 즉 2전략 × 2구간 × 7후보를 재생했어요. 값은 표본 내 결과만으로 선정했고, 표본 밖 구간은 선택한 값이 다른 시장 국면에서도 유지되는지 확인하는 데만 사용했어요.
단기 전략의 표본 내 구간에서 최종 수익률은 무밴드 대조군이 -12.75%, 기존 +2%/-0.2%가 +16.32%, +10%/-5%가 +42.78%였어요. 표본 밖 구간에서는 각각 -51.49%, -40.51%, -33.35%를 기록했어요.
두 전략과 두 구간, 두 지표를 조합한 여덟 칸 가운데 +10%/-5%가 다섯 칸에서 1위를 차지했어요. 기존 +2%/-0.2%가 1위였던 곳은 단기 전략의 표본 밖 사이클 평균 수익률 한 칸뿐이었어요.
그렇다고 이를 보편적인 최적값으로 해석할 수는 없어요. 측정 구간은 두 개뿐이었고, 각각 상승장과 하락장이라는 상반된 특성을 보였어요. 백테스트의 매수 선정 방식도 운영과 달랐으며, 슬리피지와 생존 편향 문제도 남아 있었어요. 밴드를 적용하면 평균 보유 기간이 9일에서 2~3일로 줄어 기존 보유일 청산이 거의 발동하지 않는다는 점도 함께 고려해야 했어요.
값을 바꾸기 전에 주문에 기준을 기록했다
측정 결과만 보면 기본값을 곧바로 +10%/-5%로 바꿀 수도 있었어요. 하지만 어떤 주문을 변경 전 기준으로 생성했고 어떤 주문을 변경 후 기준으로 생성했는지 기록하지 않으면 이후 성과가 하나의 표본에 뒤섞여요.
추천 주문에는 이미 적용 규칙 버전 필드가 있었지만 청산 밴드를 추적할 축은 없었어요. 그래서 운영값을 바꾸기 전에 주문 원장 테이블에 다음 필드를 추가했어요.
적용된 익절 기준 비율적용된 손절 기준 비율
청산 밴드로 만든 매도 주문에는 해당 회차에 적용한 임계값을 기록해요. 매수 주문이나 모델이 직접 선택한 매도 주문에는 null을 남겨요. 이제 원장만 봐도 밴드로 만든 매도와 다른 경로에서 만든 매도를 구분할 수 있어요.
과거 밴드 매도 여러 건도 주문 사유에 적힌 기준 +2% 이상, 기준 -0.2% 이하 문구를 근거로 백필했어요. 모델이 직접 선택한 매도는 값을 추정하지 않고 null로 남겼어요.
성적표에서 변경 전후의 혼합을 드러냈다
주문에 기준을 기록해도 성적표에 표시하지 않으면 운영자가 표본의 성격을 파악하기 어려워요. 그래서 추천 성적표 집계에 다음 필드를 추가했어요.
평가 구간에 사용된 청산 밴드 집합밴드 기준이 없는 매도 건수
청산 밴드 사용량 집계 함수는 구간 안에서 체결된 매도를 집계하고, 성적표에는 다음과 같은 형식으로 표시해요.
단기 전략 · 규칙 버전 2 · 밴드 +2/-0.2 + 밴드 외 1건
밴드가 바뀐 시점에 걸친 기간에는 여러 값이 함께 표시돼요. 다만 PENDING이나 EXPIRED 주문까지 포함하면 실제 성과와 무관한 기준이 섞일 수 있어서 FILLED와 PARTIALLY_FILLED 매도만 집계하도록 제한했어요.
이 집계만으로 개별 추천 사이클과 밴드가 일대일로 연결되지는 않아요. 현 단계의 목적은 특정 밴드의 성과라고 단정하는 것이 아니라, 평가 표본에 여러 기준이 섞였다는 사실을 드러내는 데 있어요.
청산 상수를 변경 이력이 남는 실험 변수로 바꿨다
먼저 기록할 축을 마련한 뒤 운영 기본값을 +2%/-0.2%에서 +10%/-5%로 바꿨어요. 코드에는 값을 선정한 구간과 표본 밖 확인 결과를 남겼고, 두 구간만으로 하나의 최적값을 확정할 수 없다는 한계도 함께 명시했어요.
이번 작업의 핵심은 수익률 숫자 하나가 아니에요. 운영과 달랐던 백테스트를 바로잡고 무밴드 대조군을 보존했으며, CLI로 실험 변수를 주입할 수 있게 했어요. 주문마다 적용한 기준을 기록하고 성적표에서도 여러 기준이 섞였는지 확인할 수 있는 흐름도 마련했어요.
이제 청산 밴드는 출처를 알 수 없는 상수가 아니에요. 운영과 같은 규칙으로 재생해 후보끼리 비교할 수 있고, 변경 시점과 각 주문에 적용한 값도 원장에서 추적할 수 있는 실험 변수가 됐어요.
다만 변경 이후 운영 성과가 실제로 개선됐다고 결론 내리기에는 아직 일러요. 현재 원장은 여러 밴드가 섞였다는 사실까지만 보여줘요. 변경 전후의 성과를 완전히 분리해 채점하려면 구간별 집계와 사이클별 귀속이 더 필요해요. 이번 변경은 이런 후속 검증에 필요한 측정과 변경 추적의 기반을 마련한 작업이에요.
근거: 백테스트에 운영 청산 규칙과 측정 경로를 추가한 변경 기록, 주문별 밴드 기록과 운영값 변경을 적용한 변경 기록