자동매매 봇 개발 M1·M2·M3: 관찰, 재현, 복구 검증

English version

TECH INSIGHT / EXPLAINED

자동매매 봇을 단계별로 만든다는 것은 기능을 조금씩 켠다는 뜻만은 아닙니다. 각 단계가 끝났다고 판단할 증거를 먼저 정하고, 주문 전송 권한은 그 증거와 별도로 관리하는 방식입니다.

이 글의 M1·M2·M3는 설명을 위한 개발 단계 이름입니다. 특정 업계 표준이나 검증된 수익 전략을 뜻하지 않습니다. M1은 관찰, M2는 주문 없는 재현, M3는 장애 복구 연습으로 나누며, 실거래 전환은 세 단계 이후의 별도 결정으로 둡니다.

세 개발자가 M1 데이터 관찰, M2 기록 재생, M3 연결 장애와 주문 장부 대조를 설명하는 삽화
세 장면의 핵심: 무엇을 보았는지 기록하고, 같은 입력을 재생하며, 연결이 끊겨도 상태를 확인한다. AI로 제작한 설명용 삽화입니다.

1. M1: 숫자를 띄우는 데서 끝내지 않는다

화면에 가격이 나타나는 것과 전략이 쓸 수 있는 데이터가 준비된 것은 다릅니다. 수신한 원본과 해석한 값을 짝지어 보관하고, 종목 식별자·가격 단위·시장 시각·로컬 수신 시각을 구분합니다. 마지막 수신이 오래됐는데 화면의 가격만 그대로 남아 있으면 ‘정상’으로 표시하지 않습니다.

예를 들어 두 상품의 차이를 계산한다면, 한쪽은 방금 받은 호가이고 다른 쪽은 몇 분 전 값인지 확인해야 합니다. 허용할 데이터 나이는 전략의 시간 범위와 데이터 제공 조건에 맞춰 정합니다. 임의의 초 단위를 모든 시장에 복사하지 않습니다. 주문 기능은 아직 연결하지 않고, 필요한 읽기 권한만 가진 데이터 경로로 시작합니다.

완료 증거확인 방법
필드 해석샘플 원본 1건과 변환 결과를 공식 데이터 명세와 대조한다.
누락·중복 처리빈 종목, 중복 메시지, 시각 역전 입력을 넣고 결과를 기록한다.
오래된 데이터 구분새 메시지를 끊었을 때 화면과 판단 상태가 함께 바뀌는지 본다.
날짜 경계시장 시간대와 거래일이 바뀔 때 기준가를 어떻게 선택하는지 확인한다.

2. M2: 같은 기록에서 같은 판단이 나오는지 확인한다

전략 함수는 입력을 받아 판단을 반환하게 만들고, HTTP 주문 전송은 분리합니다. M2의 결과는 주문이 아니라 ‘그 시점에 이런 후보를 만들었다’는 기록입니다. 가상 체결 모델을 추가한다면 수수료, 지연, 부분 체결 가정을 함께 남깁니다. 그 결과를 실제 체결 성과로 표현하지 않습니다.

아래는 재현성의 의미를 보여 주는 작은 Python 예제입니다. 문자열 가격을 Decimal로 읽고 같은 입력을 두 번 실행합니다. 이 임계값 조건은 매매 전략이나 교차 신호가 아니며, 거래소 호출도 없습니다.

from decimal import Decimal

def replay(prices, threshold):
    limit = Decimal(threshold)
    if not limit.is_finite() or limit <= 0:
        raise ValueError("threshold must be positive and finite")
    decisions = []
    for sequence, raw in enumerate(prices):
        price = Decimal(raw)
        if not price.is_finite() or price <= 0:
            raise ValueError("price must be positive and finite")
        decisions.append((sequence, "candidate" if price >= limit else "observe"))
    return decisions

recording = ["100.00", "101.00", "99.00"]
first = replay(recording, "101.00")
assert first == replay(recording, "101.00")
print(first)

Python 3.12에서 확인한 출력은 [(0, 'observe'), (1, 'candidate'), (2, 'observe')]입니다. 0, 음수, NaN, Infinity 입력은 거절하는 것도 확인했습니다. 실제 수집기에서는 숫자 변환 실패를 처리하고, 실패 원본을 기록할 별도 정책이 필요합니다.

재생 결과를 비교할 때 실행 당시의 현재 시각이나 난수를 판단 함수 안에서 몰래 읽으면 재현성이 깨집니다. 시장 시각·설정 버전·필요한 난수 시드처럼 결과에 영향을 주는 값은 입력으로 고정합니다. 같은 결과가 나온다는 사실만으로 전략의 타당성까지 입증되지는 않습니다.

3. M3: 실패를 넣어 보고 복구 규칙을 확인한다

여기서 알림·중지·복구 기능을 준비합니다. 실거래를 먼저 시작하고 나중에 추가하는 부가 기능으로 두지 않습니다. 테스트 환경에서 연결 끊김, 오래된 데이터, 프로세스 재시작, 응답 지연을 넣고 봇이 어떤 상태로 이동하는지 관찰합니다.

실패 상황확인할 행동
데이터 수신 중단신규 판단을 막고 마지막 정상 수신 시각과 중단 이유를 표시한다.
주문 응답 유실을 모의실패로 단정해 재전송하지 않고 식별자로 기존 주문 상태를 조회한다.
재시작저장된 의도와 외부의 미체결·체결·잔고를 대조하기 전 새 주문을 막는다.
중지 버튼 실행신규 진입 차단과 기존 주문 취소·포지션 처리 정책을 구분한다.
알림 전송 실패별도 로그와 상태 화면에 실패를 남기고 대응 경로를 확인한다.

‘정지’와 ‘청산 완료’는 다른 상태입니다. 주문 취소 요청은 이미 체결된 수량을 되돌리지 못하고, 청산 요청도 접수·부분 체결·완료·거절로 나뉩니다. 화면의 초록색 표시 하나로 이 과정을 합치지 않는 것이 복구 연습의 목적입니다.

4. 실거래로 넘어가기 전 남겨야 할 기록

단계 종료 기록에는 테스트 입력, 기대 결과, 실제 결과, 실패 시 조치와 설정 버전을 적습니다. M1의 원본 샘플, M2의 재생 파일, M3의 장애 주입 결과를 연결하면 이후 변경이 어느 가정을 깨뜨렸는지 찾기 쉽습니다. 기간이나 금액이 작다는 이유만으로 검증을 통과했다고 보지는 않습니다.

실거래 전환에는 별도의 승인 조건, 노출 제한, 주문 상태 대조, 계정 권한과 운영 대응 준비가 필요합니다. 테스트가 모든 장애나 손실 가능성을 없애지는 않습니다. 개발 검증은 수익성 검증과 구분해서 기록합니다.

관련 글과 확인 자료

내용·예제 검토: 2026-09-29

댓글