양적 지표를 기반으로 한 캔들스틱 감정 모멘텀 트렌드 거래 전략

CEI ADX RSI MACD VOLUME SMA
생성 날짜: 2025-02-21 13:23:51 마지막으로 수정됨: 2025-02-21 13:23:51
복사: 1 클릭수: 361
avatar of ianzeng123 ianzeng123
2
집중하다
319
수행원

양적 지표를 기반으로 한 캔들스틱 감정 모멘텀 트렌드 거래 전략 양적 지표를 기반으로 한 캔들스틱 감정 모멘텀 트렌드 거래 전략

개요

이 전략은 시장의 정서를 분석하는 그래프 형식을 기반으로 세 개의 핵심 진동기 (심각 진동기, 공포 진동기, 탐욕 진동기) 를 통해 시장의 심리를 측정한다. 이 전략은 동력과 추세 지표를 통합하고, 거래량 확인을 결합하여 완전한 거래 시스템을 구축한다. 이 전략은 시장의 정서 분석을 통해 높은 확률의 거래 기회를 식별하려는 거래자에게 적합하다.

전략 원칙

이 전략의 핵심은 세 가지의 감정 진동기를 구축하는 것으로, 다양한 그램 형태를 분석하는 것입니다.

  1. 주저하는 진동기 - 십자별과 토로스 모양을 통해 시장의 불확실성을 측정합니다.
  2. 공포 진동기 - 유성, 상향선, 하락의 포화 형태를 통해 공허한 감정을 추적하는 것
  3. 탐욕 진동기 (Greed Oscillator) 는 맨발의 햇빛, 의 선, 의 삼킨, 삼백병을 통해 다목적인 감정을 감지한다.

이 세 개의 진동기의 평균값은 ?? 감정 지수 ((CEI) 를 구성한다. CEI가 다른 ?? 값을 돌파할 때 다공간 거래 신호를 유발하고 거래량으로 확인한다.

전략적 이점

  1. 체계화된 감정 분석 - 수량적 그래프 형태를 통해 주관적 분석을 객관적 지표로 변환
  2. 리스크 관리 - 최대 지분 기간, 스톱 로즈, 냉각 기간과 같은 메커니즘
  3. 유연한 회복 메커니즘 - 거래가 손실을 입었을 때, 전략은 균형을 깨서 회복하려고 시도합니다.
  4. 다 시장 적용성 - 주식, 외환 및 암호화폐와 같은 여러 시장에 적용할 수 있습니다.
  5. 높은 신호 신뢰성 - 트랜지스 양 확인 및 다중 기술 지표 검증으로 정확도를 향상

전략적 위험

  1. 매개 변수 감수성 - 다양한 절댓값의 설정이 충분히 테스트되고 최적화되어야 합니다.
  2. 시장 환경 의존성 - 불안정한 시장에서 잘못된 신호를 줄 수 있습니다.
  3. 슬라이드 리스크 - 유동성이 낮은 시장에서 실행 위험이 발생할 수 있습니다.
  4. 과도한 거래 위험 - 거래 빈도를 방지하기 위해 냉각 기간을 적절하게 설정해야 합니다.
  5. 시스템 위험 - 주요 시장 사건에서 큰 손실을 입을 수 있습니다.

전략 최적화 방향

  1. 동적 하락 - 시장의 변동에 따라 다양한 하락을 자동으로 조정합니다.
  2. 시장 상태 분류 - 증가 추세와 흔들림 시장의 식별 메커니즘
  3. 머신러닝 최적화 - 머신러닝 알고리즘을 사용하여 최적화 변수 모음
  4. 리스크 관리 강화 - 자금 관리 및 포지션 제어 모듈 추가
  5. 신호 필터링 - 가짜 신호를 필터링하기 위해 더 많은 기술 지표를 통합

요약하다

이것은 기술 분석과 양적 거래를 결합하는 혁신적인 전략이다. 체계화된 감정 분석과 엄격한 위험 관리를 통해 전략은 거래자에게 신뢰할 수 있는 거래 신호를 제공할 수 있다. 약간의 최적화 공간이 있지만 전략의 기본 프레임 워크는 더 많은 개발과 실내 응용에 적합하며 안정적이다.

전략 소스 코드
/*backtest
start: 2024-03-09 18:40:00
end: 2025-02-19 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Binance","currency":"SOL_USDT"}]
*/

//@version=6
strategy("Candle Emotion Index Strategy", shorttitle="CEI Strategy", overlay=true)

// User Inputs
length = input.int(14, title="Lookback Period", minval=1)
dojiThreshold = input.float(0.1, title="Doji Threshold", minval=0.01, maxval=0.5)
spinningTopThreshold = input.float(0.3, title="Spinning Top Threshold", minval=0.1, maxval=0.5)
shootingStarThreshold = input.float(0.5, title="Shooting Star Threshold", minval=0.1, maxval=1.0)
hangingManThreshold = input.float(0.5, title="Hanging Man Threshold", minval=0.1, maxval=1.0)
engulfingThreshold = input.float(0.5, title="Engulfing Threshold", minval=0.1, maxval=1.0)
marubozuThreshold = input.float(0.9, title="Marubozu Threshold", minval=0.5, maxval=1.0)
hammerThreshold = input.float(0.5, title="Hammer Threshold", minval=0.1, maxval=1.0)
threeWhiteSoldiersThreshold = input.float(0.5, title="Three White Soldiers Threshold", minval=0.1, maxval=1.0)

// Volume Multiplier Input
volumeMultiplier = input.float(1.5, title="Volume Multiplier", minval=1.0)

// Cooldown Period Input
cooldownPeriod = input.int(10, title="Cooldown Period (Candles)", minval=1)

// Maximum Holding Period Inputs
maxHoldingPeriod = input.int(20, title="Maximum Holding Period (Candles)", minval=1)
lossHoldingPeriod = input.int(10, title="Loss Exit Holding Period (Candles)", minval=1)
lossThreshold = input.float(0.02, title="Loss Threshold (as % of Entry Price)", minval=0.01, maxval=1.0)

// --- Indecision Oscillator Functions ---
isDoji(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    rangeSize = high - low
    bodySize / rangeSize < threshold

isSpinningTop(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    rangeSize = high - low
    bodySize / rangeSize < threshold and bodySize / rangeSize >= dojiThreshold

indecisionOscillator() =>
    var float dojiScore = 0.0
    var float spinningTopScore = 0.0
    for i = 1 to length
        if isDoji(open[i], close[i], high[i], low[i], dojiThreshold)
            dojiScore := dojiScore + 1.0
        if isSpinningTop(open[i], close[i], high[i], low[i], spinningTopThreshold)
            spinningTopScore := spinningTopScore + 1.0
    dojiScore := dojiScore / length
    spinningTopScore := spinningTopScore / length
    (dojiScore + spinningTopScore) / 2

// --- Fear Oscillator Functions ---
isShootingStar(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    upperWick = high - math.max(open, close)
    lowerWick = math.min(open, close) - low
    upperWick / bodySize > threshold and lowerWick < bodySize

isHangingMan(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    upperWick = high - math.max(open, close)
    lowerWick = math.min(open, close) - low
    lowerWick / bodySize > threshold and upperWick < bodySize

isBearishEngulfing(open, close, openPrev, closePrev, threshold) =>
    bodySize = math.abs(close - open)
    prevBodySize = math.abs(closePrev - openPrev)
    close < openPrev and open > closePrev and bodySize / prevBodySize > threshold

fearOscillator() =>
    var float shootingStarScore = 0.0
    var float hangingManScore = 0.0
    var float engulfingScore = 0.0
    for i = 1 to length
        if isShootingStar(open[i], close[i], high[i], low[i], shootingStarThreshold)
            shootingStarScore := shootingStarScore + 1.0
        if isHangingMan(open[i], close[i], high[i], low[i], hangingManThreshold)
            hangingManScore := hangingManScore + 1.0
        if isBearishEngulfing(open[i], close[i], open[i+1], close[i+1], engulfingThreshold)
            engulfingScore := engulfingScore + 1.0
    shootingStarScore := shootingStarScore / length
    hangingManScore := hangingManScore / length
    engulfingScore := engulfingScore / length
    (shootingStarScore + hangingManScore + engulfingScore) / 3

// --- Greed Oscillator Functions ---
isMarubozu(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    totalRange = high - low
    bodySize / totalRange > threshold

isHammer(open, close, high, low, threshold) =>
    bodySize = math.abs(close - open)
    lowerWick = math.min(open, close) - low
    upperWick = high - math.max(open, close)
    lowerWick / bodySize > threshold and upperWick < bodySize

isBullishEngulfing(open, close, openPrev, closePrev, threshold) =>
    bodySize = math.abs(close - open)
    prevBodySize = math.abs(closePrev - openPrev)
    close > openPrev and open < closePrev and bodySize / prevBodySize > threshold

isThreeWhiteSoldiers(open, close, openPrev, closePrev, openPrev2, closePrev2, threshold) =>
    close > open and closePrev > openPrev and closePrev2 > openPrev2 and close > closePrev and closePrev > closePrev2

greedOscillator() =>
    var float marubozuScore = 0.0
    var float hammerScore = 0.0
    var float engulfingScore = 0.0
    var float soldiersScore = 0.0
    for i = 1 to length
        if isMarubozu(open[i], close[i], high[i], low[i], marubozuThreshold)
            marubozuScore := marubozuScore + 1.0
        if isHammer(open[i], close[i], high[i], low[i], hammerThreshold)
            hammerScore := hammerScore + 1.0
        if isBullishEngulfing(open[i], close[i], open[i+1], close[i+1], engulfingThreshold)
            engulfingScore := engulfingScore + 1.0
        if isThreeWhiteSoldiers(open[i], close[i], open[i+1], close[i+1], open[i+2], close[i+2], threeWhiteSoldiersThreshold)
            soldiersScore := soldiersScore + 1.0
    marubozuScore := marubozuScore / length
    hammerScore := hammerScore / length
    engulfingScore := engulfingScore / length
    soldiersScore := soldiersScore / length
    (marubozuScore + hammerScore + engulfingScore + soldiersScore) / 4

// --- Final Calculations ---
indecision = indecisionOscillator()
fear = fearOscillator()
greed = greedOscillator()

// Calculate the average of the three oscillators
averageOscillator = (indecision + fear + greed) / 3

// --- Combined Strategy Logic ---
var float entryPriceLong = na
var float entryPriceShort = na
var int holdingPeriodLong = 0
var int holdingPeriodShort = 0
var int cooldownCounter = 0

// Buy Signal Logic for Long and Short
longBuySignal = ta.crossover(averageOscillator, 0.1)
shortBuySignal = ta.crossover(averageOscillator, 0.2)

// Calculate average volume over the lookback period
avgVolume = ta.sma(volume, length)

// Take Profit Conditions
longTakeProfitCondition = close > open and volume > avgVolume * volumeMultiplier
shortTakeProfitCondition = close < open and volume > avgVolume * volumeMultiplier

// Buy Logic for Long Positions
if longBuySignal and strategy.position_size == 0 and cooldownCounter <= 0
    entryPriceLong := close
    strategy.entry("Long Entry", strategy.long)
    cooldownCounter := cooldownPeriod
    holdingPeriodLong := 0

// Increment holding period if in a long position
if strategy.position_size > 0
    holdingPeriodLong := holdingPeriodLong + 1

// Sell Logic for Long Positions
if longTakeProfitCondition and strategy.position_size > 0 and close > entryPriceLong
    strategy.close_all()
    cooldownCounter := cooldownPeriod

if holdingPeriodLong >= maxHoldingPeriod and strategy.position_size > 0 and close >= entryPriceLong
    strategy.close_all()
    cooldownCounter := cooldownPeriod

if holdingPeriodLong >= lossHoldingPeriod and strategy.position_size > 0 and close < entryPriceLong * (1 - lossThreshold)
    strategy.close_all()
    cooldownCounter := cooldownPeriod

// Short Logic for Short Positions
if shortBuySignal and strategy.position_size == 0 and cooldownCounter <= 0
    entryPriceShort := close
    strategy.entry("Short Entry", strategy.short)
    cooldownCounter := cooldownPeriod
    holdingPeriodShort := 0

// Increment holding period if in a short position
if strategy.position_size < 0
    holdingPeriodShort := holdingPeriodShort + 1

// Cover Logic for Short Positions
if shortTakeProfitCondition and strategy.position_size < 0 and close < entryPriceShort
    strategy.close_all()
    cooldownCounter := cooldownPeriod

if holdingPeriodShort >= maxHoldingPeriod and strategy.position_size < 0 and close <= entryPriceShort
    strategy.close_all()
    cooldownCounter := cooldownPeriod

if holdingPeriodShort >= lossHoldingPeriod and strategy.position_size < 0 and close > entryPriceShort * (1 + lossThreshold)
    strategy.close_all()
    cooldownCounter := cooldownPeriod

// Decrement the cooldown counter each candle
if cooldownCounter > 0
    cooldownCounter := cooldownCounter - 1