Files

211 lines
8.0 KiB
Plaintext
Raw Permalink Normal View History

2026-04-25 17:32:49 +09:00
//@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")