TradingView Pine Script 웹훅을 실거래로 연결하는 3가지 방법
TradingView 의 Pine Script 에서 alert() 로 웹훅 URL 에 POST 요청을 쏘는 건 10분이면 배웁니다.
문제는 "그 웹훅을 어디서 받아서 실제 증권사/거래소 주문으로 변환할 것인가" 입니다.
이 글에서는 실전에서 자주 쓰는 3가지 방법을 비교하고, 각 방법의 장단점과 실패 패턴을 알려드립니다.
전제 — TradingView 웹훅이 요구하는 것
TradingView alert() 가 웹훅 POST 를 보낼 때 요구사항은 3가지입니다.
- 공개 접근 가능한 URL — TradingView 서버가 직접 POST. 내 로컬 PC IP는 접근 불가
- HTTPS 필수 — HTTP는 거부
- 응답 1초 이내 반환 — 길면 타임아웃으로 신호 누락. 실 주문 처리는 비동기로
이 3조건을 만족하는 "웹훅 수신기" 가 있어야 하고, 수신기에서 받은 JSON 을 파싱해 증권사 API 로 주문을 넣는 구조가 필요합니다.
방법 1. ngrok — 가장 쉬운 시작, 가격 이슈 있음
ngrok 은 로컬 서버(예: Flask on localhost:5000) 를 공개 HTTPS URL 로 노출해주는 터널 서비스입니다. 본인 PC 에서 Python Flask 돌리고 ngrok 한 줄이면 TradingView 웹훅 수신 가능.
# 1. 로컬 Flask 서버 (Python)
from flask import Flask, request
app = Flask(__name__)
@app.post("/webhook")
def webhook():
data = request.json
# → 키움/바이낸스 API 호출
return {"ok": True}
app.run(port=5000)
# 2. ngrok 실행 (다른 터미널)
ngrok http 5000
# → https://abc123.ngrok-free.app 같은 공개 URL 생성
장점
- 10분이면 셋업 완료. 튜토리얼 넘침.
- 로컬에서 개발·디버깅 편함.
단점 (실전에서 만나는 문제)
- 무료 플랜 URL 은 매번 바뀜. 재시작마다 TradingView alert 설정을 새 URL로 교체해야 함
- 고정 URL 원하면 월 $8+ 유료 플랜
- 무료 플랜은 동시 접속 1개·시간당 요청 한도 있음
- 로컬 PC 꺼지면 자동매매 중단
방법 2. Cloudflare Quick Tunnel — 무료·무계정·고정 필요 없으면 최고
Cloudflare 의 cloudflared 바이너리로 만드는 "Quick Tunnel". 계정·토큰·가입 전부 불필요.
실행하면 https://xxx.trycloudflare.com 공개 URL 이 나옵니다.
# 1. cloudflared 다운로드 (Windows)
# https://github.com/cloudflare/cloudflared/releases
# 2. 실행 (tcp 5000 로컬 → 공개 URL 매핑)
cloudflared tunnel --url http://localhost:5000
# stdout에 https://abc-xyz.trycloudflare.com 출력
알고랩에서 제작한 키움 자동매매 프로젝트들은 대부분 이 방식을 기본으로 탑재합니다.
프로그램 시작 시 cloudflared 를 subprocess 로 실행하고 stdout 을 파싱해서 URL 추출 → 고객이 TradingView alert 에 복붙.
장점
- 완전 무료·무계정. 가입 절차 없음
- cloudflared 바이너리 하나로 끝. 설정 파일 불필요
- Cloudflare 인프라라 안정적
단점
- URL 이 실행 때마다 바뀜. 매번 TradingView alert 에 URL 재입력 필요
- 장시간 실행 시 간헐적 재연결 발생 (재시작 로직 필요)
- 고정 URL 원하면 Cloudflare 무료 계정 + Named Tunnel 설정 필요 (복잡도 상승)
방법 3. 중개 서버 (Cloudflare Worker / VPS) — 가장 안정적
터널 없이 처음부터 공개 서버에 웹훅 수신기를 올리는 방식. 이 구조가 가장 안정적이고 URL 도 영구적입니다.
가장 가성비 좋은 조합: Cloudflare Workers
- 무료 플랜 월 10만 requests, 충분
- HTTPS 자동, 커스텀 도메인 무료
- 코드 20줄로 webhook 수신기 구성
- 수신한 웹훅을 큐에 쌓아두면 로컬 자동매매 프로그램이 폴링해서 주문 실행
// Cloudflare Worker 예시 (간소화)
export default {
async fetch(request, env) {
if (request.method !== "POST") return new Response("OK");
const data = await request.json();
// 1. 웹훅 저장 (D1·KV·R2 아무거나)
await env.WEBHOOK_KV.put(`sig:${Date.now()}`, JSON.stringify(data));
return new Response(JSON.stringify({ ok: true }));
}
}
로컬 자동매매 프로그램은 Worker 의 다른 엔드포인트를 2~5초마다 폴링해서 새 웹훅을 받아 주문 실행.
장점
- URL 영구 고정 (한 번 설정하면 끝)
- 고객 PC 가 꺼져도 웹훅은 큐에 누적 — PC 재시작 시 밀린 것부터 처리
- 감사 로그·재전송 로직 등 확장 용이
- 무료 플랜으로 월 10만 웹훅까지
단점
- 셋업 난이도 중간 (Worker 배포·커스텀 도메인 연결)
- 폴링 구조라 1~5초 latency 추가 (스캘핑엔 부적합, 분봉 이상은 무관)
웹훅을 받은 다음 — 국내주식(키움·KIS) 주문으로 바꾸는 부분
위 세 방법은 전부 "신호를 받는 데까지"입니다. 실제로 막히는 곳은 그다음입니다.
코인 거래소는 웹훅 본문을 그대로 주문 파라미터로 넘기기 쉬운 편이지만, 국내주식은 증권사 API의 인증·헤더 체계를 한 번 거쳐야 합니다.
키움 REST API와 한국투자증권 KIS API 둘 다
액세스 토큰을 먼저 받고, 주문마다 업무를 구분하는 헤더(KIS는 tr_id, 키움은 api-id)를 붙여야 합니다.
1단계 — Pine Script alert 메시지를 파싱 가능한 JSON으로
알림 메시지를 자유 문장으로 두면 수신 측 파싱이 매번 깨집니다. 처음부터 JSON으로 고정하는 편이 낫습니다.
Pine Script의 alert() 메시지 칸에 아래처럼 넣습니다.
{
"secret": "발급받은_공유_비밀문자열",
"id": "005930-BUY-{{timenow}}",
"code": "005930",
"side": "BUY",
"qty": 10,
"price": {{close}},
"strategy": "sma-cross-10-34"
}
id가 멱등키입니다. 종목·방향·봉 시각을 합쳐 같은 신호는 같은 값이 되도록 만듭니다.
secret은 아래에서 설명할 인증용입니다.
2단계 — 수신부: 검증하고 큐에 넣고 즉시 응답
수신 함수 안에서 증권사 주문까지 끝내려 하면 TradingView 타임아웃에 걸립니다.
받아서 검증하고 큐에 넣은 뒤 바로 200을 돌려주고, 주문은 워커가 처리하는 구조가 정석입니다.
import queue
from flask import Flask, request, abort
app = Flask(__name__)
SIGNALS = queue.Queue()
SEEN = set() # 멱등키 저장소 (운영은 파일/DB 권장)
ALLOWED = {"005930", "000660"} # 종목 화이트리스트
@app.post("/webhook")
def webhook():
d = request.get_json(silent=True) or {}
if d.get("secret") != WEBHOOK_SECRET: # 1) 비밀키 검증
abort(403)
if d.get("code") not in ALLOWED: # 2) 종목 화이트리스트
abort(400)
if d.get("id") in SEEN: # 3) 중복 신호 차단
return {"ok": True, "skipped": "duplicate"}
SEEN.add(d["id"])
SIGNALS.put(d) # 4) 큐에 넣고 즉시 반환
return {"ok": True}
비밀키 검증을 빼먹지 마세요. 웹훅 URL은 공개 주소입니다. URL만 알면 누구나 POST로 임의의 주문 신호를 밀어 넣을 수 있습니다. secret 대조와 종목 화이트리스트, 이 두 줄이 최소 방어선입니다. 그리고 증권사 appkey·appsecret은 웹훅 수신 서버가 아니라 주문 실행 쪽에만 두세요. 알림 메시지나 저장소에 API 키를 넣는 순간 그건 공개된 것과 같습니다.
3단계 — 워커: 장 시간을 확인하고 증권사 주문으로 번역
여기가 국내주식에서 가장 많이 깨지는 지점입니다. TradingView 알림은 새벽 3시에도 발생합니다. 그때 그대로 주문을 던지면 접수 자체가 거부됩니다. 게다가 지금은 국내주식 거래소가 KRX 하나가 아니라 KRX·NXT 둘이라, 주문 요청에 어느 거래소로 보낼지를 넣어야 합니다.
import requests, datetime
def worker():
while True:
sig = SIGNALS.get()
now = datetime.datetime.now().time()
if not is_tradable(now): # 장 시간 밖이면 버리거나 보류
log.warning("장 시간 외 신호 폐기: %s", sig["id"])
continue
body = {
"dmst_stex_tp": "SOR", # KRX | NXT | SOR
"stk_cd": sig["code"],
"ord_qty": str(sig["qty"]),
"ord_uv": "", # 시장가면 공란
"trde_tp": "3",
}
r = requests.post(
"https://api.kiwoom.com/api/dostk/ordr",
headers={
"authorization": f"Bearer {access_token}",
"api-id": "kt10000" if sig["side"] == "BUY" else "kt10001",
"Content-Type": "application/json;charset=UTF-8",
},
json=body, timeout=5,
)
log.info("주문 응답 %s %s", r.status_code, r.text)
KIS API를 쓴다면 헤더 체계가 다릅니다. appkey·appsecret과 함께
매수·매도를 구분하는 tr_id를 넣고, 응답은 HTTP 상태가 아니라 본문의 rt_cd로 성패를 판정해야 합니다.
호출이 몰리면 EGW00201(초당 거래건수 초과)이 떨어지므로
웹훅이 연속으로 들어오는 전략이라면 리미터를 함께 두는 편이 안전합니다.
증권사 API 규격은 개정이 잦습니다. 위 파라미터명과 엔드포인트는 작성 시점 기준이며, 적용 전 각 증권사 개발자센터 문서에서 현재 규격을 확인하시기 바랍니다. 증권사별 차이는 증권사 API 비교에 정리해 두었습니다.
국내주식 연동에서 자주 나는 4가지
| 증상 | 원인 | 대응 |
|---|---|---|
| 같은 신호로 두 번 체결 | TradingView 재전송 · 중개 서버 재시도 | 멱등키(id) 중복 차단 |
| 밤에 신호가 쌓였다가 아침에 몰림 | 장 시간 검사 없음 | 워커에서 폐기 또는 시가 재평가 |
| 주문이 조용히 실패 | HTTP 200만 보고 rt_cd 미검사 | 응답 본문 판정 + 로그 |
| 신호는 오는데 체결가가 크게 벌어짐 | 웹훅 지연 + 시장가 | 지정가 전환 · 허용 슬리피지 상한 |
순서를 지키면 대부분 예방됩니다. 웹훅 수신 확인 → 모의투자 계좌로 주문까지 관통 → 최소 수량 실계좌 → 정상 운영. 특히 모의에서 주문 응답을 눈으로 확인하기 전에는 실계좌를 붙이지 마세요. 웹훅은 잘 오는데 주문 헤더 하나가 틀려서 안 나가는 경우가 가장 흔합니다.
결론 — 어떤 걸 써야 하나
| 상황 | 추천 |
|---|---|
| 1회성 테스트·개발 | ngrok (무료 플랜) |
| 본인이 매번 재실행 OK · 단독 사용 | Cloudflare Quick Tunnel |
| 장기 운영 · 고객 납품 | Cloudflare Workers 중개 서버 |
| 대규모 · 다중 사용자 | VPS + Nginx + Worker 혼합 |
실전 참고: 알고랩에서 제작하는 TradingView 연동 자동매매는 기본적으로 Cloudflare Quick Tunnel 을 자동 실행하는 구조로 제작합니다. 개인 사용자에겐 이 정도로 충분하고, 상업 운영용이면 Workers 중개 서버로 업그레이드합니다. 자세한 견적은 상담에서.