본문 바로가기

실전 원칙

그 순간에만 존재하는 값을 잃지 않는 법: 진입 시점 데이터 저장 전략

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

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


왜 위험한가: 시점 고정 데이터라는 성질

핵심은 살 때의 상황값이 "그 순간에만 존재하는 값"이라는 데 있다. 그때의 시장 흐름이나 심리 지표는 시간이 지나면 값 자체가 바뀌므로, 며칠 뒤에 되돌아가 다시 뽑아낼 방법이 없다. 이런 값을 시점 고정 데이터(point-in-time data)라고 부를 수 있다.
더 나쁜 조합은 데이터 구조 쪽에 있었다. 기존 프로그램은 실제로 들고 있는 종목 정보를 매 주기마다 계좌 잔고를 보고 다시 만들어 냈다. 잔고는 "지금 얼마가 있다"만 알려줄 뿐, "살 때 시장이 어땠는지"는 담지 못한다. 그래서 산 순간에만 존재하던 값들은 다음 주기에 종목 정보가 새로 만들어지는 순간 흔적도 없이 사라졌다.

계좌 잔고 기반 정보매 주기 새로 만들어짐지금 값은 항상 있음
살 때의 시장 상황값그 순간에만 존재지나가면 되살릴 수 없음

이 두 성질이 부딪히면, 나중에 아무리 열심히 다시 계산하려 해도 원본이 없어 되살릴 수 없다. 로그가 얇았던 원인은 게으름이 아니라, 데이터 구조가 시점 값을 담을 자리를 아예 두지 않았다는 데 있었다.


해결: 저장소를 두 개로 분리하는 라운드트립 기록

흔한 유혹은 기존 종목 정보 구조에 상황값 필드를 억지로 끼워 넣는 것이다. 하지만 그 구조는 매 주기 새로 만들어지므로 끼워 넣어도 다음 주기에 지워진다. 그래서 기존 종목 정보나 잔고 계산 부분은 전혀 건드리지 않고, 기록 전용 저장소 두 개를 새로 만들어 문제를 풀었다.

  1. 진입 상황 저장소: 매수가 체결되면, 그 종목별로 당시의 시장 상황값을 저장한다.
  2. 연결 저장소: 매도가 체결되면, 저장해 뒀던 진입 상황을 꺼내(pop) 청산 결과와 이어 붙여 한 줄로 기록한다.

동작 순서를 그림처럼 적으면 이렇게 된다.

매수 체결 → 진입 상황 만들어 저장(진입 저장소)
매도 체결 → 진입 상황 꺼내 청산과 연결(연결 저장소)

위 두 줄이 이 작업의 핵심 흐름이다. 매수 직후 시점 값을 진입 저장소에 넣어두고, 매도 시점에 같은 종목의 진입 값을 꺼내 결과와 붙인다. 다만 실제 작업에는 이 두 줄 외에도 짝 없음 처리, 민감정보·잔고 원본 금액 제외, 테스트 추가 같은 세부가 함께 들어간다. 꺼낸다(pop)는 표현을 쓴 이유는 진입 상황을 꺼내 청산 결과와 연결하기 때문이며, 일반적으로 이렇게 꺼내는 방식은 같은 진입을 여러 번 연결하는 것을 막는 효과도 기대할 수 있다.
담는 값에는 원칙을 뒀다. 시장 흐름·위험 관련 지표는 넣되, 민감한 정보나 잔고 원본 금액은 일부러 뺐다. 기록에는 분석에 필요한 지표만 담고, 계좌 상태를 그대로 옮기지 않도록 한 것이다.


따라 할 수 있는 절차와 예외 처리

자기 시스템(매매 봇, 실험 로그, 주문 처리 등)에 같은 방식을 적용하려면 다음 순서로 옮길 수 있다.

  1. 사라지는 값을 먼저 목록으로 적는다. "이 값은 지금은 뽑을 수 있지만 1시간 뒤엔 못 뽑는가?"를 기준으로 시점 고정 데이터를 골라낸다.
  2. 기존 구조를 건드리지 않는 별도 저장소를 만든다. 진입 시점 값을 담을 저장소 하나, 진입과 결과를 이어 붙인 최종 기록을 담을 저장소 하나.
  3. 진입(매수) 직후에 값을 캡처해 넣는다. 이때 민감 정보·원본 금액은 제외하고 분석에 필요한 지표만 담는다.
  4. 청산(매도) 시점에 짝을 꺼내 이어 붙인다. 종목/식별자를 키로 진입 기록을 꺼내 결과와 한 줄로 만든다.
  5. 짝이 없을 때의 규칙을 정한다. 이번 작업에서는 매도 시점에 짝이 맞는 진입 상황이 없으면 그 기록에 "진입 상황 없음" 표시를 붙여 나중에 걸러낼 수 있게 했다.

이 짝 없음 처리가 중요하다. 진입 저장 전에 프로그램이 재시작됐거나, 수동으로 매도가 끼어들면 진입 기록이 비어 있을 수 있다. 이때 조용히 버리면 통계가 은근히 왜곡되고, 반대로 오류로 멈추면 매매가 막힌다. "표시만 붙여 두고 나중에 걸러내기"이 관찰 전용 작업에 맞는 절충이다. 이 변경에 대해 테스트 5건을 추가했다.
주의할 점도 있다. 기존에 있던 시간에 의존하던 실패 테스트 몇 건은 이번 변경과 무관하며, 손대지 않은 상태에서도 똑같이 실패한다. 이런 사전 실패를 이번 변경 탓으로 오해하지 않도록 별도로 분리해 다루기로 했다.


적용 전후 비교와 이 작업의 진짜 성과

매수 기록에 남는 값선정 점수 정도살 때의 시장 상황값까지
살 때 상황 ↔ 팔 때 결과 연결불가(값이 사라짐)한 줄로 연결
원인 분석상황별로 쪼갤 수 없음상황별로 결과를 나눠 볼 수 있음
실제 매매 규칙변경 없음(관찰 전용)

표에서 분명히 해둘 것이 있다. 이번 작업의 성과는 "수익이 올랐다"가 아니라 "원인을 볼 수 있게 됐다"에 있다. 매매 로직은 한 줄도 바꾸지 않았으므로, 이 변경이 손익을 개선하는지 여부는 아직 판단할 수 없다. 손익 개선 여부는 이 기록이 충분히 쌓인 다음에야 판단할 문제다. 기록을 늘렸다는 이유로 성과를 앞당겨 주장하지 않는 것이 관찰 전용 작업의 기본자세다.


한계와 원칙: 측정부터 만들고 전략은 나중에

가장 크게 배운 점은, 그 순간에만 존재하는 값은 나중에 다시 계산할 수 없으니 기존 데이터 구조에 억지로 끼워 넣지 말고 살 때 따로 저장했다가 팔 때 꺼내 쓰는 방식이 깔끔하다는 것이다. 복잡한 배선 없이 저장소 두 개만으로 목적을 이뤘다.
또 하나는 순서다. 전략을 바꾸기 전에 먼저 제대로 측정할 수 있는 기록부터 만들어야 한다. 측정 재료가 없으면 어떤 전략 변경도 효과를 검증할 수 없기 때문이다.
한계도 분명하다. 이 글은 기록 기능을 붙인 데까지만 다룬다. 어떤 진입 상황이 결과가 좋고 나빴는지 실제로 분석하는 일, 그 분석으로 전략을 바꾸는 일은 데이터가 쌓인 뒤의 별도 과제다. 또한 이 방식은 진입과 청산이 명확히 짝지어지는 경우에 잘 맞는다. 부분 매도·물타기처럼 한 진입이 여러 청산으로 나뉘면 꺼내는 규칙을 더 정교하게 설계해야 한다. 자신의 상황에서는 짝이 어긋나는 경우를 먼저 그려 보고 규칙을 정한 뒤 작은 테스트로 검증하길 권한다.
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

반응형