
문제: '왜 손해 봤지?'에 답할 수 없는 기록

매매든, 실험이든, 사용자 행동 분석이든 결과를 남기는 시스템은 많다. 그런데 결과만 남기면 나중에 특정 질문에 답하기 어려울 수 있다. "이 거래는 왜 손해가 났을까?"라고 물었을 때, 살 때 어떤 상황이었는지에 대한 기록이 아예 없기 때문이다.
실제 자동매매 프로그램을 손보면서 이 문제를 정면으로 겪었다. 매수 기록에는 그 종목을 왜 골랐는지 선정 점수 하나만 남아 있었다. 정작 살 때의 시장 상황 — 짧은 흐름과 긴 흐름, 하루 안에서 가격이 어디쯤이었는지, 거래량 비교, 가격이 얼마나 출렁였는지, 대표 코인의 움직임, 시장 심리 지표 — 은 어디에도 저장되지 않았다.
그 결과, 이긴 비율은 높은데도 수수료를 빼면 손해가 나는 상황의 원인을 살 때의 상황별로 쪼개서 따져볼 수가 없었다. 원인을 나눠 볼 재료 자체가 사라지고 없었던 것이다.
왜 위험한가: 시점 고정 데이터라는 성질

핵심은 살 때의 상황값이 "그 순간에만 존재하는 값"이라는 데 있다. 그때의 시장 흐름이나 심리 지표는 시간이 지나면 값 자체가 바뀌므로, 며칠 뒤에 되돌아가 다시 뽑아낼 방법이 없다. 이런 값을 시점 고정 데이터(point-in-time data)라고 부를 수 있다.
더 나쁜 조합은 데이터 구조 쪽에 있었다. 기존 프로그램은 실제로 들고 있는 종목 정보를 매 주기마다 계좌 잔고를 보고 다시 만들어 냈다. 잔고는 "지금 얼마가 있다"만 알려줄 뿐, "살 때 시장이 어땠는지"는 담지 못한다. 그래서 산 순간에만 존재하던 값들은 다음 주기에 종목 정보가 새로 만들어지는 순간 흔적도 없이 사라졌다.
| 계좌 잔고 기반 정보 | 매 주기 새로 만들어짐 | 지금 값은 항상 있음 |
| 살 때의 시장 상황값 | 그 순간에만 존재 | 지나가면 되살릴 수 없음 |
이 두 성질이 부딪히면, 나중에 아무리 열심히 다시 계산하려 해도 원본이 없어 되살릴 수 없다. 로그가 얇았던 원인은 게으름이 아니라, 데이터 구조가 시점 값을 담을 자리를 아예 두지 않았다는 데 있었다.
해결: 저장소를 두 개로 분리하는 라운드트립 기록
흔한 유혹은 기존 종목 정보 구조에 상황값 필드를 억지로 끼워 넣는 것이다. 하지만 그 구조는 매 주기 새로 만들어지므로 끼워 넣어도 다음 주기에 지워진다. 그래서 기존 종목 정보나 잔고 계산 부분은 전혀 건드리지 않고, 기록 전용 저장소 두 개를 새로 만들어 문제를 풀었다.
- 진입 상황 저장소: 매수가 체결되면, 그 종목별로 당시의 시장 상황값을 저장한다.
- 연결 저장소: 매도가 체결되면, 저장해 뒀던 진입 상황을 꺼내(pop) 청산 결과와 이어 붙여 한 줄로 기록한다.
동작 순서를 그림처럼 적으면 이렇게 된다.
매수 체결 → 진입 상황 만들어 저장(진입 저장소)
매도 체결 → 진입 상황 꺼내 청산과 연결(연결 저장소)위 두 줄이 이 작업의 핵심 흐름이다. 매수 직후 시점 값을 진입 저장소에 넣어두고, 매도 시점에 같은 종목의 진입 값을 꺼내 결과와 붙인다. 다만 실제 작업에는 이 두 줄 외에도 짝 없음 처리, 민감정보·잔고 원본 금액 제외, 테스트 추가 같은 세부가 함께 들어간다. 꺼낸다(pop)는 표현을 쓴 이유는 진입 상황을 꺼내 청산 결과와 연결하기 때문이며, 일반적으로 이렇게 꺼내는 방식은 같은 진입을 여러 번 연결하는 것을 막는 효과도 기대할 수 있다.
담는 값에는 원칙을 뒀다. 시장 흐름·위험 관련 지표는 넣되, 민감한 정보나 잔고 원본 금액은 일부러 뺐다. 기록에는 분석에 필요한 지표만 담고, 계좌 상태를 그대로 옮기지 않도록 한 것이다.
따라 할 수 있는 절차와 예외 처리
자기 시스템(매매 봇, 실험 로그, 주문 처리 등)에 같은 방식을 적용하려면 다음 순서로 옮길 수 있다.
- 사라지는 값을 먼저 목록으로 적는다. "이 값은 지금은 뽑을 수 있지만 1시간 뒤엔 못 뽑는가?"를 기준으로 시점 고정 데이터를 골라낸다.
- 기존 구조를 건드리지 않는 별도 저장소를 만든다. 진입 시점 값을 담을 저장소 하나, 진입과 결과를 이어 붙인 최종 기록을 담을 저장소 하나.
- 진입(매수) 직후에 값을 캡처해 넣는다. 이때 민감 정보·원본 금액은 제외하고 분석에 필요한 지표만 담는다.
- 청산(매도) 시점에 짝을 꺼내 이어 붙인다. 종목/식별자를 키로 진입 기록을 꺼내 결과와 한 줄로 만든다.
- 짝이 없을 때의 규칙을 정한다. 이번 작업에서는 매도 시점에 짝이 맞는 진입 상황이 없으면 그 기록에 "진입 상황 없음" 표시를 붙여 나중에 걸러낼 수 있게 했다.
이 짝 없음 처리가 중요하다. 진입 저장 전에 프로그램이 재시작됐거나, 수동으로 매도가 끼어들면 진입 기록이 비어 있을 수 있다. 이때 조용히 버리면 통계가 은근히 왜곡되고, 반대로 오류로 멈추면 매매가 막힌다. "표시만 붙여 두고 나중에 걸러내기"이 관찰 전용 작업에 맞는 절충이다. 이 변경에 대해 테스트 5건을 추가했다.
주의할 점도 있다. 기존에 있던 시간에 의존하던 실패 테스트 몇 건은 이번 변경과 무관하며, 손대지 않은 상태에서도 똑같이 실패한다. 이런 사전 실패를 이번 변경 탓으로 오해하지 않도록 별도로 분리해 다루기로 했다.
적용 전후 비교와 이 작업의 진짜 성과
| 매수 기록에 남는 값 | 선정 점수 정도 | 살 때의 시장 상황값까지 |
| 살 때 상황 ↔ 팔 때 결과 연결 | 불가(값이 사라짐) | 한 줄로 연결 |
| 원인 분석 | 상황별로 쪼갤 수 없음 | 상황별로 결과를 나눠 볼 수 있음 |
| 실제 매매 규칙 | — | 변경 없음(관찰 전용) |
표에서 분명히 해둘 것이 있다. 이번 작업의 성과는 "수익이 올랐다"가 아니라 "원인을 볼 수 있게 됐다"에 있다. 매매 로직은 한 줄도 바꾸지 않았으므로, 이 변경이 손익을 개선하는지 여부는 아직 판단할 수 없다. 손익 개선 여부는 이 기록이 충분히 쌓인 다음에야 판단할 문제다. 기록을 늘렸다는 이유로 성과를 앞당겨 주장하지 않는 것이 관찰 전용 작업의 기본자세다.
한계와 원칙: 측정부터 만들고 전략은 나중에
가장 크게 배운 점은, 그 순간에만 존재하는 값은 나중에 다시 계산할 수 없으니 기존 데이터 구조에 억지로 끼워 넣지 말고 살 때 따로 저장했다가 팔 때 꺼내 쓰는 방식이 깔끔하다는 것이다. 복잡한 배선 없이 저장소 두 개만으로 목적을 이뤘다.
또 하나는 순서다. 전략을 바꾸기 전에 먼저 제대로 측정할 수 있는 기록부터 만들어야 한다. 측정 재료가 없으면 어떤 전략 변경도 효과를 검증할 수 없기 때문이다.
한계도 분명하다. 이 글은 기록 기능을 붙인 데까지만 다룬다. 어떤 진입 상황이 결과가 좋고 나빴는지 실제로 분석하는 일, 그 분석으로 전략을 바꾸는 일은 데이터가 쌓인 뒤의 별도 과제다. 또한 이 방식은 진입과 청산이 명확히 짝지어지는 경우에 잘 맞는다. 부분 매도·물타기처럼 한 진입이 여러 청산으로 나뉘면 꺼내는 규칙을 더 정교하게 설계해야 한다. 자신의 상황에서는 짝이 어긋나는 경우를 먼저 그려 보고 규칙을 정한 뒤 작은 테스트로 검증하길 권한다.
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
'실전 원칙' 카테고리의 다른 글
| 실행 차단 훅이 엉뚱한 폴더를 검사할 때: cd 명령 추적으로 경로 판별 고치기 (0) | 2026.08.17 |
|---|---|
| 쇼츠 자막이 밋밋한 진짜 이유: 정지 배경의 함정과 '움직이는 글자'로 바꾸는 법 (0) | 2026.08.16 |
| 달러 자산을 원화로 볼 때 수익률이 헷갈리는 이유와 표시를 분리하는 법 (0) | 2026.08.14 |
| 자동화가 사람 개입 후 멈추는 문제: 중복 없이 다시 태우는 재개 경로 설계법 (0) | 2026.08.13 |
| 인프라를 계약으로 만들면 기능 추가가 10파일로 끝난다 — 재사용 가능한 코어 설계법 (0) | 2026.08.12 |