
이 전략은 시장의 정서를 분석하는 그래프 형식을 기반으로 세 개의 핵심 진동기 (심각 진동기, 공포 진동기, 탐욕 진동기) 를 통해 시장의 심리를 측정한다. 이 전략은 동력과 추세 지표를 통합하고, 거래량 확인을 결합하여 완전한 거래 시스템을 구축한다. 이 전략은 시장의 정서 분석을 통해 높은 확률의 거래 기회를 식별하려는 거래자에게 적합하다.
이 전략의 핵심은 세 가지의 감정 진동기를 구축하는 것으로, 다양한 그램 형태를 분석하는 것입니다.
이 세 개의 진동기의 평균값은 ?? 감정 지수 ((CEI) 를 구성한다. CEI가 다른 ?? 값을 돌파할 때 다공간 거래 신호를 유발하고 거래량으로 확인한다.
이것은 기술 분석과 양적 거래를 결합하는 혁신적인 전략이다. 체계화된 감정 분석과 엄격한 위험 관리를 통해 전략은 거래자에게 신뢰할 수 있는 거래 신호를 제공할 수 있다. 약간의 최적화 공간이 있지만 전략의 기본 프레임 워크는 더 많은 개발과 실내 응용에 적합하며 안정적이다.
/*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