📌 이 글에서 배울 수 있는 것
- 토스증권 Open API 인증부터 실시간 시세 조회까지 전 과정
- 중복 코드 없는 공통 모듈(
toss_common.py) 설계 원칙 - 한글·달러 혼합 포트폴리오를 터미널에 정확히 정렬 출력하는 방법
- 실제 돈을 지키는 3중 안전장치 설계 패턴
- API 키를 안전하게 관리하는
.env보안 처리
1. 왜 토스증권 API인가?
국내 증권사 중 개인 개발자가 접근하기 가장 쉬운 Open API를 제공하는 곳이 토스증권입니다. OAuth 2.0 Client Credentials 방식으로 토큰을 발급받고, REST API로 실시간 시세·잔고·주문까지 모두 처리할 수 있습니다.
특히 해외주식 소수점 투자까지 API로 지원하기 때문에, 원화와 달러 혼합 포트폴리오를 하나의 코드로 관리할 수 있다는 점이 큰 장점입니다.
| 항목 | 내용 |
|---|---|
| API 방식 | REST API + OAuth 2.0 |
| 인증 방식 | Client Credentials (서버 to 서버) |
| 지원 기능 | 토큰 발급 / 계좌 조회 / 잔고 조회 / 실시간 시세 / 주문 |
| 해외주식 | 소수점 투자 포함 API 지원 |
| 개발자 콘솔 | 별도 신청 필요 (토스증권 앱 → 설정 → Open API) |
2. 사전 준비물
2-1. 필수 조건
- ✅ 토스증권 계좌 개설 완료
- ✅ 토스증권 앱 → 개발자 콘솔에서 Open API 신청 및
CLIENT_ID/CLIENT_SECRET발급 - ✅ Python 3.10 이상 설치
2-2. 패키지 설치
pip install requests python-dotenv
또는 requirements.txt를 프로젝트 루트에 만들고 한 번에 설치합니다.
# requirements.txt
requests>=2.31.0
python-dotenv>=1.0.0
pip install -r requirements.txt
3. 완성된 프로젝트 구조
📁 toss_auto_trader/
├── toss_common.py ← 공통 모듈 (인증·시세·계좌 함수 집결)
├── toss_portfolio.py ← 실시간 포트폴리오 대시보드
├── auto_trader.py ← 자동매매 감시 엔진 (3중 안전장치)
├── get_price.py ← 다종목 실시간 시세 조회
├── toss_test.py ← API 연결 진단 도구
├── .env ← API 자격증명 (Git 추적 제외 필수)
├── .env.example ← .env 템플릿
├── .gitignore ← .env 등 민감 파일 추적 방지
└── requirements.txt ← 의존 패키지 목록
처음에는 단일 파일로 시작하기 쉽지만, 파일이 늘어나면 get_access_token() 같은 함수가 파일마다 복붙되는 상황이 발생합니다. 이를 방지하기 위해 공통 모듈을 중심에 두는 구조가 핵심입니다.
4. 🔒 API 키 보안 처리 — 절대 코드에 하드코딩하지 마세요
가장 먼저 해결해야 할 것이 보안입니다. API 키를 코드에 직접 입력하면 GitHub 등에 실수로 노출되는 순간 계좌가 위험에 처합니다.
❌ 절대 하면 안 되는 방식
# 위험! 키값이 코드에 노출됨
CLIENT_ID = "tsck_live_여기에직접입력"
CLIENT_SECRET = "tssk_live_여기에직접입력"
✅ 올바른 방식: .env 파일 분리
프로젝트 루트에 .env 파일을 만들고 키를 넣습니다.
# .env (절대 Git에 커밋하지 말 것!)
TOSSINVEST_CLIENT_ID=tsck_live_여기에_발급받은_키_입력
TOSSINVEST_CLIENT_SECRET=tssk_live_여기에_발급받은_키_입력
그리고 .gitignore에 반드시 추가합니다.
# .gitignore
.env
코드에서는 python-dotenv로 읽어옵니다.
import os
from dotenv import load_dotenv
load_dotenv()
CLIENT_ID = os.environ.get("TOSSINVEST_CLIENT_ID")
CLIENT_SECRET = os.environ.get("TOSSINVEST_CLIENT_SECRET")
# 키가 없으면 즉시 실행 차단
if not CLIENT_ID or not CLIENT_SECRET:
raise EnvironmentError("🚨 .env 파일의 API 키를 확인하세요.")
5. 공통 모듈 toss_common.py — 중복의 뿌리를 뽑아라
토스 API를 다루다 보면 모든 파일에서 반드시 필요한 두 가지 작업이 있습니다. 바로 Access Token 발급과 계좌 일련번호 조회입니다. 이 둘을 공통 모듈에 한 번만 작성하고, 나머지 파일은 import만 하면 됩니다.
인증 전체 흐름을 한 줄로
import toss_common
# 토큰 발급 → 계좌번호 조회를 한 번에
token, account_seq = toss_common.authenticate()
if not token or not account_seq:
exit(1)
내부적으로는 아래 두 단계가 자동으로 진행됩니다.
def get_access_token() -> str | None:
"""OAuth 2.0 토큰 발급"""
url = "https://openapi.tossinvest.com/oauth2/token"
payload = {
"grant_type" : "client_credentials",
"client_id" : CLIENT_ID,
"client_secret": CLIENT_SECRET,
}
response = requests.post(url, data=payload,
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=5)
if response.status_code == 200:
return response.json().get("access_token")
return None
공통 모듈에는 이 밖에도 get_current_price()(단일 종목 시세)와 get_current_prices()(다종목 일괄 시세)가 포함되어 있어, 각 파일은 비즈니스 로직에만 집중할 수 있습니다.
6. 실시간 포트폴리오 대시보드 구축
python toss_portfolio.py를 실행하면 다음과 같은 대시보드가 출력됩니다.
========================================================================
📊 토스증권 실시간 포트폴리오
========================================================================
💰 총 매수금액 : 1,360,070 원
📈 총 평가금액 : 1,369,296 원
✨ 총 평가손익 : +9,226 원 (+0.67%)
📅 전일 대비 : +32,391 원 (+1.99%)
========================================================================
종목명 (코드) | 수량 | 평균단가 | 현재가 | 수익률
------------------------------------------------------------------------
삼성전자 (005930) | 10 | ₩ 75,000.0 | ₩ 76,200.0 | +1.60%
SK하이닉스 (000660) | 5 | ₩ 185,000.0 | ₩ 188,500.0 | +1.89%
[가상 예시 종목 C] (XX… | 8 | ₩ 25,000.0 | ₩ 24,500.0 | -2.00%
[해외주식 예시] (ABCD) | 0.42 | $ 150.00 | $ 155.00 | +3.33%
========================================================================
한글·이모지 정렬 문제 해결법
Python의 기본 ljust()는 바이트 수 기준으로 정렬해서, 한글이 포함되면 열이 어긋납니다. unicodedata.east_asian_width()를 이용해 문자별 실제 터미널 표시 너비를 정확히 계산하는 함수를 만들었습니다.
import unicodedata
def get_display_width(text: str) -> int:
"""
'W'(Wide) / 'F'(Fullwidth) → 2칸 : 한글, 한자 등 전각 문자
그 외 ('Na','N','H','A') → 1칸 : ASCII, ₩, … 등 반각 문자
"""
return sum(2 if unicodedata.east_asian_width(c) in ('W', 'F') else 1
for c in text)
def fit_to_width(text: str, width: int) -> str:
"""길면 '…'으로 잘라내고, 짧으면 공백 패딩 — 항상 width 칸 고정"""
dw = get_display_width(text)
if dw <= width:
return text + " " * (width - dw)
target = width - get_display_width("…")
result, cur_w = "", 0
for ch in text:
ch_w = 2 if unicodedata.east_asian_width(ch) in ('W', 'F') else 1
if cur_w + ch_w > target:
break
result += ch
cur_w += ch_w
result += "…"
return result + " " * (width - get_display_width(result))
💡 함정 주의:
₩(U+20A9)는ord > 127이지만 유니코드 규격상H(Halfwidth, 반각) = 1칸입니다.ord(c) > 127 → 2칸단순 공식을 쓰면 가격 열이 1칸씩 어긋나는 버그가 발생합니다. 반드시unicodedata를 사용하세요.
7. 🤖 자동매매 엔진 — 3중 안전장치 설계
자동매매에서 가장 중요한 것은 실수로 실제 주문이 나가는 것을 막는 것입니다. 아무리 코드가 완성되어도, 충분한 테스트 없이 실전 모드를 켜는 것은 매우 위험합니다.
이 프로젝트에서는 3단계 안전장치를 기본 탑재합니다.
🔒 안전 1단계 — DRY_RUN 플래그
DRY_RUN = True # ✅ True = 시뮬레이션 모드 (실제 주문 절대 불가)
True인 상태에서는 조건이 충족되어도 실제 토스 서버에 주문 요청을 보내지 않고, 로그와 텔레그램 알림만 발송합니다.
🔒 안전 2단계 — 1원 목표가
TARGET_BUY_PRICE = 1 # ✅ 시장에서 절대 체결될 수 없는 안전 목표가
삼성전자 1원 매수 지정가 주문은 실제 시장에서 절대 체결되지 않습니다. DRY_RUN을 끄더라도 이 가격 덕분에 이중으로 보호됩니다.
🔒 안전 3단계 — 주문 API 물리적 주석 처리
def execute_order(token, account_seq, symbol, ...):
# ✅ 시뮬레이션 모드: 주문 없이 알림만 발송
if DRY_RUN:
send_notification(f"[시뮬레이션] {symbol} 매수 조건 충족!")
return True
# 🔒 실제 주문 API 호출부 — 물리적 주석 처리 (해제 금지)
# url = "https://openapi.tossinvest.com/api/v1/orders"
# response = requests.post(url, json=payload, headers=headers)
# return response.status_code in [200, 201]
실제 HTTP 요청을 보내는 requests.post() 라인이 주석 처리되어 있어, DRY_RUN을 실수로 False로 바꾸더라도 주문이 나가지 않는 물리적 차단 장치입니다.
감시 루프의 네트워크 장애 대응
# 연속 오류가 MAX_CONSECUTIVE_ERRORS(10회)를 초과하면 봇 강제 종료
MAX_CONSECUTIVE_ERRORS = 10
while True:
current_price = toss_common.get_current_price(token, account_seq, TARGET_SYMBOL)
if isinstance(current_price, int):
# 정상 시세 처리
...
elif current_price == "TOKEN_EXPIRED":
# 토큰 만료 → 토큰 + 계좌번호 동시 재발급
token, account_seq = toss_common.authenticate()
else:
# 네트워크 오류 → 카운터 증가 후 재시도
consecutive_errors += 1
if consecutive_errors >= MAX_CONSECUTIVE_ERRORS:
send_notification("🚨 연속 오류 한계 초과! 봇 종료.")
break
8. 📱 텔레그램 알림 연동
자동매매 봇이 24시간 돌아가는 동안, 중요한 상태 변화를 스마트폰으로 바로 받을 수 있도록 텔레그램 알림을 연동합니다. .env에 아래 두 값을 추가하면 자동 활성화됩니다.
# .env
TELEGRAM_TOKEN=your_bot_token_from_BotFather
TELEGRAM_CHAT_ID=your_chat_id
봇 토큰은 텔레그램에서 @BotFather에게 /newbot 명령으로, 채팅 ID는 @userinfobot으로 간단히 발급받을 수 있습니다.
알림이 발송되는 시점은 다음과 같습니다.
- 🤖 봇 최초 가동 시
- ⚠️ 네트워크 오류 감지 시
- 🔄 토큰 만료 재발급 시
- ✅ 매수 조건 충족 시 (시뮬레이션 or 실전)
- 🚨 봇 강제 종료 시
9. 📋 로그 파일 자동 저장
모든 print()를 logging 모듈로 교체하여, 콘솔 출력과 동시에 toss_trader.log 파일에 타임스탬프와 함께 자동 저장됩니다.
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[
logging.FileHandler("toss_trader.log", encoding="utf-8"),
logging.StreamHandler() # 콘솔 동시 출력
]
)
로그 파일이 쌓이면 과거의 매매 이력과 오류 내역을 언제든지 검색하고 분석할 수 있습니다.
10. 실행 순서 — 처음 시작하는 분들을 위한 가이드
# 1. 패키지 설치
pip install -r requirements.txt
# 2. .env 파일 생성 후 API 키 입력
cp .env.example .env
# 메모장 등으로 .env 열어 키 값 입력
# 3. 연결 진단 (API 정상 작동 확인)
python toss_test.py
# 4. 포트폴리오 대시보드 확인
python toss_portfolio.py
# 5. 자동매매 봇 시뮬레이션 가동 (DRY_RUN=True 상태)
python auto_trader.py
⚠️ 주의:
auto_trader.py는 반드시DRY_RUN = True,TARGET_BUY_PRICE = 1상태에서 충분히 테스트한 후, 신중하게 실전 전환 여부를 결정하세요. 자동매매는 예상치 못한 손실이 발생할 수 있으며, 이 글의 코드는 교육 목적으로 제공됩니다.
11. 마무리 및 다음 단계
이번 글에서는 토스증권 Open API를 연동하여 포트폴리오 대시보드와 3중 안전장치가 탑재된 자동매매 엔진을 파이썬으로 구축하는 전 과정을 다뤘습니다.
다음 편에서는 아래 내용을 다룰 예정입니다.
- 📈 이동평균(MA) / RSI 기반 매수 조건 고도화
- 📊 과거 데이터 백테스팅으로 전략 검증
- 🕐 장 시작·종료 시간 자동 감지 로직
- ☁️ 클라우드(GCP / AWS)에 봇 24시간 배포
전체 소스코드는 블로그 댓글 또는 문의를 통해 공유 가능합니다. 궁금한 점은 댓글로 남겨주세요! 🙌
※ 이 글의 코드는 교육 목적으로 작성되었습니다. 실제 자동매매에는 충분한 검증과 리스크 관리가 필요하며, 투자 손실에 대한 책임은 본인에게 있습니다.