API 시간 오차 오류 — 내 PC 시계가 몇 초 틀려서 주문이 전부 거절되는 문제
키 권한도 맞고 잔고도 충분한데 API로 낸 주문만 계속 거절됩니다. 에러 메시지에는 timestamp나 recvWindow 같은 단어가 섞여 있습니다. 거래소 장애도, 키 문제도 아닙니다. 내 컴퓨터 시계가 거래소 시계와 몇 초 어긋나 있는 것이 원인입니다.
거래소는 요청마다 "몇 시에 만든 요청인가"를 묻는다
API 주문에는 서명이 붙습니다. 서명만 있으면 될 것 같지만, 서명은 복사해서 다시 보낼 수 있습니다. 누군가 내 요청을 그대로 가로채 한 시간 뒤에 다시 보내면 같은 주문이 또 들어갑니다. 그래서 거래소는 요청에 만든 시각을 같이 적게 하고, 그 시각이 지금과 가까울 때만 받아줍니다.
내 요청에 적힌 시각 = timestamp
거래소가 열어두는 창 = recvWindow
─────────────
통과 조건
(서버시각 − timestamp)가
0 이상 recvWindow 이하
· 너무 오래된 요청 → 거절
(재전송 공격 방지)
· 서버보다 미래인 요청 → 거절
(있을 수 없는 시각)
여기서 중요한 건 두 번째입니다. 시계가 느린 경우엔 창이 좁아질 뿐이지만, 시계가 빠른 경우엔 거래소 입장에서 "아직 오지도 않은 시각에 만들어진 요청"이 되어 창 폭과 무관하게 바로 거절됩니다. 그래서 몇 초 차이만으로 주문이 100% 막히는 상황이 생깁니다.
3초 차이가 만드는 결과
기본값이 recvWindow 5,000ms(5초)인 거래소를 가정하고 계산해 봅니다.
거래소 서버 시각 12:00:00.000
내 PC 시각 12:00:03.000
recvWindow = 5,000ms
내가 보낸 timestamp = 12:00:03.000
서버가 받은 시각 = 12:00:00.080
(전송 지연 80ms)
차이 = 0.080 − 3.000 = −2.92초
─────────────
음수 = 서버보다 미래
→ 조건 불통과 → 전량 거절
재시도해도 결과는 같다
(시계가 계속 3초 앞서 있으므로)
반대로 시계가 느린 경우를 봅니다. 이쪽은 "가끔 되고 가끔 안 되는" 더 헷갈리는 증상이 나옵니다.
recvWindow = 5,000ms
시계 오차 = 4,600ms (느림)
남은 여유 = 5,000 − 4,600 = 400ms
네트워크 왕복이 300ms일 때
→ 여유 100ms · 통과
네트워크가 잠깐 500ms로 튈 때
→ 예산 초과 · 거절
─────────────
같은 코드가
평소엔 되고 · 변동성 구간에만 실패
가장 필요한 순간에 주문이 안 나간다
거래가 몰리는 순간에 지연이 커지므로, 여유가 얼마 없는 상태는 급등락 구간에서만 골라서 실패합니다. 로그를 나중에 보면 조용한 시간대에는 정상이라 원인을 찾기 어렵습니다.
시계가 틀어지는 네 가지 경로
"윈도우가 알아서 시간을 맞춰주지 않나" 싶지만, 자동 동기화는 생각보다 자주 하지 않습니다.
① 동기화 주기
기본 동기화 간격이 길면
그 사이 하드웨어 시계가 드리프트
② 절전·최대 절전 복귀
깨어난 직후 몇 초 어긋난 채 시작
③ 가상머신·클라우드
호스트가 멈췄다 재개하면 시계도 튄다
④ 동기화 서버 접속 실패
방화벽·네트워크 문제로 조용히 실패
→ 에러 없이 오차만 누적
─────────────
공통점
화면의 시계는 멀쩡해 보인다
초 단위 차이는 눈으로 안 보인다
④가 특히 골치입니다. 시간 동기화가 실패해도 경고창이 뜨지 않습니다. 며칠에 걸쳐 오차가 조금씩 늘다가, 어느 날 창을 넘는 순간부터 주문이 안 나가기 시작합니다. 그날 코드를 바꾼 적이 없으니 원인을 코드에서 찾게 됩니다.
네트워크 왕복도 같은 예산을 쓴다
recvWindow는 시계 오차만을 위한 예산이 아닙니다. 요청이 서버에 도착하는 데 걸린 시간도 같은 창에서 함께 깎입니다.
recvWindow 5,000ms
− 시계 오차 2,000ms
− 요청 생성·서명 10ms
− 네트워크 편도 150ms
− 서버 대기열 100ms
─────────────
남은 여유 = 2,740ms
여기서 시계 오차가 4,800ms가 되면
남은 여유 = −60ms → 거절
같은 오차라도
해외 서버·모바일 테더링이면
더 빨리 창을 넘는다
그래서 국내에서 잘 돌던 코드를 해외 서버로 옮기거나, 유선에서 무선으로 바꾸는 것만으로 갑자기 거절이 시작되기도 합니다. 요청 한도 초과(429)로 대기열이 길어지는 상황과 겹치면 예산은 더 빨리 소진됩니다.
진단은 서버 시간 한 번 조회로 끝난다
추측하지 말고 오차를 숫자로 재면 됩니다. 대부분의 거래소는 인증 없이 서버 시각을 알려주는 엔드포인트를 제공합니다.
1) 내 시각 기록 → t1
2) 서버 시간 조회 → 응답값 s
3) 내 시각 기록 → t2
왕복 = t2 − t1
내 시각 추정 = (t1 + t2) ÷ 2
오프셋 = s − (t1 + t2) ÷ 2
─────────────
예시
t1 = 12:00:00.000
s = 12:00:02.100
t2 = 12:00:00.200
왕복 200ms · 중간값 12:00:00.100
오프셋 = +2,000ms
→ 내 시계가 2초 느리다
이 값이 수백 ms 안쪽이면 시계는 원인이 아닙니다. 그때는 API 키 권한·IP 화이트리스트나 주문이 거절되는 다른 이유 쪽을 봐야 합니다. 오차가 초 단위로 나온다면 원인은 시계가 맞습니다.
고치는 순서 — 시계 먼저, 보정은 그다음
대책은 두 층입니다. 하나만 하면 재발합니다.
· 시간 자동 동기화 켜기
· 지금 동기화 실행 → 성공 확인
· 동기화 주기를 짧게(시간 단위)
② 코드에서 오프셋 보정 (권장)
· 시작할 때 서버 시간 1회 측정
· timestamp = 내 시각 + 오프셋
· 수십 분마다 재측정
· 시계 오류 응답을 받으면 즉시 재측정
─────────────
②만 하면
장부·로그 시각이 계속 어긋난다
①만 하면
절전 복귀·동기화 실패에 또 뚫린다
②에서 재측정 주기를 넣지 않는 실수가 흔합니다. 프로그램이 며칠씩 켜져 있으면 시작할 때 잰 오프셋은 낡습니다. 그리고 오프셋 보정이 있어도 OS 시계는 맞춰야 합니다 — 체결 기록과 로그의 시각이 실제와 다르면 나중에 포지션 대조나 손익 검증을 할 때 어느 쪽이 맞는지 알 수 없게 됩니다.
recvWindow를 늘리는 건 해결이 아니다
가장 쉬운 대응은 창을 키우는 것입니다. 실제로 거절은 사라집니다. 문제는 사라진 게 증상뿐이라는 점입니다.
recvWindow = 60,000ms
시계 오차 = 3,000ms → 통과
그런데
네트워크가 20초 멈췄다 복구
→ 20초 전 만든 주문이 지금 체결
그 사이 가격이 움직였다면
내가 의도한 가격이 아니다
─────────────
창은 재전송 방어선이다
넓히면 방어선이 넓어지고
지연된 주문이 뒤늦게 살아난다
시장가 주문이라면 20초 늦게 들어간 진입이 어떤 가격에 체결될지 알 수 없습니다. 이런 지연 체결의 비용은 슬리피지 문제와 같은 성격입니다. 창은 오차를 덮는 도구가 아니라 오래된 요청을 버리는 안전장치이므로, 기본값을 크게 벗어나 키우지 않는 편이 낫습니다.
더 위험한 경우 — 주문은 갔는데 응답만 못 받았다
시계 오류 응답을 보고 곧바로 재시도하도록 짜두면, 상황에 따라 같은 주문이 두 번 들어갑니다.
1) 주문 요청 전송
2) 서버는 정상 접수 · 체결
3) 응답이 오는 길에 타임아웃
4) 내 쪽 기록 = 실패
5) 자동 재시도 → 같은 주문 또 접수
결과
의도 0.1 · 실제 0.2
수량이 두 배 = 손실도 두 배
─────────────
대책
· 주문마다 고유 ID를 붙인다
· 재시도 전에 그 ID로 조회
· 이미 있으면 재전송하지 않는다
거래소는 보통 사용자가 지정하는 주문 ID를 받아주고, 같은 ID의 중복 접수를 거절합니다. 재시도 로직을 넣을 거라면 이 ID를 같이 넣어야 합니다. 실패 응답과 "성공했는데 응답만 못 받음"은 내 쪽에서 구분되지 않기 때문에, 재시도 전 조회가 유일한 구분 방법입니다. 부분 체결이 섞이면 수량 계산은 더 어긋납니다.
봇 점검 항목
자동매매를 돌린다면 아래는 한 번 넣어두면 끝나는 것들입니다.
· 시작 시 서버 시간 오프셋 측정 · 로그 기록
· 주기적 재측정 (예: 30분)
· 오프셋이 임계치 초과면 알림
(예: 1초 넘으면 경고)
· 시계 오류 응답 → 재측정 후 1회 재시도
· 주문마다 고유 ID · 재시도 전 조회
─────────────
가장 중요한 것
시계 오차를 수치로 남긴다
남기지 않으면
주문이 안 나간 이유를 영영 모른다
거절 로그에 에러 코드만 남기고 그때의 오프셋을 남기지 않으면, 나중에 원인을 재구성할 수 없습니다. 시계는 고쳐지고 나면 흔적이 사라지는 종류의 장애입니다.
정리
② 시계가 빠르면 창 폭과 무관하게 전량 거절된다
③ 시계가 느리면 여유가 줄어 변동성 구간에만 실패한다
④ recvWindow는 시계 오차·전송 지연·대기열이 나눠 쓰는 예산이다
⑤ 자동 동기화는 조용히 실패할 수 있다 — 경고가 안 뜬다
⑥ 진단은 서버 시간 조회 한 번으로 오프셋을 숫자로 재면 끝난다
⑦ OS 시계 교정과 코드 오프셋 보정은 둘 다 해야 한다
⑧ 오프셋은 주기적으로 재측정한다 — 시작 때 한 번은 부족하다
⑨ recvWindow를 크게 늘리면 지연된 주문이 뒤늦게 살아난다
⑩ 재시도에는 고유 주문 ID와 조회 후 재전송이 필요하다
한 줄로 줄이면 이렇습니다. API 거래에서 시각은 부가 정보가 아니라 인증의 일부입니다. 코드가 멀쩡한데 주문만 안 나간다면, 코드보다 시계를 먼저 재보는 편이 빠릅니다.
주의
본문의 recvWindow 값, 지연 시간, 오프셋, 수량은 구조를 설명하기 위한 가정 예시이며 특정 거래소의 실제 측정값이 아닙니다. 기본 recvWindow 값과 상한, 서버 시간 엔드포인트, 사용자 지정 주문 ID의 지원 여부와 유효 기간은 거래소마다 다르므로 본인이 쓰는 거래소 문서에서 확인하고 소액으로 직접 시험해야 합니다. 레버리지 거래는 원금 전액을 잃을 수 있으며, 투자 판단과 그 결과는 본인 책임입니다.
NOONOO TRADING 무료 채팅방에서 실시간 트레이딩을 같이 보세요.
무료 채팅방 입장 →📈 OKX 신규 가입 시 거래 수수료 할인
OKX 수수료 할인 가입 →