증권사 REST API로 파이썬 자동매매 첫걸음 — 인증·잔고조회 개념
이 글의 범위
증권사마다 REST API의 세부 스펙(엔드포인트 이름, 파라미터, 헤더 형식)은 조금씩 다르다. 이 글은 특정 증권사의 실제 엔드포인트나 계좌 정보를 다루지 않는다. 대신 국내 대부분의 증권사 REST API가 공통으로 따르는 일반적인 흐름—토큰 발급, 인증 헤더 구성, 잔고 조회—을 개념과 의사코드 위주로 정리한다. 실제 연동은 반드시 해당 증권사가 공개한 공식 API 문서를 기준으로 진행해야 한다.
왜 "인증"부터 막히는가
자동매매를 처음 시도할 때 대부분 여기서 막힌다. 시세를 보거나 주문을 넣기 전에, API 서버에게 "나는 이 계좌의 소유자다"라는 걸 증명해야 하는데 이 절차가 웹 로그인과는 다르게 느껴지기 때문이다. 하지만 뜯어보면 대부분 OAuth 2.0과 비슷한 패턴을 따른다.
일반적인 흐름은 이렇다.
- 증권사 개발자센터에서 앱을 등록하고 App Key / App Secret 같은 식별자 쌍을 발급받는다
- 이 식별자 쌍으로 접근 토큰(Access Token) 발급을 요청한다
- 발급받은 토큰을 이후 모든 요청의 헤더에 실어서 보낸다
- 토큰에는 유효기간이 있어서, 만료되면 재발급받아야 한다
여기서 App Key/Secret이나 토큰은 비밀번호와 동급으로 취급해야 한다. 코드에 하드코딩하거나 공개 저장소에 커밋하면 안 되고, 환경변수나 별도 설정 파일(그리고 그 파일을 .gitignore에 반드시 추가)로 분리해서 관리하는 게 표준적인 방식이다.
토큰 발급 — 의사코드
python 코드 보기
import os
class BrokerAuthClient:
def __init__(self, app_key: str, app_secret: str, base_url: str):
self.app_key = app_key
self.app_secret = app_secret
self.base_url = base_url
self._token = None
self._token_expires_at = None
def get_access_token(self) -> str:
"""토큰이 없거나 만료됐으면 재발급, 아니면 캐시된 토큰 반환"""
if self._token and not self._is_expired():
return self._token
response = self._request_new_token()
self._token = response["access_token"]
self._token_expires_at = response["expires_at"]
return self._token
def _request_new_token(self) -> dict:
"""실제로는 증권사 API 문서에 명시된 토큰 발급 엔드포인트에 POST"""
raise NotImplementedError
def _is_expired(self) -> bool:
raise NotImplementedError
# 사용 예 — 키는 반드시 환경변수 등 외부에서 주입
auth = BrokerAuthClient(
app_key=os.environ["BROKER_APP_KEY"],
app_secret=os.environ["BROKER_APP_SECRET"],
base_url="https://api.example-broker.com",
)os.environ["BROKER_APP_KEY"]처럼 환경변수에서 읽어오게 만들어두면, 코드 자체에는 어떤 민감정보도 남지 않는다. 이 패턴은 특정 증권사가 아니라 REST API 인증을 다루는 대부분의 서비스에 공통으로 적용된다.
인증 헤더로 잔고 조회하기 — 의사코드
토큰을 받았으면 이후 요청에는 그 토큰을 헤더에 실어 보낸다. 잔고 조회도 마찬가지다.
python 코드 보기
class AccountClient:
def __init__(self, auth_client: BrokerAuthClient):
self.auth_client = auth_client
def get_balance(self, account_no_masked: str) -> dict:
token = self.auth_client.get_access_token()
headers = {
"Authorization": f"Bearer {token}",
"appkey": self.auth_client.app_key,
}
# 실제로는 requests.get(url, headers=headers, params=...) 형태
response = self._call_balance_endpoint(headers, account_no_masked)
return {
"cash": response["cash"],
"holdings": response["holdings"],
}
def _call_balance_endpoint(self, headers, account_no_masked):
raise NotImplementedError잔고 조회 응답에는 보통 예수금(현금), 보유 종목별 수량과 평가금액이 담긴다. 이 데이터를 앞서 다룬 리스크 관리 로직(포지션 한도 계산 등)의 입력값으로 쓰는 게 자연스러운 다음 단계다.
처음 연동할 때 자주 하는 실수
- 키를 코드에 직접 적어 커밋하는 것: 한 번 공개 저장소에 올라가면 즉시 무효화하고 재발급해야 한다. 이력에서 지우는 것만으로는 부족하다.
- 토큰 만료를 고려하지 않는 것: 장시간 실행되는 자동매매 프로세스는 토큰이 중간에 만료될 수 있다는 걸 전제로, 재발급 로직을 반드시 넣어야 한다.
- 모의투자(페이퍼 트레이딩) 환경과 실전 환경의 엔드포인트를 혼동하는 것: 대부분의 증권사는 두 환경의 base URL이 다르다. 실전 URL로 잘못 요청을 보내는 실수는 생각보다 흔하다.
- 응답 에러 처리를 생략하는 것: 네트워크 오류, 토큰 만료, 요청 한도 초과 등 다양한 실패 케이스가 있는데 "성공"만 가정하고 코드를 짜면 조용히 잘못된 상태로 흘러갈 수 있다.
오늘의 정리
- 증권사 REST API 인증은 대체로 App Key/Secret으로 토큰을 발급받고, 이후 요청 헤더에 그 토큰을 싣는 공통 패턴을 따른다.
- 키와 토큰은 코드가 아니라 환경변수 등 외부에서 주입하는 구조로 처음부터 설계해야 한다.
- 잔고 조회는 인증만 되면 비교적 단순하지만, 그 결과값을 리스크 관리 로직에 연결하는 지점부터가 진짜 설계다.
다음 글에서는 이렇게 얻은 데이터와 전략을 실전에 투입하기 전, 반드시 거쳐야 하는 백테스팅이 무엇이고 왜 없으면 위험한지를 개념 위주로 다뤄본다.
이 글의 코드는 개념 설명을 위한 의사코드이며, 특정 증권사의 실제 엔드포인트·계좌·API 키를 담고 있지 않습니다. 실제 연동은 해당 증권사가 공개한 공식 API 문서를 기준으로 진행하시기 바랍니다. 이 글은 투자 조언이 아니며 특정 종목이나 매매기법을 추천하지 않습니다. 수익을 보장하지 않으며, 투자 손실에 대한 책임은 투자자 본인에게 있습니다.