시장의 추세, 진입 전략 및 자산 관리 세가지가 정확하게 맞물려야 성공적인 자동매매 시스템 구축이 가능합니다.
이번 포스팅에서는 시장 구조의 주요 전환 시점 —추세가 시작되거나 강화되거나 종료되는 순간— 을 인식하기 위한 대표적인 구조적 피벗 지표 CHoCH, SMS 및 BMS의 개념과 파인 스크립트 실습을 통해 심도있게 알아보도록 하겠습니다.
구조적 피벗이란?
구조적 피벗(Structural Pivots)은 가격의 고점과 저점을 기반으로 다음과 같은 요소를 파악할 수 있도록 도와주는 핵심 지표입니다:
- 현재 추세의 방향
- 추세 전환의 가능성
- 추세 지속성 확인
이는 특히 스마트 머니 컨셉(SMC, Smart Money Concepts)을 활용하는 트레이더들에게 가격 행동 기반 트레이딩의 기초가 되는 개념입니다.
핵심 개념: CHoCH, SMS, BMS
시장 추세를 파악하기 위해 다음과 같은 세 가지 지표를 사용합니다.
지표명 | 명칭 | 의미 |
CHoCH | Change of Character (캐릭터 전환) | 추세 전환의 초기 신호 |
SMS | Swing Market Structure (스윙 마켓 구조) | 추세 전환을 확인하는 신호 |
BMS | Break of Market Structure (마켓 구조 돌파) | 새로운 추세가 강하게 지속될 때 발생 |
지표별 시나리오
- CHoCH: 가격이 이전의 하락 고점을 돌파 → 추세 전환의 가능성
- SMS: 새로운 고점과 고저 구조 형성 → 전환 확인
- BMS: 추가적인 상승 돌파 → 상승 추세 강화
지표 예시
1. CHoCH
- 저점 추세를 깨고 이탈한 상황
- 상승 추세를 깨고 돌파한 상황
2. BOS
- CHoCH 발생(상승 전환) 이후 다시 고점을 돌파하며 추세가 강화된 상황
- CHoCH 발생(하락 전환) 이후 다시 저점을 돌파하며 추세가 강화된 상황
3. SMS
- 전 저점과 전 고점이 갱신되는 경우 SMS
- 갱신된 고점과 갱신된 저점은 각각 지지와 저항을 나타냅니다.
- 이전 저점은 지지가 되고, 이전 고점은 저항이 됩니다.
- 저점이 깨지면서 이탈한 경우 해당 저점은 저항이 되고, 고점이 깨지면서 돌파한 경우 해당 고점은 지지가 됩니다.
파인 스크립트 코드
TradingView — Track All Markets
Where the world charts, chats and trades markets. We're a supercharged super-charting platform and social network for traders and investors. Free to sign up.
www.tradingview.com
CHoCH와 BOS 확인을 위한 파인 스크립트 코드
//@version=6
indicator("Market Structure MTF Trend [Pt]", shorttitle="MS-MTF_Trend[Pt]", overlay=false, max_labels_count=500, max_lines_count=500)
import PtGambler/TimeframeUtils/1 as tf_Utils
// ─────────────────────────────────────────────────────────────
// INPUTS: TIMEFRAME SETTINGS & COLOR DEFINITIONS
// ─────────────────────────────────────────────────────────────
// ----- Timeframe 1 -----
timeFrame1 = input.timeframe("15", "Timeframe 1", inline="tf1")
pivotStrength1 = input.int(15, "Pivot Strength", inline="tf1")
is_tf1_lowerTF = input.bool(false, "└ Lower than chart TF?")
CHoCHbullColor1 = input.color(color.rgb(46, 104, 48), "└ CHoCH: Bull", inline="col1")
CHoCHbearColor1 = input.color(color.rgb(128, 41, 41), "Bear", inline="col1")
showCHoCH1 = input.bool(true, "Show on chart", inline="col1")
BoSbullColor1 = input.color(color.green, "└ BoS: Bull", inline="col1.")
BoSbearColor1 = input.color(color.red, "Bear", inline="col1.")
showBoS1 = input.bool(true, "Show on chart", inline="col1.")
// ----- Timeframe 2 -----
timeFrame2 = input.timeframe("30", "Timeframe 2", inline="tf2")
pivotStrength2 = input.int(15, "Pivot Strength", inline="tf2")
is_tf2_lowerTF = input.bool(false, "└ Lower than chart TF?", inline="tf2")
CHoCHbullColor2 = input.color(color.rgb(46, 104, 48), "└ CHoCH: Bull", inline="col2")
CHoCHbearColor2 = input.color(color.rgb(128, 41, 41), "Bear", inline="col2")
showCHoCH2 = input.bool(false, "Show on chart", inline="col2")
BoSbullColor2 = input.color(color.green, "└ BoS: Bull", inline="col2.")
BoSbearColor2 = input.color(color.red, "Bear", inline="col2.")
showBoS2 = input.bool(false, "Show on chart", inline="col2.")
// ----- Timeframe 3 -----
timeFrame3 = input.timeframe("60", "Timeframe 3", inline="tf3")
pivotStrength3 = input.int(15, "Pivot Strength", inline="tf3")
is_tf3_lowerTF = input.bool(false, "└ Lower than chart TF?", inline="tf3")
CHoCHbullColor3 = input.color(color.rgb(46, 104, 48), "└ CHoCH: Bull", inline="col3")
CHoCHbearColor3 = input.color(color.rgb(128, 41, 41), "Bear", inline="col3")
showCHoCH3 = input.bool(false, "Show on chart", inline="col3")
BoSbullColor3 = input.color(color.green, "└ BoS: Bull", inline="col3.")
BoSbearColor3 = input.color(color.red, "Bear", inline="col3.")
showBoS3 = input.bool(false, "Show on chart", inline="col3.")
// ----- Timeframe 4 -----
timeFrame4 = input.timeframe("240", "Timeframe 4", inline="tf4")
pivotStrength4 = input.int(15, "Pivot Strength", inline="tf4")
is_tf4_lowerTF = input.bool(false, "└ Lower than chart TF?", inline="tf4")
CHoCHbullColor4 = input.color(color.rgb(46, 104, 48), "└ CHoCH: Bull", inline="col4")
CHoCHbearColor4 = input.color(color.rgb(128, 41, 41), "Bear", inline="col4")
showCHoCH4 = input.bool(false, "Show on chart", inline="col4")
BoSbullColor4 = input.color(color.green, "└ BoS: Bull", inline="col4.")
BoSbearColor4 = input.color(color.red, "Bear", inline="col4.")
showBoS4 = input.bool(false, "Show on chart", inline="col4.")
// ─────────────────────────────────────────────────────────────
// FUNCTION: calcMarketStructureTrend
// Calculates the market structure trend based on pivot points.
// Returns a tuple containing:
// 1. currentTrend : bool - Current trend (true if bullish, false if bearish)
// 2. breakOfStructure : bool - True if a break of structure occurred
// 3. pivotHighTime : int - Time of the last valid pivot high
// 4. pivotLowTime : int - Time of the last valid pivot low
// 5. previousPivotHigh : float - Previous pivot high level (offset by one bar)
// 6. previousPivotLow : float - Previous pivot low level (offset by one bar)
// ─────────────────────────────────────────────────────────────
calcMarketStructureTrend(int pivotLen) =>
float pivotHigh = ta.pivothigh(pivotLen, pivotLen)
float pivotLow = ta.pivotlow(pivotLen, pivotLen)
// Persistent variables for pivot data
var float lastPivotHigh = na
var float lastPivotLow = na
var float lastBrokenHigh = na
var float lastBrokenLow = na
var int pivotHighTime = na
var int pivotLowTime = na
var bool currentTrend = false
bool breakOfStructure = false
// Update pivot high data
if not na(pivotHigh)
lastPivotHigh := currentTrend ? math.max(high[pivotLen], lastPivotHigh) : high[pivotLen]
pivotHighTime := lastPivotHigh != lastPivotHigh[1] ? time[pivotLen] : pivotHighTime
// Update pivot low data
if not na(pivotLow)
lastPivotLow := not currentTrend ? math.min(low[pivotLen], lastPivotLow) : low[pivotLen]
pivotLowTime := lastPivotLow != lastPivotLow[1] ? time[pivotLen] : pivotLowTime
// Determine trend change and break of structure
if ta.crossover(close, lastPivotHigh)
breakOfStructure := currentTrend and lastPivotHigh != lastBrokenHigh ? true : false
currentTrend := true
lastBrokenHigh := lastPivotHigh
lastBrokenLow := na
if ta.crossunder(close, lastPivotLow)
breakOfStructure := not currentTrend and lastPivotLow != lastBrokenLow ? true : false
currentTrend := false
lastBrokenLow := lastPivotLow
lastBrokenHigh := na
[currentTrend, breakOfStructure, pivotHighTime, pivotLowTime, lastPivotHigh[1], lastPivotLow[1]]
// ─────────────────────────────────────────────────────────────
// FUNCTION: drawLabelAtBar
// Creates/updates a label at a given bar index (x) and price level (y) with the provided text and styling.
// ─────────────────────────────────────────────────────────────
drawLabelAtBar(x, y, series string labelText, bgColor, style, txtColor, txtSize, align, tooltipText, bool forceOverlay=false) =>
var lbl = label.new(x, y, "", xloc=xloc.bar_index, yloc=yloc.price, color=bgColor, style=style, textcolor=txtColor, size=txtSize, textalign=align, tooltip=tooltipText, force_overlay=forceOverlay)
label.set_xy(lbl, x, y)
label.set_text(lbl, labelText)
label.set_tooltip(lbl, tooltipText)
label.set_textcolor(lbl, txtColor)
lbl
// ─────────────────────────────────────────────────────────────
// FUNCTION: drawTrendChange
// Draws a dotted line and label to mark a trend change for a given timeframe.
// For bullish trends, uses pivot high data with a downward label;
// for bearish trends, uses pivot low data with an upward label.
// The 'toggle' parameter enables/disables drawing.
// The 'spacing' string can add extra line breaks for visual adjustment.
// ─────────────────────────────────────────────────────────────
drawTrendChange(bool toggle, bool hasChange, bool currentTrend, int pivotHighTime, float pivotHighLevel, int pivotLowTime, float pivotLowLevel, string labelText, string spacing, color trendColor) =>
if toggle and hasChange
if currentTrend
// Bullish trend: draw line from pivot high and label below the line
line.new(pivotHighTime, pivotHighLevel, time, pivotHighLevel, xloc=xloc.bar_time, color=trendColor, style=line.style_dotted, force_overlay=true)
label.new(pivotHighTime + (time - pivotHighTime) / 2, pivotHighLevel, labelText + spacing, xloc=xloc.bar_time,
color=color.new(chart.bg_color, 100), style=label.style_label_down, textcolor=trendColor, size=size.small,
textalign=text.align_center, force_overlay=true)
else
// Bearish trend: draw line from pivot low and label above the line
line.new(pivotLowTime, pivotLowLevel, time, pivotLowLevel, xloc=xloc.bar_time, color=trendColor, style=line.style_dotted, force_overlay=true)
label.new(pivotLowTime + (time - pivotLowTime) / 2, pivotLowLevel, spacing + labelText, xloc=xloc.bar_time,
color=color.new(chart.bg_color, 100), style=label.style_label_up, textcolor=trendColor, size=size.small,
textalign=text.align_center, force_overlay=true)
SMS 확인을 위한 파인 스크립트 코드
//@version=5
indicator(title='Swing High and Low with Market Structure Breaks', overlay=true)
// Function to detect swing highs and lows
f_swingHighLow(_src, _len) =>
_sw = high >= ta.highest(high, _len)
_lw = low <= ta.lowest(low, _len)
[_sw, _lw]
// User inputs
var len = input.int(20, minval=1, title='Loopback')
var mult = input.float(1, minval=0.1, title='Multipler')
bullish_MSB_Color = input.color(color.green, title='Bullish MSB Color', inline='Bullish MSB Style')
bearish_MSB_Color = input.color(color.red, title='Bearish MSB Color', inline='Bearish MSB Style')
bullish_MSB_Width = input.int(1, minval=1, maxval = 5, title='Line Width', inline='Bullish MSB Style')
bearish_MSB_Width = input.int(1, minval=1, maxval = 5, title='Line Width', inline='Bearish MSB Style')
// Calculation of swing highs and lows
[sh, sl] = f_swingHighLow(close, len)
// Identify market structure break
break_up = sh and sh[1] == false and close > high[1] * mult
break_down = sl and sl[1] == false and close < low[1] * mult
// Draw lines on market structure break
var line bullish_MSB = na
var line bearish_MSB = na
var float highest_MSB = na
var float lowest_MSB = na
if break_up
// Find the bar_index where the swing high is and draw a line until the candle CLOSES ABOVE the market structure
for i = 1 to 100 by 1
if sh[i]
bullish_MSB := line.new(bar_index[i], high[i], bar_index, high[i], color=bullish_MSB_Color, width=bullish_MSB_Width)
highest_MSB := high[i]
break
if break_down
// Find the bar_index where the swing low is and draw a line until the candle CLOSES BELOW the market structure
for i = 1 to 100 by 1
if sl[i]
bearish_MSB := line.new(bar_index[i], low[i], bar_index, low[i], color=bearish_MSB_Color, width=bearish_MSB_Width)
lowest_MSB := low[i]
break
구조적 피벗 지표 어떻게 사용하면 좋을까?
- 큰 가격 변동 이후 추세 전환을 확인할 때
- 조정 구간에서 매수/매도 진입 시점을 결정할 때
- 변동성 장세에서 휩소를 피하고 싶을 때
- SMC 기반으로 전략을 강화하고 싶을 때
지금까지 시장의 추세, 진입 전략 및 자산 관리 세가지 중 시장의 추세를 파악하는 방법으로 널리 활용되는 CHoCH, BOS 및 SMS를 알아보았습니다. 이 글을 통해 시장의 추세를 파악하는데 도움이 되셨으면 좋겠습니다.
다음 글에서는 매우 유명한 지표이자 트레이딩에 널리 사용되는 EMA 지표를 알아보도록 하겠습니다.
'트레이딩 전략 > 지표 설명' 카테고리의 다른 글
트레이딩 뷰 지표 시리즈 (4) - 스토캐스틱 RSI : 과매수·과매도 구간을 정확히 포착하는 보조지표 (0) | 2025.04.24 |
---|---|
트레이딩 뷰 지표 시리즈 (3) - 시장의 추세를 파악하는 슈퍼 트렌드 지표 알아보기 (0) | 2025.04.23 |
트레이딩 뷰 지표 시리즈 (2) - 지수 이동 평균 (EMA, Exponential Moving Average) (0) | 2025.04.19 |