차트 오버레이 (Chart Overlays)¶
ScaleChart 객체의 메서드를 사용하여 차트 위에 시각적 요소를 표시할 수 있습니다. 오버레이는 전략의 시그널과 지표를 직관적으로 확인하는 데 활용됩니다.
런타임 차트와 스튜디오 차트의 overlay 범례는 기본적으로 최신 snapshot 값을 표시합니다. 사용자가 차트 위에 크로스헤어를 올리면 그 시점의 overlay 값으로 범례가 전환됩니다.
한국 주식 세션 기준
차트와 지표는 09:00~15:20 정규장 연속매매 구간을 기준으로 표시합니다.
상위 타임프레임(일봉 등)의 종가·거래량은 15:30 동시호가 체결 데이터까지 반영됩니다.
따라서 일부 증권사 MTS/HTS의 차트와 마지막 구간이 다를 수 있습니다.
Studio 테스트 오버레이 누적 방식
Studio 테스트 모드에서 line()과 histogram()은 매 평가 시점의 현재값 1개씩 차트에 누적됩니다.
다른 타임프레임 지표를 그릴 때(예: 5분봉 차트에 일봉 SMA 표시), 아직 마감되지 않은 봉의 종가가 매 틱마다 갱신되므로 일봉 SMA가 장중에 어떻게 변화하는지 연속적으로 확인할 수 있습니다.
line()¶
차트에 라인을 그립니다. 이동평균 등 연속적인 시계열을 표시하는 데 사용합니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
name |
str | 라인 이름 (범례에 표시) |
value |
TSeries | 표시할 시계열 |
color |
str | 색상 |
예제:
c = chart("1D")
sma20 = ta.sma(c.close, 20)
ema50 = ta.ema(c.close, 50)
c.line("SMA 20", sma20, color="orange")
c.line("EMA 50", ema50, color="blue")
name을 생략하고 TSeries만 넘기면 이름이 자동 생성됩니다. histogram()도 동일합니다.
histogram()¶
차트에 히스토그램(막대)을 그립니다. MACD 히스토그램이나 거래량 시각화에 적합합니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
name |
str | 히스토그램 이름 |
value |
TSeries | 표시할 시계열 |
color |
str | 색상 |
예제:
c = chart("1D")
macd_line, signal, hist = ta.macd(c.close, 12, 26, 9)
c.histogram("MACD Hist", hist, color="gray")
c.line("MACD", macd_line, color="blue")
c.line("Signal", signal, color="orange")
hline()¶
수평선을 그립니다. 과매수/과매도 수준, 지지/저항선 표시에 사용합니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
name |
str | 라인 이름 |
value |
float | 수평선 Y축 값 |
color |
str | 색상 |
예제:
c = chart("1D")
rsi = ta.rsi(c.close, 14)
c.line("RSI", rsi, color="purple")
c.hline("과매수", 70, color="red")
c.hline("과매도", 30, color="green")
c.hline("중심선", 50, color="gray")
vline()¶
수직선을 그립니다. 특정 시점의 이벤트를 표시하는 데 사용합니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
name |
str | 라인 이름 |
color |
str | 색상 |
예제:
c = chart("1D")
sma5 = ta.sma(c.close, 5)
sma20 = ta.sma(c.close, 20)
if sma5.cross_up(sma20):
c.vline("Golden Cross", color="gold")
buy(tag="골든크로스")
elif sma5.cross_down(sma20):
c.vline("Dead Cross", color="red")
sell(tag="데드크로스")
marker()¶
차트에 상태형 마커(텍스트 라벨)를 표시합니다. 하위 호환을 위해 유지되는 기본 API이며, 새 스크립트는 status_marker() 또는 shape helper를 우선하는 편이 읽기 쉽습니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
text |
str | 마커에 표시할 텍스트 |
color |
str | 색상 |
position |
str | 위치 ("above" 또는 "below") |
shape |
str | 모양 ("circle", "square", "arrow_up", "arrow_down") |
예제:
c = chart("1D")
rsi = ta.rsi(c.close, 14)
if rsi[0] < 30:
c.marker("BUY", color="green", position="below", shape="arrow_up")
buy(tag="RSI 과매도")
elif rsi[0] > 70:
c.marker("SELL", color="red", position="above", shape="arrow_down")
sell(tag="RSI 과매수")
marker helper¶
shape/position enum을 반복하고 싶지 않다면 helper를 사용할 수 있습니다.
status_marker()는 상태형 marker 의도를 이름으로 드러내는 기본 helper입니다.arrow_up_marker()는below + arrow_up을 고정합니다.arrow_down_marker()는above + arrow_down을 고정합니다.circle_marker()와square_marker()는 shape만 고정하고 position은 유지합니다.- 과거 예제의
triangle/arrowshape는 계속 허용되지만, 현재 표준 표기는arrow_up/arrow_down입니다.
c = chart("1D")
if ta.crossover(ta.sma(c.close, 5), ta.sma(c.close, 20)):
c.arrow_up_marker("GOLDEN")
elif ta.crossunder(ta.sma(c.close, 5), ta.sma(c.close, 20)):
c.arrow_down_marker("DEAD")
pane()¶
서브 패인(별도 차트 영역)을 생성합니다. 오실레이터(RSI, ADX, MACD 등)처럼 가격과 다른 스케일의 지표를 분리 표시하는 데 사용합니다.
파라미터:
| 이름 | 타입 | 설명 |
|---|---|---|
name |
str | 패인 이름 (같은 이름이면 같은 패인에 추가) |
PaneProxy 메서드:
line(), histogram(), hline(), vline(), marker(), status_marker(), shape helper — ScaleChart와 동일한 시그니처.
예제:
c = chart("1T")
# 메인 차트에는 가격 오버레이
c.line("EMA 20", ta.ema(c.close, 20), color="orange")
# ADX 서브 패인
p = c.pane("ADX")
adx = ta.adx(c.high, c.low, c.close, 14)
p.line("ADX", adx, color="purple")
p.hline("강한 추세", 25, color="gray")
# RSI 서브 패인
r = c.pane("RSI")
rsi = ta.rsi(c.close, 14)
r.line("RSI", rsi, color="cyan")
r.hline("과매수", 70, color="red")
r.hline("과매도", 30, color="green")
패인 이름이 같은 지표는 같은 서브 패인에 함께 표시됩니다. RSI, ADX, MACD 히스토그램처럼 메인 가격축과 다른 스케일의 지표를 분리해서 볼 수 있습니다.
범례 & 값 표시¶
Studio와 거래 탭 차트는 OHLCV 외에 오버레이 범례와 값을 함께 표시합니다.
- 메인 차트 overlay legend는 OHLCV 바로 아래 줄에 표시됩니다.
- named pane도 패인 안쪽 상단에 같은 형식의 legend/value row를 표시합니다.
line과histogram은 crosshair가 올라간 시점의 exact-time 값만 사용합니다. 같은 시점 포인트가 없으면 직전값으로 보정하지 않고-를 표시합니다.- crosshair가 없을 때는
line과histogram모두 latest snapshot 값을 표시합니다. hline은 시계열 포인트가 아니라 현재 snapshot 가격선으로 취급하므로, crosshair 위치와 무관하게 현재 가격을 표시합니다.- 메인 차트와 named pane은 crosshair 시점을 함께 동기화하므로, 어느 패인을 hover해도 같은 시점 기준으로 값을 읽을 수 있습니다.
주문 마커¶
사용자가 marker()로 직접 그린 마커는 선언한 타임프레임 차트에만 표시됩니다. 다른 타임프레임 차트로 자동 복제되지 않습니다.
시스템 주문 마커(체결, 차단 등)는 strategy.order_on 기준 차트에 표시됩니다. 주문 상태의 최종 확인은 주문 패널과 거래 로그를 사용하세요.
색상 값¶
오버레이에서 사용할 수 있는 색상 이름의 예시입니다.
| 색상 | 값 | 일반 용도 |
|---|---|---|
| 빨강 | "red" |
매도, 과매수, 상한 |
| 녹색 | "green" |
매수, 과매도, 하한 |
| 파랑 | "blue" |
이동평균, 기본 라인 |
| 주황 | "orange" |
시그널 라인, 경고 |
| 보라 | "purple" |
RSI, 보조 지표 |
| 회색 | "gray" |
중심선, 보조선 |
| 흰색 | "white" |
제로 라인 |
| 청록 | "cyan" |
보조 지표 |
| 금색 | "gold" |
특수 시그널 |
| 자홍 | "magenta" |
보조 지표 |
종합 예제¶
version("1.0")
description("풀 차트 오버레이 전략")
c = chart("1D")
# 볼린저밴드
upper, mid, lower = ta.bbands(c.close, 20, 2.0)
c.line("BB Upper", upper, color="red")
c.line("BB Mid", mid, color="gray")
c.line("BB Lower", lower, color="green")
# 이동평균
ema12 = ta.ema(c.close, 12)
c.line("EMA 12", ema12, color="orange")
# MACD
macd_line, signal, hist = ta.macd(c.close, 12, 26, 9)
c.histogram("MACD Hist", hist, color="gray")
c.line("MACD", macd_line, color="blue")
c.line("Signal", signal, color="orange")
# RSI 레벨
rsi = ta.rsi(c.close, 14)
c.hline("OB 70", 70, color="red")
c.hline("OS 30", 30, color="green")
# 시그널 마커
if c.close[0] < lower[0] and rsi[0] < 30:
c.marker("BUY", color="green", position="below", shape="triangle")
buy(tag="BB하단 + RSI과매도")
elif c.close[0] > upper[0] and rsi[0] > 70:
c.marker("SELL", color="red", position="above", shape="triangle")
sell(tag="BB상단 + RSI과매수")
else:
hold()
인자 타입 검증¶
오버레이 메서드는 잘못된 타입의 인자가 전달되면 다음과 같이 동작합니다.
line(), histogram() — 자동 보정:
첫 번째 인자에 문자열 대신 TSeries를 넘기면 value로 자동 재배치하고 이름을 자동 생성합니다.
hline(), vline(), pane(), marker() — 오류:
name 또는 text 인자에 문자열이 아닌 값을 넘기면 실행 오류가 발생합니다. 오류 메시지가 스크립트 로그에 표시되며, 캔들 차트는 정상 렌더링됩니다.
c.hline(tseries, 70) # 오류: "c.hline()의 name 인자는 문자열이어야 합니다"
c.pane(tseries) # 오류: "c.pane()의 name 인자는 문자열이어야 합니다"
c.marker(tseries) # 오류: "c.marker()의 text 인자는 문자열이어야 합니다"
성능 팁: ta.* 내장 함수 활용¶
ta.sma(), ta.ema(), ta.rsi(), ta.rma() 등 내장 지표 함수는 차트 시리즈(c.close, c.high 등)에 대해 증분 캐시를 사용합니다. 캔들이 추가될 때마다 새 값 1개만 계산하므로(O(1)), 백테스트나 장시간 라이브 실행에서도 속도가 일정합니다. 상위 타임프레임 봉이 진행 중일 때(봉 마감 전 틱 갱신)에도 마지막 값만 O(1)로 재계산되어 성능이 유지됩니다.
반면, TSeries 산술로 직접 계산하면 매번 전체 시계열을 다시 계산합니다.
# 권장: ta.* 함수 활용 — 증분 캐시 O(1)
ema20 = ta.ema(c.close, 20)
rsi14 = ta.rsi(c.close, 14)
# 비권장: 직접 산술로 만든 TSeries를 ta.* 입력에 사용
my_spread = (c.high - c.low) / c.close # 매번 새 TSeries 생성
ta.ema(my_spread, 20) # my_spread가 매번 새 객체이므로 캐시 불가
# 조건 판단에는 [0] 인��싱으로 스칼라 비교 (O(1))
if ta.ema(c.close, 12)[0] > ta.ema(c.close, 26)[0]:
buy()
hl2, hlc3, ohlc4는 내장 최적화 대상
c.hl2, c.hlc3, c.ohlc4는 차트 객체에서 증분 관리되므로 (c.high + c.low) / 2 대신 c.hl2를 사용하세요.
커스텀 지표에서 var를 활용한 캐싱 패턴¶
ta.* 함수만으로 표현할 수 없는 커스텀 지표는 var를 사용하여 틱 간 상태를 직접 관리할 수 있습니다. 매 틱마다 새 값 1개만 계산하므로 O(1)입니다.
패턴 1: 캐싱된 지표를 조합
c = chart("1D")
# ta.* 결과끼리 조합 — 각각 캐싱되므로 효율적
ema_fast = ta.ema(c.close, 12)
ema_slow = ta.ema(c.close, 26)
# 마지막 값만 사용하면 O(1)
spread = ema_fast[0] - ema_slow[0]
패턴 2: var로 커스텀 EMA 캐싱
c = chart("1D")
var.init(my_ema=None)
k = 2.0 / (20 + 1)
if var.my_ema is None:
var.my_ema = c.close[0] # 첫 값으로 seed
else:
var.my_ema = c.close[0] * k + var.my_ema * (1 - k)
패턴 3: var로 롤링 윈도우 직접 관리
c = chart("1D")
var.init(window=[], my_sum=0.0)
length = 20
var.window = var.window + [c.close[0]]
var.my_sum = var.my_sum + c.close[0]
if len(var.window) > length:
var.my_sum = var.my_sum - var.window[0]
var.window = var.window[1:]
my_sma = var.my_sum / len(var.window) if var.window else 0
패턴 4: 복합 커스텀 지표 (RSI 변형 등)
c = chart("1D")
var.init(avg_gain=0.0, avg_loss=0.0, prev_close=None, rsi_ready=False)
length = 14
if var.prev_close is not None:
diff = c.close[0] - var.prev_close
gain = diff if diff > 0 else 0.0
loss = -diff if diff < 0 else 0.0
if var.rsi_ready:
var.avg_gain = (var.avg_gain * (length - 1) + gain) / length
var.avg_loss = (var.avg_loss * (length - 1) + loss) / length
else:
# seed 단계: 단순 누적
var.avg_gain = var.avg_gain + gain / length
var.avg_loss = var.avg_loss + loss / length
var.prev_close = c.close[0]
if c.bars >= length + 1:
var.rsi_ready = True
var 패턴의 제한
var에 저장된 값은 JSON 직렬화 가능한 타입(숫자, 문자열, 리스트, dict)만 가능합니다. _TSeries 객체를 var에 저장할 수 없습니다. 또한 var.to_dict()에 64KB 상한이 있으므로 큰 윈도우를 저장하면 잘릴 수 있습니다.