트레이딩 전략/지표 설명

트레이딩 뷰 지표 시리즈 (1) - 시장의 추세를 파악하는 구조적 피벗 지표

TF포지 2025. 4. 18. 22:30

시장의 추세, 진입 전략자산 관리 세가지가 정확하게 맞물려야 성공적인 자동매매 시스템 구축이 가능합니다.

이번 포스팅에서는 시장 구조의 주요 전환 시점추세가 시작되거나 강화되거나 종료되는 순간— 을 인식하기 위한 대표적인 구조적 피벗 지표 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

- 갱신된 고점과 갱신된 저점은 각각 지지와 저항을 나타냅니다.

- 이전 저점은 지지가 되고, 이전 고점은 저항이 됩니다.

- 저점이 깨지면서 이탈한 경우 해당 저점은 저항이 되고, 고점이 깨지면서 돌파한 경우 해당 고점은 지지가 됩니다.


파인 스크립트 코드

https://www.tradingview.com/

 

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 지표를 알아보도록 하겠습니다.