김치 프리미엄·역프리미엄 거래는 한 거래소에서 현물을 사고, 가격 변동 위험은 선물 숏으로 막아 두고, 코인을 옮긴 뒤 팔면서 숏을 푸는 여러 단계로 이루어집니다. 사람이 앱 여러 개를 오가면 체결 수량과 헷지 수량이 어긋나고, 주문 응답이 끊기면 무엇이 체결·헷지됐는지 다시 맞춰야 합니다. 그래서 판단(언제·어느 코인)은 사람에게 남기고, 체결에 맞춘 헷지와 장애 뒤 복원을 봇이 맡도록 나눴습니다.

Overview
Features
- 공개 시장 감시(API 키 없음): 공개 호가 스냅샷으로 현물 거래소 간 갭, 현물–선물 교차시장 프리미엄, 같은 거래소의 현선갭을 표시하고 출금 수수료 근거와 관측 시각을 함께 보여 줌
- 체결 기반 헷지 진입: 규칙·잔고·증거금·중복 포지션·선물 계정 설정을 확인한 뒤 현물을 사고, 체결될 때마다 그 수량만큼 선물 숏
- 세 가지 청산 방식(시장가, IOC 추격, 메이커)과 매도 체결분만큼의 reduce-only 숏 해제, 다른 거래소로 옮긴 뒤 청산 지원
- 포지션 감시: 거래소의 실제 숏 수량과 원장을 주기적으로 대조하고, 반올림 조각이나 수동 청산으로 남은 숏을 코인 단위로 정리
- 재시작 복원: 살아 있는 주문을 클라이언트 주문 ID로 다시 연결하고, 꺼진 동안의 체결을 중복 없이 반영한 뒤 헷지
Architecture

다이어그램 원문 (Mermaid)
flowchart TB
subgraph EX["국내·해외 거래소"]
PUB["공개 API<br/>호가 스냅샷 WS · 티커 · 캔들 · 상장 목록"]
PRIV["Private API<br/>현물·선물 주문 · 체결 WS · 잔고 · 포지션"]
end
subgraph MON["감시 프로세스 (API 키 없음)"]
MADP["공개 클라이언트<br/>+ 감시용 rate limiter"]
STORE["시장 저장소<br/>단일 writer"]
GAP["갭 · 프리미엄 · 현선갭 계산"]
MAPI["FastAPI"]
MUI["React 감시 화면"]
MADP --> STORE --> GAP --> MAPI --> MUI
end
subgraph EXE["실행 프로세스"]
UI["React 실행 화면"]
API["FastAPI<br/>확정 · 청산 · 정리 명령"]
ACT["포지션 actor<br/>포지션별 단일 writer"]
ENT["진입 executor<br/>매수 → 체결분 숏"]
EXIT["청산 executor<br/>market · chase · maker<br/>→ 체결분 숏 해제"]
CONF["주문 확정 서비스<br/>intent 선저장 → 1회 전송<br/>→ ID 조회로만 확정"]
WATCH["포지션 감시 · 재시작 복원"]
ADP["거래소 어댑터 (직접 구현)<br/>+ 실행용 rate limiter"]
DB[("SQLite<br/>positions · orders · fills<br/>hedge_orders · exit_jobs")]
UI --> API --> ACT
ACT --> ENT --> CONF
ACT --> EXIT --> CONF
CONF --> ADP
WATCH --> ADP
ACT --> DB
CONF --> DB
end
PUB --> MADP
ADP <--> PRIV
MUI -. "진입 폼 프리필 링크<br/>(URL 쿼리만)" .-> UI
다이어그램 원문 (Mermaid)
stateDiagram-v2
direction LR
[*] --> ENTRY: 진입 확정
ENTRY --> HOLDING: 매수 종료 · 체결분 헷지 완료
ENTRY --> ABORTED: 체결 0으로 전량 취소
HOLDING --> EXITING: 청산 확정
EXITING --> HOLDING: 잔량 남음
EXITING --> DONE: 현물 잔여 0 · 숏 잔여 0
HOLDING --> DONE: 잔여 정리·조정 후 양쪽 0Key decisions
기능을 늘리기보다 범위를 잘라 냈다
첫 버전은 감시·검증·출금·도착 확인·청산을 한 프로세스에서 자동으로 이어 가는 전송형 봇이었지만, 감시 부하로 이벤트 루프 지연과 rate limit 오류가 반복됐습니다. 두 번째 버전은 봇의 일을 "현물 매수와 동시 헷지, 지시 시 매도와 숏 해제"로 좁히고, 갭 판단과 출금은 사람이 하도록 했습니다. 자금 이동 기능은 별도 저장소로 분리했습니다.
감시와 실행을 프로세스로 나누고, 감시는 키에 닿지 않게
감시와 실행은 거래소 응답 파서만 공유하고 DB·파일·이벤트 루프는 공유하지 않습니다. 감시용 클라이언트에는 키 인자 자체가 없고, 성능 기준은 CPU 점유율이 아니라 루프 지연과 실행 봇에 대한 영향으로 잡았습니다.
한 번 보내고, 모르면 조회로만 확정한다
주문 의도와 클라이언트 주문 ID를 먼저 SQLite에 저장한 뒤 전송하고, 결과를 모르면 재전송하지 않고 ID 조회로 확정합니다. 포지션마다 쓰기 주체를 하나로 두고, 판정할 수 없는 값은 안전해 보이는 기본값으로 채우지 않고 "미확정"으로 드러냅니다.
Security
- 출금 경로 없음: 실행 봇에 출금·전송 API를 호출하는 코드 경로를 만들지 않고, 출금 권한 없는 키와 IP 허용 목록을 전제로 함
- 헷지 방향을 규칙으로 고정: 진입은 현물 체결 → 숏, 청산은 매도 체결 → 숏 해제 순서만 허용, 과소 헷지가 생기지 않는 방향으로 반올림
- reduce-only는 독립 인자로 받고 현물 주문에 켜져 오면 어댑터가 거부, 선물 계정 설정을 확인하지 못하면 현물 주문도 내지 않음
- 재시도 횟수·간격은 스펙에 고정, 제한 응답을 받으면 해당 거래소 요청을 모두 멈춤
- 실행 화면은 loopback 전용 바인딩 + Host/Origin 검사, 감시 → 실행 연결은 입력값을 채우는 URL 쿼리뿐
Status
개발 중
소액 실계좌로 세 가지 청산 방식, 수동 출금 뒤 다른 거래소 청산, 주문이 살아 있는 상태의 재시작 복원을 검증했고 실사용 배치를 앞두고 있습니다.