키움 OpenAPI+ 자동로그인 — 무인 운영 함정 6가지
키움 OpenAPI+ 봇이 몇 주에 한 번씩 아침에만 안 뜨는 가장 흔한 원인은 코드가 아니라 자동로그인과 버전처리의 충돌입니다. 키움 OpenAPI+ 모듈은 비주기적으로 업데이트되고 그 반영 과정을 버전처리라고 부르는데, 자동로그인이 켜져 있으면 버전처리가 정상 수행되지 않습니다. 그래서 업데이트가 없는 날엔 멀쩡하고 있는 날에만 OnEventConnect 의 nErrCode 가 -102 버전처리 실패로 떨어집니다. 무인 운영에서 실제로 걸리는 자리는 여섯입니다. ⑴ 자동로그인은 코드가 아니라 트레이 메뉴 설정이라 PC 를 옮기면 따라오지 않습니다. ⑵ 자동로그인 ON = 버전처리 불가. ⑶ 버전처리 실패의 실체는 opstarter 의 파일 삭제 실패 [183] — 파일 잠금 + 관리자 권한 문제입니다. ⑷ 중복 로그인이 안 되고, 나중에 로그인한 쪽이 이기며 앞 프로세스는 예외 없이 로그오프됩니다. ⑸ 세션은 하루짜리로 보고 매일 재시작을 전제로 짜야 합니다. ⑹ GetConnectState() 하나만 봐서는 죽은 걸 못 잡습니다.
이 글의 순서
- 자동로그인은 코드가 아니라 PC 설정이다
- 자동로그인을 켜면 버전처리가 안 된다 — 핵심
- 파일 삭제 실패 [183] — 버전처리가 실제로 깨지는 자리
- 중복 로그인 금지 — 조용히 무력화되는 봇
- 매일 재시작을 전제로 짠다 — 작업 스케줄러
- 죽은 걸 어떻게 아나 — 워치독 설계
- 키움 REST API 로 가면 뭐가 바뀌나
1. 자동로그인은 코드가 아니라 PC 설정이다
키움 OpenAPI+ 에서 로그인을 시작하는 코드는 CommConnect() 한 줄이고, 결과는 OnEventConnect 콜백으로 옵니다. 여기까지는 자동로그인이 켜져 있든 아니든 똑같습니다. 달라지는 것은 로그인 창이 뜨느냐 마느냐뿐입니다.
# 자동로그인 여부와 무관하게 코드는 이 형태 그대로다.
# 다른 것은 "로그인 창이 뜨는가"뿐 — 즉 코드가 아니라 PC 설정이 바뀐다.
from PyQt5.QAxContainer import QAxWidget
ocx = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
ocx.OnEventConnect.connect(on_event_connect)
ocx.dynamicCall("CommConnect()") # 자동로그인 ON 이면 창 없이 바로 콜백
def on_event_connect(err_code):
# 0 = 정상. 음수는 로그인 자체가 성립하지 않은 것
if err_code == 0:
user = ocx.dynamicCall("GetLoginInfo(QString)", "USER_ID")
accounts = ocx.dynamicCall("GetLoginInfo(QString)", "ACCNO").split(";")
log.info("로그인 성공 user=%s accounts=%s", user, [a for a in accounts if a])
else:
log.error("로그인 실패 nErrCode=%s", err_code)
자동로그인 자체는 키움 OpenAPI+ 모듈의 트레이 메뉴에서 켭니다. 한 번 수동으로 로그인한 뒤 작업 표시줄 트레이의 OpenAPI+ 아이콘을 우클릭해 계좌비밀번호 저장을 열고, 계좌 비밀번호를 넣고 전체계좌에 등록을 누른 다음 AUTO 체크박스를 켜면 끝입니다. (화면 구성은 키움증권 공식 개발가이드에서 확인하십시오. 모듈 업데이트로 메뉴 문구가 바뀔 수 있습니다.)
여기서 첫 번째 사고가 납니다. 자동로그인은 그 PC, 그 윈도우 계정에만 저장됩니다. 코드에도 config.json 에도 흔적이 없습니다. 그래서 개발 PC 에서 잘 돌던 봇을 고객 PC 나 VPS 에 옮기면 로그인 창이 떠서 아무도 없는 화면에서 멈춰 있습니다. 무인 운영 PC 를 새로 세팅했다면 봇을 켜기 전에 자동로그인부터 다시 등록해야 합니다. 키움 비밀번호를 변경한 뒤에도 저장된 계좌 비밀번호를 다시 등록해야 합니다.
2. 자동로그인을 켜면 버전처리가 안 된다 — 이 글의 핵심
키움 OpenAPI+ 모듈은 비주기적으로 업데이트되고, 이 업데이트를 반영하는 과정을 버전처리라고 부릅니다. 그런데 키움 측 안내에 따르면 자동로그인이 설정되어 있으면 버전처리가 제대로 수행되지 않습니다. 버전처리가 필요한 날에는 자동로그인을 해제하고 수동으로 로그인해야 합니다.
무인 운영 관점에서 이 문장이 뜻하는 바는 하나입니다. 봇은 대부분의 날에 잘 돌고, 키움이 모듈을 갱신한 날 아침에만 죽습니다. 그래서 로그는 깨끗하고 재현은 안 되며, 몇 주에 한 번씩 무작위로 터지는 것처럼 보입니다. 실제로 남는 흔적은 OnEventConnect 의 nErrCode 한 줄뿐입니다.
| nErrCode | 의미 | 무인 운영에서 읽는 법 |
|---|---|---|
0 | 정상 | 로그인 성립. 이후 GetConnectState() 가 1 |
-100 | 사용자 정보교환 실패 | 저장된 계좌 비밀번호·계정 정보 문제 가능성 |
-101 | 서버접속 실패 | 네트워크·키움 서버 점검 시간대. 재시도 대상 |
-102 | 버전처리 실패 | 사람이 개입해야 하는 유일한 코드. 재시도해도 계속 실패 |
-101 과 -102 를 구분하지 않고 뭉뚱그려 재시도 루프에 넣는 것이 흔한 실수입니다. -101 은 기다리면 풀리지만 -102 는 몇 번을 재시도해도 풀리지 않습니다. 사람이 버전처리를 끝내 줘야 합니다. 그래서 이 둘은 알림 문구부터 달라야 합니다.
# 로그인 실패를 한 덩어리로 처리하면 -102 를 영원히 재시도하게 된다.
RETRYABLE = {-101} # 서버접속 실패 — 대기 후 재시도
NEEDS_HUMAN = {-100, -102} # 사람이 PC 앞에서 처리해야 풀림
def on_event_connect(err_code):
if err_code == 0:
state.connected = True
return
if err_code in RETRYABLE and state.retry < 5:
state.retry += 1
QTimer.singleShot(60_000, lambda: ocx.dynamicCall("CommConnect()"))
return
if err_code == -102:
notify_telegram(
"[키움] 버전처리 실패(-102) — 자동매매가 시작되지 않았습니다.\n"
"① 키움 관련 프로그램 전부 종료 "
"② 자동로그인 해제 후 수동 로그인으로 버전처리 완료 "
"③ 자동로그인 재설정 후 봇 재시작"
)
return
notify_telegram(f"[키움] 로그인 실패 nErrCode={err_code} — 확인 필요")
3. 파일 삭제 실패 [183] — 버전처리가 실제로 깨지는 자리
버전처리를 하려고 자동로그인을 껐는데도 안 되는 경우가 있습니다. 이때 화면에 뜨는 문구가 이것입니다.
파일 삭제 실패 [183]
업그레이드에 실패하였습니다. 프로그램을 재시작하여 주십시오.
버전처리 프로그램인 opstarter 가 C:\OpenAPI 아래의 khopenapi.ocx 같은 파일을 교체하려다 실패한 것입니다. 원인은 두 가지가 겹칩니다. 첫째, 우리 프로그램이나 KOA Studio 가 OCX 를 로드 중이라 파일이 잠겨 있습니다. 둘째, C:\OpenAPI 는 보호 경로라 파일 교체에 관리자 권한이 필요합니다.
알고랩이 실제 납품 건에서 고객에게 안내하는 순서는 이렇습니다.
- 키움·OpenAPI 관련 프로그램을 전부 종료한다(영웅문, KOA Studio, 봇, 진단 스크립트 포함).
- 바로가기 우클릭 → 관리자 권한으로 실행. 대부분 여기서 끝납니다.
- 그래도 안 되면
C:\OpenAPI\KOAStudioSA를 관리자 권한으로 실행해 버전처리를 먼저 끝내고 봇을 켠다. - 최후 수단은 키움증권 홈페이지에서 OpenAPI+ 모듈 재설치.
우리 프로그램을 재설치하는 것은 의미가 없습니다. 이 오류는 C:\OpenAPI 폴더에서 일어나는 일이고, 봇 재설치는 그 폴더를 건드리지 않습니다. 실제로 고객이 봇을 세 번 재설치하고 나서야 연락을 준 사례가 있었습니다. 예방책은 봇 바로가기의 속성 → 호환성에서 “관리자 권한으로 이 프로그램 실행”을 고정해 두는 것입니다. 그리고 KOA Studio 를 띄워 둔 채로 봇을 켜지 마십시오 — 이 조합이 183 을 가장 자주 만듭니다.
4. 중복 로그인 금지 — 조용히 무력화되는 봇
키움 OpenAPI+ 는 중복 로그인을 허용하지 않습니다. 가장 나중에 로그인한 쪽만 유지되고, 먼저 붙어 있던 프로그램은 자동으로 로그오프됩니다. 문제는 그 로그오프가 예외를 던지지 않는다는 점입니다.
봇 프로세스는 살아 있고 GUI 도 멀쩡한데 조회와 주문만 실패합니다. 장중에 시세를 확인하려고 영웅문을 켜는 순간, 또는 “봇이 잘 도나” 확인하려고 진단 스크립트를 한 번 돌리는 순간 봇이 무력화될 수 있습니다. 알고랩 납품 기준에서는 무인 운영 PC 를 봇 전용으로 두고 확인은 텔레그램 알림으로 하도록 안내합니다.
| 하고 싶은 것 | 키움 OpenAPI+ (OCX) | 키움 REST API |
|---|---|---|
| 봇 + 영웅문 동시 사용 | 불가 — 나중 로그인이 이김 | 영향 없음(별개 시스템) |
| 한 PC 에서 여러 계정 | 불가 — 1계정 1프로세스 | 가능 — 계정별 앱키 발급 |
| 리눅스 서버 상주 | 불가 — 윈도우 OCX 전용 | 가능 |
| 같은 키 두 곳 실행 | 해당 없음 | 불가 — 이전 토큰 즉시 무효 |
OCX 와 REST 의 차이를 통째로 비교한 내용은 키움 REST API 자동매매 가이드와 키움 vs 한국투자증권 KIS API 비교에 정리해 뒀습니다.
5. 매일 재시작을 전제로 짠다
OCX 기반 봇은 세션을 며칠씩 끌고 가도록 설계하지 않는 편이 안전합니다. 버전처리, 키움 서버 점검, 윈도우 업데이트, 메모리 누수까지 하루를 넘기면 변수가 늘어납니다. 실무에서 가장 단순하고 잘 버티는 형태는 매일 장 시작 전에 프로세스를 죽이고 새로 띄우는 것입니다. 윈도우 작업 스케줄러에 아래 배치를 매일 08:00 트리거로 걸어 두면 됩니다.
@echo off
REM kiwoom_bot_daily.bat — 작업 스케줄러 매일 08:00 트리거
REM 작업 등록 시 "가장 높은 수준의 권한으로 실행" 체크 필수(버전처리 파일 교체 때문)
REM 1) 어제 세션이 남아 있으면 정리 — 중복 로그인 방지
taskkill /IM kiwoom_bot.exe /F >nul 2>&1
taskkill /IM KOAStudioSA.exe /F >nul 2>&1
taskkill /IM opstarter.exe /F >nul 2>&1
REM 2) OCX 해제까지 여유를 준다
timeout /t 20 /nobreak >nul
REM 3) 봇 기동 — 로그인 결과는 프로그램이 텔레그램으로 보고한다
cd /d C:\algolab\kiwoom_bot
start "" kiwoom_bot.exe
REM 주의: 이 배치는 "죽이고 켜기"까지만 한다.
REM -102 버전처리 실패는 배치로 해결되지 않는다(4번 문단 참고).
재시작을 걸었다고 안심하면 안 됩니다. 스케줄러는 프로세스가 떴는지만 보장하고 로그인이 됐는지는 보장하지 않습니다. -102 로 로그인이 실패한 상태에서도 프로세스는 정상 실행 중으로 보입니다. “프로세스 살아 있음”을 “봇 정상”으로 읽는 순간 그날 장은 통째로 날아갑니다.
참고로 자동로그인은 업데이트가 즉시 반영되지 않는 경우가 있어, 주 1회 정도는 사람이 수동 로그인을 한 번 해 주는 편이 접속을 안정적으로 만듭니다. 무인이라도 완전 무접촉은 어렵다고 보는 편이 현실적입니다. 장이 열리는 날 자체를 판별하는 문제는 KRX 휴장일 확인과 봇 스케줄링에서 따로 다뤘습니다.
6. 죽은 걸 어떻게 아나 — 워치독 설계
GetConnectState() 는 연결 여부를 1/0 으로 돌려주지만, 이것만으로는 부족합니다. 연결은 살아 있는데 TR 응답이 안 오는 상태, 실시간 시세만 끊긴 상태가 실제로 발생합니다. 최소한 연결 플래그 · 마지막 수신 시각 · 로그인 결과 세 가지를 같이 보십시오.
# 워치독 — 60초마다 세 가지를 함께 본다
import time
HEARTBEAT_TIMEOUT = 180 # 정규장 중 3분간 무수신이면 이상
def watchdog():
now = time.time()
connected = ocx.dynamicCall("GetConnectState()") == 1
silent_for = now - state.last_real_data_ts
if not connected:
notify_telegram("[키움] 연결 끊김 — GetConnectState()=0. 재로그인 시도")
ocx.dynamicCall("CommConnect()")
return
if is_market_open(now) and silent_for > HEARTBEAT_TIMEOUT:
# 연결 플래그는 1인데 실시간이 안 온다 = 중복 로그인으로 밀렸을 가능성
notify_telegram(
f"[키움] 연결은 살아 있으나 {int(silent_for)}초간 실시간 무수신. "
"다른 곳에서 같은 계정으로 로그인했는지 확인하십시오."
)
resubscribe_realtime()
if is_market_open(now) and not state.connected_ok_today:
notify_telegram("[키움] 장이 열렸는데 오늘 로그인 성공 기록이 없습니다.")
알림은 실패했을 때만 보내지 말고, 아침에 정상 기동했을 때도 한 줄 보내는 편이 좋습니다. 무소식이 정상인 시스템에서는 알림 채널 자체가 고장 난 것과 봇이 조용히 죽은 것을 구분할 수 없기 때문입니다. 텔레그램으로 상태를 받는 구성은 텔레그램 봇 모니터링·원격 제어에 정리해 뒀고, 장애가 났을 때의 복구 순서는 자동매매 장애 복구 플레이북에 있습니다.
7. 키움 REST API 로 가면 뭐가 바뀌나
지금까지의 문제는 전부 OCX + 윈도우 로그인 세션이라는 구조에서 나옵니다. 키움 REST API 는 이 구조 자체가 없습니다. 설치할 OCX 도, 로그인 창도, 트레이 설정도 없고 리눅스 서버에서 돕니다. 대신 감시 대상이 로그인 세션에서 토큰과 단말기 등록으로 바뀝니다.
| 운영 항목 | OpenAPI+ (OCX) | 키움 REST API |
|---|---|---|
| 매일 아침 해야 할 일 | 재시작 + 로그인 성립 확인 | 접근토큰 유효성 확인·재발급 |
| 사람이 개입해야 하는 사고 | -102 버전처리 실패 | 지정단말기 미등록 거부 |
| 조용히 무력화되는 경우 | 다른 곳에서 중복 로그인 | 같은 앱키로 토큰 재발급 → 이전 토큰 무효 |
| 실행 환경 | 윈도우 상주 PC 필수 | 리눅스 VPS 가능 |
즉 무인 운영 설계가 필요 없어지는 것이 아니라 감시 항목이 바뀌는 것입니다. REST 쪽 토큰·단말기 등록은 키움 REST API 토큰 만료와 폐기에서, 서버 상주 자체의 비용과 구성은 VPS 24시간 봇 운영 가이드에서 다뤘습니다. 아직 OCX 와 REST 중 무엇으로 만들지 정하지 않았다면 키움 자동매매 프로그램 제작 전 확인 6가지를 먼저 보시는 편이 순서상 맞습니다.
무인 운영 체크리스트
봇을 24시간 PC 에 올리기 전에 이 여섯 줄을 확인하십시오.
- 운영 PC 에서 자동로그인이 등록되어 있는가(개발 PC 설정은 따라오지 않습니다)
- 봇 바로가기가 관리자 권한 실행으로 고정되어 있는가
- 작업 스케줄러 작업이 가장 높은 수준의 권한으로 등록되어 있는가
-102를 재시도하지 않고 사람에게 알리도록 분기되어 있는가- 운영 PC 에서 영웅문·KOA Studio 를 켜지 않기로 합의되어 있는가
- 아침 정상 기동 알림이 오는가(실패 알림만으로는 채널 고장을 못 잡습니다)
자주 묻는 질문
Q. 자동로그인을 아예 안 쓰고 무인 운영할 수 있나요?
화면 자동화로 로그인 창을 대신 눌러 주는 방식은 권하지 않습니다. 창 위치와 문구가 모듈 업데이트로 바뀌면 그대로 깨지고, 실패해도 실패한 줄 모릅니다. 자동로그인을 쓰되 버전처리 날만 사람이 개입하는 쪽이 훨씬 안정적입니다.
Q. 버전처리는 얼마나 자주 있나요?
키움 측이 필요할 때 비주기적으로 배포하므로 주기를 특정할 수 없습니다. 그래서 “언제 오는지”를 예측하려 하지 말고 왔을 때 즉시 알림이 오게 만드는 것이 정답입니다. 공지 일정은 키움증권 공식 안내를 확인하십시오.
Q. VPS 에 올리면 자동로그인이 유지되나요?
윈도우 VPS 라면 유지됩니다. 다만 원격 데스크톱 세션이 끊길 때 화면이 없어져 OCX 동작이 영향을 받는 구성이 있으므로, 세션 유지 방식을 확인하고 실제로 하루 이상 돌려 본 뒤 실전에 넣으십시오. 리눅스 VPS 에서는 OCX 가 동작하지 않습니다.
Q. 이 문제 때문에 손실이 났다면 증권사 책임인가요?
이 글은 법률 자문이 아닙니다. 다만 실무적으로는 API 는 도구로 제공되고 운영 책임은 이용자에게 있는 구조가 일반적입니다. 그래서 “봇이 안 떴다”를 즉시 아는 장치가 손실을 줄이는 유일하게 확실한 방법입니다. 약관과 책임 범위는 키움증권 공식 문서를 확인하십시오.
고지. 이 글은 자동매매 프로그램의 운영·기술 자료이며 특정 종목이나 매매 전략을 권유하지 않습니다. 본문의 메뉴 이름·오류 코드·동작은 알고랩이 납품 과정에서 확인한 내용이지만, 키움 OpenAPI+ 모듈은 비주기적으로 갱신되므로 실제 화면과 코드 값은 반드시 키움증권 공식 개발가이드로 대조하십시오. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 고객이 지정한 규칙을 프로그램으로 구현해 드리는 도구 제공자입니다.