//@version=5 indicator("LJH - 테스트 (Table 제거 버전)", overlay=true) // === high close, low close === highClose = ta.highest(high, 12) lowClose = ta.lowest(low, 4) // === SMA4 Bands === sma4 = (close + close[1] + close[2] + close[3]) / 4 prevSma4 = (close[4] + close[1] + close[2] + close[3]) / 4 prev2Sma4 = (close[4] + close[5] + close[2] + close[3]) / 4 prev3Sma4 = (close[4] + close[5] + close[6] + close[3]) / 4 sma72 = ta.sma(close, 72) sma24 = ta.sma(close, 24) prevSma72 = ta.sma(close, 72) prev2Sma72 = ta.sma(close, 72) prevSma24 = ta.sma(close, 24) prev2Sma24 = ta.sma(close, 24) sma4High = ta.highest(sma4, 12) sma4Middle = ta.sma(sma4, 12) sma4Low = ta.lowest(sma4, 12) prevSma4High = ta.highest(prevSma4, 12) prevSma4Middle = ta.sma(prevSma4, 12) prevSma4Low = ta.lowest(prevSma4, 12) prev2Sma4High = ta.highest(prev2Sma4, 12) prev2Sma4Middle = ta.sma(prev2Sma4, 12) prev2Sma4Low = ta.lowest(prev2Sma4, 12) prev3Sma4High = ta.highest(prev3Sma4, 12) prev3Sma4Middle = ta.sma(prev3Sma4, 12) prev3Sma4Low = ta.lowest(prev3Sma4, 12) // === 상태 추적 === var bool inLong = false var bool inShort = false // === 새 조건을 위한 상태 변수 === var int sma4LongTouchBar = na var int sma4ShortTouchBar = na // sma4가 sma4Low에 닿은 시점 저장 if (sma4 == sma4Low and prevSma4 >= prevSma4Low) sma4LongTouchBar := bar_index // sma4가 sma4High에 닿은 시점 저장 if (sma4 == sma4High and prevSma4 <= prevSma4High) sma4ShortTouchBar := bar_index //전저가 전고가 처리 maLength = input.int(4, title="이동평균 기간") ma = ta.sma(close, maLength) // 이동평균의 방향 비교 maPrev = ma maPrev2 = ma // 꼭지점 조건: 상승에서 하락으로 전환 (고점) isTop = maPrev2 < maPrev and maPrev > ma // 꼭지점 조건: 하락에서 상승으로 전환 (저점) isBottom = maPrev2 > maPrev and maPrev < ma var float[] topCloses = array.new_float() var float[] bottomCloses = array.new_float() if isTop array.push(topCloses, maPrev) if isBottom array.push(bottomCloses, maPrev) // === 구름떼 (Ichimoku Cloud) 추가 === conversionPeriods = 9 basePeriods = 26 laggingSpan2Periods = 52 displacement = 26 conversionLine = (ta.highest(high, conversionPeriods) + ta.lowest(low, conversionPeriods)) / 2 baseLine = (ta.highest(high, basePeriods) + ta.lowest(low, basePeriods)) / 2 leadingSpan1 = (conversionLine + baseLine) / 2 leadingSpan2 = (ta.highest(high, laggingSpan2Periods) + ta.lowest(low, laggingSpan2Periods)) / 2 cloudTopNow = math.max(leadingSpan1[displacement], leadingSpan2[displacement]) cloudBottomNow = math.min(leadingSpan1[displacement], leadingSpan2[displacement]) cloudTopAfter = math.max(leadingSpan1, leadingSpan2) cloudBottomAfter = math.min(leadingSpan1, leadingSpan2) isSma4BelowCloud = low < cloudBottomNow isSma4AboveCloud = high > cloudTopNow var bool hasEnteredShortAfterCloudBreak = false // 매 봉마다 상태 업데이트 if not isSma4BelowCloud hasEnteredShortAfterCloudBreak := false // 상태 변수 var bool allowNewShortEntry = true var bool wasBelowCloud = false // 구름 위/아래 상태 추적 if sma4 < cloudTopNow and not wasBelowCloud allowNewShortEntry := true wasBelowCloud := true if not (sma4 < cloudTopNow) allowNewShortEntry := false wasBelowCloud := false // === 횡보 구간 감지 === lengthWindow = 12 rangePeriod = 240 shortLengthWindow = 4 shortRangePeriod = 40 // Long 기준 비율 계산식 longRatio = (high - ta.lowest(low, lengthWindow)) / ta.lowest(low, lengthWindow) * 100 // Short 기준 비율 계산식 shortRatio = (ta.highest(high, shortLengthWindow) - low) / ta.highest(high, shortLengthWindow) * 100 // 평균 계산 rangeThresholdLongPercent = ta.sma(longRatio, rangePeriod) rangeThresholdShortPercent = ta.sma(shortRatio, shortRangePeriod) rangeLookback = 12 highestHigh = ta.highest(high, rangeLookback) lowestLow = ta.lowest(low, rangeLookback) priceRangeLong = high - lowestLow priceRangeShort = highestHigh - low isSidewaysLong = priceRangeLong > (lowestLow * rangeThresholdLongPercent * 1 / 100) and sma4 > cloudTopNow isSidewaysShort = priceRangeShort > (highestHigh * (rangeThresholdShortPercent) / 100) and sma4 < cloudTopNow isLongRecentTouch = not na(sma4LongTouchBar) and (bar_index - sma4LongTouchBar <= 10) isShortRecentTouch = not na(sma4ShortTouchBar) and (bar_index - sma4ShortTouchBar <= 5) // === 진입/청산 조건 === rawNewLong = highClose == high and open < close and isLongRecentTouch and isSidewaysLong and prevSma4 <= sma4 and sma4High == sma4 and (prev2Sma4Middle - prevSma4Middle) > (prevSma4Middle - sma4Middle) and sma4Middle > sma72 and sma4 > cloudTopNow rawLongSell = (sma4Middle < sma72 and prevSma4Middle > prevSma72) or (sma4 == sma4Low) rawNewShort = (sma72 < cloudTopNow and prevSma4Middle > prevSma24 and sma4Middle > cloudBottomNow and sma4 < sma72 and sma4High > sma72 and sma24 > sma4Middle and sma4 == sma4Low and isSidewaysShort and prevSma4 > sma4 and sma4 < cloudBottomNow) or (sma4 < sma72 and sma4High > sma72 and sma24 < sma4Middle and sma4 == sma4Low and isSidewaysShort and open > close and sma4 < cloudBottomNow and sma4Middle < cloudTopNow) rawShortSell = (sma4High <= sma4 and sma4High < sma72) or sma4 > sma72 or (prevSma4Middle > prevSma4 and sma4Middle < sma4) or (isSidewaysShort and prevSma4Low == prevSma4 and sma4Low < sma4) // === 시그널 변수 === newLongSignal = false longSellSignal = false newShortSignal = false shortSellSignal = false // === 롱 진입/청산 로직 === if not inLong and rawNewLong newLongSignal := true inLong := true if inLong and rawLongSell longSellSignal := true inLong := false // === 쇼트 진입/청산 로직 === if not inShort and rawNewShort newShortSignal := true inShort := true if inShort and rawShortSell shortSellSignal := true inShort := false // === 수익률 계산 및 라벨 표시용 변수 === var float longEntryPrice = na var float shortEntryPrice = na if newLongSignal longEntryPrice := close if longSellSignal and not na(longEntryPrice) lastLongProfit = (close - longEntryPrice) / longEntryPrice * 100 label.new(bar_index, high, text="롱: " + str.tostring(lastLongProfit, "#.##") + "%", color=color.green, textcolor=color.white, size=size.small) longEntryPrice := na if newShortSignal shortEntryPrice := close if shortSellSignal and not na(shortEntryPrice) lastShortProfit = (shortEntryPrice - close) / shortEntryPrice * 100 label.new(bar_index, low, text="숏: " + str.tostring(lastShortProfit, "#.##") + "%", color=color.red, textcolor=color.white, size=size.small, style=label.style_label_up) shortEntryPrice := na // === 시각화 (Plot) === bgcolor(isSidewaysLong ? color.new(#458558, 85) : na, title="Sideways Long Background") bgcolor(isSidewaysShort ? color.new(#964c4c, 85) : na, title="Sideways Short Background") plot(sma4High, color=color.green, linewidth=2, title="SMA(4) High") plot(sma4Low, color=color.red, linewidth=2, title="SMA(4) Low") plot(sma4Middle, color=color.rgb(130, 98, 247), linewidth=2, title="SMA(4) Middle") plot(sma72, color=color.rgb(0, 0, 0), linewidth=2, title="SMA(72)") plot(sma24, color=color.rgb(0, 4, 255), linewidth=2, title="SMA(24)") plotshape(newLongSignal, title="Long Entry", location=location.belowbar, color=color.green, style=shape.labelup, text="Long") plotshape(longSellSignal, title="Long Exit", location=location.abovebar, color=color.red, style=shape.labeldown, text="Long Sell") plotshape(newShortSignal, title="Short Entry", location=location.abovebar, color=color.rgb(153, 180, 91), style=shape.labeldown, text="Short") plotshape(shortSellSignal, title="Short Exit", location=location.belowbar, color=color.rgb(165, 62, 122), style=shape.labelup, text="Short Sell") // 일목균형표 구름대 시각화 p1 = plot(leadingSpan1, offset = displacement, title="Leading Span 1", color=color.new(color.green, 30)) p2 = plot(leadingSpan2, offset = displacement, title="Leading Span 2", color=color.new(color.red, 30)) fill(p1, p2, color = leadingSpan1 > leadingSpan2 ? color.new(color.green, 85) : color.new(color.red, 85), title="Ichimoku Cloud")