AlgoLab Blog · 완전 백과 · 2026 최신

트레이딩뷰 자동매매 완전 가이드 — Pine Script부터 웹훅·증권사·거래소 연동까지

완전 백과 2026-06-07 · 약 25분 읽기 · 알고랩 AlgoLab
한 줄 정리 이 글은 트레이딩뷰로 자동매매를 시작하는 처음부터 끝까지를 정리한 한국어 완전 가이드입니다. Pine Script 전략 작성 → 알람 설정 → 웹훅 서버(Flask) 구축 → 키움·KIS·바이낸스·업비트·빗썸 5개 플랫폼 연동 → 보안·운영까지, 모든 단계를 Python 풀코드와 함께 다룹니다. 차트는 트레이딩뷰가, 실거래는 본인 봇이 — 이 분담의 모든 것을 한 자리에서.

트레이딩뷰는 한국 트레이더에게 가장 친숙한 차트 플랫폼이고, "내 차트에서 신호가 나오면 자동으로 매매됐으면" 이라는 욕구는 자동매매 의뢰 1순위 패턴입니다. 그러나 트레이딩뷰는 차트·알람만 담당하고 실제 주문은 본인이 만든 서버에서 처리해야 합니다. 그 분담의 전체 그림을 처음부터 끝까지 정리한 한국어 자료는 의외로 적습니다.

이 글은 입문자가 따라가면 작동하는 트레이딩뷰 자동매매를 완성할 수 있게 8부로 구성했습니다. Pine Script 기본 전략, 알람 설정, Python Flask 웹훅 서버, 그리고 한국 주식 2개(키움·KIS) + 코인 거래소 3개(바이낸스·업비트·빗썸) 모두의 연동 코드를 포함합니다.

이 글의 8부 구성

  1. Part 1 — 트레이딩뷰 자동매매 전체 구조 (3개 컴포넌트의 역할)
  2. Part 2 — Pine Script 입문 (전략·알람·시그널)
  3. Part 3 — 알람 설정과 웹훅 메시지 포맷
  4. Part 4 — Python Flask 웹훅 서버 구축
  5. Part 5 — 한국 증권사 연동 (키움 OpenAPI+, KIS API)
  6. Part 6 — 코인 거래소 연동 (바이낸스·업비트·빗썸)
  7. Part 7 — 보안과 운영 (시그너처·VPS·헬스체크)
  8. Part 8 — 체크리스트 + 5가지 함정

Part 1 — 트레이딩뷰 자동매매 전체 구조

"차트와 신호는 트레이딩뷰가, 실거래는 본인 서버가." 분담을 이해해야 모든 게 보입니다.

전체 흐름

트레이딩뷰 차트 Pine Script 신호 알람 + 웹훅 웹훅 서버 (VPS) 증권사/거래소 API 실거래 체결

3개 컴포넌트의 역할

컴포넌트역할구현 위치
트레이딩뷰차트·지표·전략 신호 발생·알람 발송트레이딩뷰 웹/앱
웹훅 서버알람 수신·검증·증권사/거래소 API 호출본인 VPS
증권사·거래소실주문 체결키움/KIS/바이낸스/업비트/빗썸

왜 트레이딩뷰 + 외부 서버 분담인가

💡 한국 사용자가 트레이딩뷰 자동매매를 선호하는 이유: (1) Pine Script가 Python보다 진입장벽 낮음 (2) 차트 보면서 전략 검증 (3) 다양한 시장(주식·코인·선물) 동일 인터페이스 (4) 신호 발생 시점·로그가 차트에 시각화 — 이 4가지가 자동매매 본질과 잘 맞습니다.

Part 2 — Pine Script 입문

JavaScript 비슷한 단순 언어. 1~2주면 자동매매에 필요한 수준 충분.

가장 단순한 전략 — EMA 크로스

5일선이 20일선 위로 골든크로스 → 매수 신호. Pine Script v5 기준:

//@version=5
strategy("EMA Crossover", overlay=true)

// 입력 파라미터
fastLen = input.int(5,  "Fast EMA")
slowLen = input.int(20, "Slow EMA")

// 지표 계산
fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)

// 매매 조건
longCondition  = ta.crossover(fast, slow)
shortCondition = ta.crossunder(fast, slow)

// 전략 실행
if longCondition
    strategy.entry("Long", strategy.long)
if shortCondition
    strategy.close("Long")

// 차트 표시
plot(fast, color=color.blue)
plot(slow, color=color.red)

지표(Indicator) vs 전략(Strategy)

유형용도알람 발송
indicator()지표·시그널 표시alertcondition()로 가능
strategy()백테스트·자동 진입/청산주문 발생 시 자동 알람 가능

자동매매 목적이면 strategy()로 작성. 백테스트 결과까지 한 번에 확인 가능.

웹훅용 알람 메시지 동적 생성

알람을 보낼 때 현재가·심볼·매매 방향·수량을 JSON으로 동적 생성하는 것이 표준:

// 알람 메시지 동적 변수 (트레이딩뷰 v5+)
alertMsg = '{"symbol":"' + syminfo.ticker +
           '","action":"BUY",' +
           '"price":' + str.tostring(close) +
           ',"time":"' + str.format_time(time, "yyyy-MM-dd HH:mm:ss") + '"}'

if longCondition
    strategy.entry("Long", strategy.long, alert_message=alertMsg)

Pine Script 깊은 예시는 → 키움 자동매매 + 트레이딩뷰 웹훅 사례

Part 3 — 알람 설정과 웹훅 메시지 포맷

트레이딩뷰 차트 우상단 알람 버튼 → 웹훅 URL 설정.

알람 설정 5단계

  1. 차트에 전략 추가
  2. 알람 버튼 (시계 아이콘) 클릭
  3. Condition: 본인 전략의 진입/청산 이벤트 선택
  4. Webhook URL: https://본인VPS/webhook 입력
  5. Message 본문: Pine Script가 생성한 동적 JSON 사용 (또는 {{strategy.order.alert_message}})

표준 웹훅 메시지 포맷

{
  "secret": "본인_시크릿_키",
  "symbol": "BTCUSDT",
  "action": "BUY",
  "price": 60125.50,
  "qty": 0.001,
  "strategy": "EMA Crossover",
  "time": "2026-06-16 10:23:00"
}

플랜별 알람 제한

플랜월 비용웹훅 가능?활성 알람 수
Basic (무료)$01개
Essential$14.9520개
Plus$29.95100개
Premium$59.95400개

⚠️ 알람 누락 가능성: 트레이딩뷰는 알람 100% 보장하지 않습니다. 서버 부하·인터넷 끊김 시 누락. 결정적 매매(손절·청산)는 트레이딩뷰가 아니라 봇 자체 로직으로 백업해야 합니다.

Part 4 — Python Flask 웹훅 서버 구축

VPS에 100줄짜리 웹훅 서버. 모든 거래소·증권사 연동의 진입점.

최소 골격 — 100줄 Flask 서버

from flask import Flask, request, abort, jsonify
import json, os, hmac, hashlib
from datetime import datetime

app = Flask(__name__)
SECRET = os.getenv("TV_WEBHOOK_SECRET")   # 환경변수에서 로드

# ─── 로깅 ──────────────────────────────────────
import logging
logging.basicConfig(filename="webhook.log", level=logging.INFO,
                    format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger(__name__)

# ─── 메인 핸들러 ───────────────────────────────
@app.route("/webhook", methods=["POST"])
def webhook():
    try:
        data = request.get_json(force=True)
    except Exception as e:
        log.error(f"JSON 파싱 실패: {e}")
        abort(400)

    # 1. 시크릿 검증 — 무단 호출 차단
    if data.get("secret") != SECRET:
        log.warning(f"Invalid secret from {request.remote_addr}")
        abort(403)

    # 2. 중복 신호 차단 (5초 내 동일 페이로드 무시)
    if is_duplicate(data):
        log.info("Duplicate signal ignored")
        return jsonify({"status": "duplicate"}), 200

    # 3. 라우팅 — 심볼별 처리
    symbol = data.get("symbol")
    action = data.get("action")
    log.info(f"신호 수신: {action} {symbol} @ {data.get('price')}")

    try:
        result = route_order(data)
        log.info(f"체결: {result}")
        return jsonify({"status": "ok", "result": result}), 200
    except Exception as e:
        log.error(f"주문 실패: {e}")
        # 텔레그램 알림
        send_telegram(f"주문 실패: {symbol} {action} - {e}")
        return jsonify({"status": "error", "error": str(e)}), 500


# ─── 중복 신호 방어 (메모리 캐시) ─────────────
_recent = {}
def is_duplicate(data, window=5):
    key = json.dumps(data, sort_keys=True)
    now = datetime.now().timestamp()
    if key in _recent and now - _recent[key] < window:
        return True
    _recent[key] = now
    # 오래된 항목 정리
    for k, t in list(_recent.items()):
        if now - t > 60:
            del _recent[k]
    return False


# ─── 라우팅: 심볼별 거래소 결정 ───────────────
def route_order(data):
    symbol = data["symbol"]
    if symbol.endswith("USDT"):
        return execute_binance(data)
    elif symbol.startswith("KRW-"):
        return execute_upbit(data)
    elif symbol.isdigit():   # 한국 주식 (6자리)
        return execute_kis(data)
    else:
        raise ValueError(f"Unknown symbol: {symbol}")


# ─── 텔레그램 알림 ─────────────────────────────
def send_telegram(msg):
    import requests
    token = os.getenv("TG_BOT_TOKEN")
    chat = os.getenv("TG_CHAT_ID")
    if token and chat:
        requests.post(f"https://api.telegram.org/bot{token}/sendMessage",
                      json={"chat_id": chat, "text": msg})


if __name__ == "__main__":
    # 운영은 gunicorn 권장
    app.run(host="0.0.0.0", port=5000)

실행 (개발)

# .env (반드시 .gitignore 추가)
TV_WEBHOOK_SECRET=긴_랜덤_문자열_64자
TG_BOT_TOKEN=텔레그램_봇_토큰
TG_CHAT_ID=본인_챗_ID

pip install flask python-dotenv requests
python webhook_server.py

운영 (gunicorn + systemd)

# /etc/systemd/system/tv-webhook.service
[Unit]
Description=TradingView Webhook
After=network.target

[Service]
User=algolab
WorkingDirectory=/home/algolab/bot
ExecStart=/home/algolab/bot/venv/bin/gunicorn -w 2 -b 0.0.0.0:5000 webhook_server:app
Restart=always

[Install]
WantedBy=multi-user.target

Part 5 — 한국 증권사 연동

키움 OpenAPI+ 또는 KIS API. 한국 주식 자동매매의 두 갈래.

1. KIS API 연동 (권장)

REST API라 어디서나 작동. 토큰 자동 갱신 + 주문:

import requests, os, json, time

KIS_BASE = "https://openapi.koreainvestment.com:9443"
_token_cache = {"token": None, "expires": 0}

def get_kis_token():
    if _token_cache["expires"] > time.time():
        return _token_cache["token"]
    url = f"{KIS_BASE}/oauth2/tokenP"
    res = requests.post(url, json={
        "grant_type": "client_credentials",
        "appkey": os.getenv("KIS_APP_KEY"),
        "appsecret": os.getenv("KIS_APP_SECRET")
    })
    d = res.json()
    _token_cache["token"] = d["access_token"]
    _token_cache["expires"] = time.time() + 23 * 3600
    return d["access_token"]

def execute_kis(data):
    """트레이딩뷰 신호로 KIS 주식 주문"""
    token = get_kis_token()
    cano, acnt = os.getenv("KIS_ACCOUNT_NO").split("-")
    tr_id = "TTTC0802U" if data["action"] == "BUY" else "TTTC0801U"
    headers = {
        "authorization": f"Bearer {token}",
        "appkey": os.getenv("KIS_APP_KEY"),
        "appsecret": os.getenv("KIS_APP_SECRET"),
        "tr_id": tr_id
    }
    body = {
        "CANO": cano,
        "ACNT_PRDT_CD": acnt,
        "PDNO": data["symbol"],
        "ORD_DVSN": "01",   # 시장가
        "ORD_QTY": str(int(data["qty"])),
        "ORD_UNPR": "0"
    }
    url = f"{KIS_BASE}/uapi/domestic-stock/v1/trading/order-cash"
    res = requests.post(url, headers=headers, data=json.dumps(body))
    return res.json()

KIS API 발급·토큰 자동 갱신 자세히: → KIS API 발급부터 첫 주문까지 30분

2. 키움 OpenAPI+ 연동

32bit Python + PyQt5 필요. Windows VPS 또는 별도 윈도우 머신:

# 키움 OpenAPI+는 비동기 OCX 컴포넌트라 별도 프로세스 권장
# webhook_server.py는 Flask가 받고, 키움 프로세스로 메시지 큐 전달

import zmq

def execute_kiwoom(data):
    """ZeroMQ로 32bit 키움 프로세스에 전달"""
    ctx = zmq.Context()
    socket = ctx.socket(zmq.REQ)
    socket.connect("tcp://localhost:5555")
    socket.send_json(data)
    return socket.recv_json()

키움 OpenAPI+ 설치·오류 대응: → 키움 OpenAPI+ 설치·오류 해결 완전 가이드

Part 6 — 코인 거래소 연동

바이낸스·업비트·빗썸 — 같은 웹훅 서버에서 3개 거래소 동시 운영.

바이낸스 (현물)

from binance.client import Client
binance = Client(os.getenv("BINANCE_API_KEY"),
                 os.getenv("BINANCE_API_SECRET"))

def execute_binance(data):
    if data["action"] == "BUY":
        return binance.order_market_buy(
            symbol=data["symbol"],
            quoteOrderQty=data.get("usdt", 10))
    elif data["action"] == "SELL":
        return binance.order_market_sell(
            symbol=data["symbol"],
            quantity=data["qty"])

업비트

import pyupbit
upbit = pyupbit.Upbit(os.getenv("UPBIT_ACCESS_KEY"),
                     os.getenv("UPBIT_SECRET_KEY"))

def execute_upbit(data):
    if data["action"] == "BUY":
        return upbit.buy_market_order(data["symbol"],
                                      data.get("krw", 10000))
    elif data["action"] == "SELL":
        return upbit.sell_market_order(data["symbol"], data["qty"])

빗썸

import python_bithumb as pb
bithumb = pb.Bithumb(os.getenv("BITHUMB_CONNECT_KEY"),
                    os.getenv("BITHUMB_SECRET_KEY"))

def execute_bithumb(data):
    if data["action"] == "BUY":
        return bithumb.buy_market_order(data["symbol"],
                                       data.get("krw", 10000))
    elif data["action"] == "SELL":
        return bithumb.sell_market_order(data["symbol"], data["qty"])

각 거래소 봇 만들기 30분 시리즈:

Part 7 — 보안과 운영

웹훅 URL이 노출되면 누구든 매매 신호를 보낼 수 있습니다. 5가지 안전장치.

1. 시크릿 키 검증

모든 웹훅 페이로드에 secret 필드 포함, 서버에서 검증. 위 Flask 코드의 핵심.

2. HTTPS 필수

HTTP는 평문 → 시크릿 노출. Let's Encrypt 무료 인증서로 HTTPS 적용:

sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com

3. 트레이딩뷰 IP만 허용 (선택)

트레이딩뷰 웹훅 IP 목록만 화이트리스트:

# nginx
location /webhook {
    allow 52.89.214.238;    # 트레이딩뷰 IP
    allow 34.212.75.30;
    allow 54.218.53.128;
    allow 52.32.178.7;
    deny all;
    proxy_pass http://localhost:5000;
}

4. 중복 신호·페이로드 검증

같은 페이로드가 5초 내 재도착하면 무시 (위 Flask 코드 is_duplicate).

5. 헬스체크 + 텔레그램

# 5분마다 자동 헬스체크
*/5 * * * * curl -s https://your-domain.com/health || \
  curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
       -d "chat_id=CHAT&text=웹훅 서버 다운!"

✅ 운영 체크리스트: HTTPS · 시크릿 · 화이트리스트 · 중복 방어 · 헬스체크 — 5가지가 모두 작동하면 안심 운영 가능. 하나라도 빠지면 봇이 잘못 매매 또는 자산 유실 위험.

Part 8 — 16단계 체크리스트 + 5가지 함정

실전 운영 시작 전 마지막 점검.

5가지 함정

함정 1. 알람 누락에 대한 대비 없음

트레이딩뷰 알람이 100% 보장 안 된다는 사실 인지 못 하고 운영. 봇 자체에 백업 로직(시간 손절·강제 청산) 필수.

함정 2. 시그너처 없는 웹훅

웹훅 URL이 노출되면 누구든 신호 발송 가능 → 자본 손실. 시크릿 키 + HTTPS + 화이트리스트.

함정 3. 같은 신호 여러 번 처리

트레이딩뷰가 같은 이벤트로 여러 번 알람 발송하면 중복 매수 발생. 페이로드 해시 캐싱.

함정 4. Pine Script repaint 신호

닫히지 않은 봉의 신호로 매매 → 봉 닫히면 신호 사라짐. barstate.isconfirmed로 봉 확정 후에만 신호 발생.

함정 5. 백테스트 결과를 실거래로 착각

Pine Script strategy.equity는 슬리피지·수수료를 단순화. 실전은 다름. 페이퍼 트레이딩 2주 후 실거래.

16단계 체크리스트

Pine Script (4단계)

웹훅 서버 (4단계)

보안 (4단계)

운영 (4단계)

자주 묻는 질문

Q. 트레이딩뷰 자동매매란 정확히?

Pine Script 신호 → 알람 → 웹훅 → 본인 서버 → 증권사·거래소 API → 실거래. 트레이딩뷰는 차트·신호만, 실주문은 본인 서버.

Q. Pine Script를 몰라도 가능?

기본 지표 알람(이동평균 크로스, RSI 30 등)만 쓰면 가능. 복잡한 전략은 Pine Script 학습(1~2주) 필요.

Q. 웹훅에 유료 플랜 필요?

네, Essential($14.95/월) 이상. 본격 운영은 Plus 권장.

Q. 키움·KIS에 트레이딩뷰로 주문 가능?

가능. 웹훅 서버에서 증권사 API 호출. KIS가 진입 친화적, 키움은 32bit 종속.

Q. 신호 누락 가능?

가능. 봇 자체 백업 로직(시간 손절·강제 청산) + 헬스체크 필수.

Q. 봇 제작 비용?

단일 연동 80만~200만원, 다거래소 통합 150만~400만원, 풀패키지 200만~500만원.

마무리

트레이딩뷰 자동매매는 차트 친화적 입문자에게 가장 빠른 자동매매 진입 경로입니다. Pine Script로 전략을 시각적으로 검증하고, 같은 신호를 한국 주식·코인 어디든 보낼 수 있다는 점이 강력합니다.

이 글의 8부 구성을 따라가면, 트레이딩뷰 알람부터 본인 VPS에서 실거래까지의 전체 파이프라인을 구축할 수 있습니다. 알고랩에서 제작하는 모든 트레이딩뷰 봇은 이 골격을 기본으로 합니다.

트레이딩뷰 자동매매 봇 맞춤 제작

Pine Script 작성, 웹훅 서버, 증권사·거래소 연동, 보안·운영까지 통합된 트레이딩뷰 봇을 알고랩이 1~3주 안에 제작해드립니다.
24시간 빠른 답변 가능합니다.

무료 상담 시작하기