複数チャネルのダイナミックサポートとレジスタンスケニーチャネル戦略

KC EMA ATR SMA WMA PP SR
作成日: 2025-01-17 15:17:59 最終変更日: 2025-01-17 15:17:59
コピー: 0 クリック数: 337
1
フォロー
1617
フォロワー

複数チャネルのダイナミックサポートとレジスタンスケニーチャネル戦略

概要

この戦略は、ケルトナー チャネルと動的なサポート レベルおよびレジスタンス レベルに基づいた複合取引システムです。複数の期間を分析し、移動平均とボラティリティ指標を組み合わせることで、完全な取引意思決定フレームワークを形成します。この戦略の核心は、市場のトレンドとボラティリティを考慮しながら、価格が主要なテクニカルレベルを突破する瞬間を特定することで、高確率の取引機会を捉えることです。

戦略原則

この戦略では、分析に多層テクニカル指標システムを使用します。

  1. 21 期間のケニー チャネルを主なトレンド判定ツールとして使用し、チャネル幅は ATR 値によって決定されます。
  2. 左側の21本のローソク足と右側の8本のローソク足を使用して、主要なサポートレベルとレジスタンスレベルを計算します。
  3. トレンドフィルターとして高レベルの期間移動平均を導入
  4. 短期(5期間)と長期(30期間)の移動平均線を組み合わせてエントリータイミングを決定する
  5. ATRを使用してストップロスポジションを動的に調整する

戦略的優位性

  1. 多次元のテクニカル指標は相互検証し、誤ったシグナルを効果的に減らします。
  2. 動的なサポートとレジスタンスのレベルは、市場の変化に適応するためにリアルタイムで更新されます。
  3. 高レベルの期間分析を通じて二次市場の動向をフィルタリングする
  4. 異なる期間に応じてストップロスパラメータを柔軟に調整します
  5. パーセンテージポジション管理を使用してリスクを効果的に管理する

戦略リスク

  1. 不安定な市場では、頻繁に取引シグナルが生成される可能性がある
  2. 複数の指標を検証すると、取引機会を逃す可能性があります
  3. パラメータ最適化には過剰適合のリスクがある
  4. ボラティリティの高い環境ではストップが広すぎる可能性がある
  5. 市場が劇的に変化すると、サポートレベルとレジスタンスレベルが無効になる場合があります。

戦略最適化の方向性

  1. ブレークスルーの有効性を判断するのに役立つボリューム指標の導入
  2. 市場変動分析モジュールを追加し、パラメータを動的に調整する
  3. サポートレベルとレジスタンスレベルの計算方法を最適化して精度を向上
  4. トレンドの強さの判断を追加し、エントリー条件を改良
  5. ポジション管理システムを改善し、より洗練されたリスク管理を実現

要約する

これは、完全な構造と厳密なロジックを備えた定量的な取引戦略です。複数の層のテクニカル指標を協調して使用することで、取引シグナルの信頼性が保証され、効果的なリスク管理が実現します。この戦略は強力なスケーラビリティを備えており、継続的な最適化と改善を通じて、さまざまな市場環境で安定したパフォーマンスを維持することが期待されます。

ストラテジーソースコード
/*backtest
start: 2024-12-17 00:00:00
end: 2024-12-21 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","balance":49999}]
*/

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sathcm
//@version=5 
strategy("KMS", overlay=true, initial_capital=100000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.05, slippage=3)

// Inputs for Keltner Channels
kcLength = input.int(21, title="Keltner Channel Length", minval=1)  // Length for Keltner Channel calculation
kcMultiplier = input.float(2.0, title="Keltner Channel Multiplier", minval=0.1)  // Multiplier for Keltner Channel width

// Calculate Keltner Channels using best practices
kcBasis = ta.ema(close, kcLength)  // Use EMA for a smoother basis line
atrValue = ta.atr(kcLength)  // Use ATR for channel width calculation
kcUpper = kcBasis + kcMultiplier * atrValue  // Upper Keltner Channel
kcLower = kcBasis - kcMultiplier * atrValue  // Lower Keltner Channel

// Inputs for Pivot Point Calculation
leftBars = input.int(21, title="Left Bars", minval=1)  // Number of bars to the left for pivot calculation
rightBars = input.int(8, title="Right Bars", minval=1, tooltip="Number of bars to the right for pivot calculation")  // Number of bars to the right for pivot calculation

// Calculate Smoothed Pivot Highs and Lows using Weighted Moving Average
pivotHigh = ta.pivothigh(high, leftBars, rightBars)  // Apply WMA for smoothing
pivotLow = ta.pivotlow(low, leftBars, rightBars)  // Apply WMA for smoothing

// Convert Pivot Highs and Lows to Boolean Conditions
isPivotHigh = not na(pivotHigh)  // True when a pivot high exists
isPivotLow = not na(pivotLow)  // True when a pivot low exists

// Get Recent Support and Resistance Levels
recentResistance = ta.valuewhen(isPivotHigh, high, 0)  // Most recent resistance level
recentSupport = ta.valuewhen(isPivotLow, low, 0)  // Most recent support level

// Plot Smoothed Support and Resistance Levels
//plot(recentResistance, color=color.red, title="Recent Resistance", linewidth=2, style=plot.style_line)
//plot(recentSupport, color=color.green, title="Recent Support", linewidth=2, style=plot.style_line)

// Store Entry Price into a Variable
var float entryPrice = na  // Declare a variable to store the entry price

// Input for Higher Timeframe
higherTimeframeInput = input.timeframe('W', title="Higher Timeframe for MA Calculation")

if (timeframe.period == "240") or (timeframe.period == "120")
    higherTimeframeInput := "D"
if (timeframe.period == "60") or (timeframe.period == "30") or (timeframe.period == "15")
    higherTimeframeInput := "120"
if (timeframe.period == "10") or (timeframe.period == "5") 
    higherTimeframeInput := "30"
if (timeframe.period == "1")
    higherTimeframeInput := "10"

prd = input.int(defval=10, title='Pivot Period', minval=4, maxval=30, group='Settings 🔨', tooltip='Used while calculating Pivot Points, checks left&right bars')
ppsrc = input.string(defval='High/Low', title='Source', options=['High/Low', 'Close/Open'], group='Settings 🔨', tooltip='Source for Pivot Points')
ChannelW = input.int(defval=5, title='Maximum Channel Width %', minval=1, maxval=8, group='Settings 🔨', tooltip='Calculated using Highest/Lowest levels in 300 bars')
minstrength = input.int(defval=1, title='Minimum Strength', minval=1, group='Settings 🔨', tooltip='Channel must contain at least 2 Pivot Points')
maxnumsr = input.int(defval=4, title='Maximum Number of S/R', minval=1, maxval=10, group='Settings 🔨', tooltip='Maximum number of Support/Resistance Channels to Show') - 1
loopback = input.int(defval=150, title='Loopback Period', minval=100, maxval=400, group='Settings 🔨', tooltip='While calculating S/R levels it checks Pivots in Loopback Period')
res_col = input.color(defval=color.new(color.red, 75), title='Resistance Color', group='Colors 🟡🟢🟣')
sup_col = input.color(defval=color.new(color.lime, 75), title='Support Color', group='Colors 🟡🟢🟣')
inch_col = input.color(defval=color.new(color.gray, 75), title='Color When Price in Channel', group='Colors 🟡🟢🟣')

// Get Pivot High/Low
src1 = ppsrc == 'High/Low' ? high : math.max(close, open)
src2 = ppsrc == 'High/Low' ? low : math.min(close, open)
ph = ta.pivothigh(src1, prd, prd)
pl = ta.pivotlow(src2, prd, prd)

// Calculate maximum S/R channel width
prdhighest = ta.highest(300)
prdlowest = ta.lowest(300)
cwidth = (prdhighest - prdlowest) * ChannelW / 100

// Get/keep Pivot levels
var pivotvals = array.new_float(0)
var pivotlocs = array.new_float(0)
if ph or pl
    array.unshift(pivotvals, ph ? ph : pl)
    array.unshift(pivotlocs, bar_index)
    for x = array.size(pivotvals) - 1 to 0 by 1
        if bar_index - array.get(pivotlocs, x) > loopback  // remove old pivot points
            array.pop(pivotvals)
            array.pop(pivotlocs)
            continue
        break

// Find/create SR channel of a pivot point
get_sr_vals(ind) =>
    float lo = array.get(pivotvals, ind)
    float hi = lo
    int numpp = 0
    for y = 0 to array.size(pivotvals) - 1 by 1
        float cpp = array.get(pivotvals, y)
        float wdth = cpp <= hi ? hi - cpp : cpp - lo
        if wdth <= cwidth  // fits the max channel width?
            if cpp <= hi
                lo := math.min(lo, cpp)
            else
                hi := math.max(hi, cpp)
            numpp += 20  // each pivot point added as 20
    [hi, lo, numpp]

// Keep old SR channels and calculate/sort new channels if we met new pivot point
var suportresistance = array.new_float(20, 0)  // min/max levels
changeit(x, y) =>
    tmp = array.get(suportresistance, y * 2)
    array.set(suportresistance, y * 2, array.get(suportresistance, x * 2))
    array.set(suportresistance, x * 2, tmp)
    tmp := array.get(suportresistance, y * 2 + 1)
    array.set(suportresistance, y * 2 + 1, array.get(suportresistance, x * 2 + 1))
    array.set(suportresistance, x * 2 + 1, tmp)

if ph or pl
    supres = array.new_float(0)  // number of pivot, strength, min/max levels
    stren = array.new_float(10, 0)
    // Get levels and strengths
    for x = 0 to array.size(pivotvals) - 1 by 1
        [hi, lo, strength] = get_sr_vals(x)
        array.push(supres, strength)
        array.push(supres, hi)
        array.push(supres, lo)

    // Add each HL to strength
    for x = 0 to array.size(pivotvals) - 1 by 1
        h = array.get(supres, x * 3 + 1)
        l = array.get(supres, x * 3 + 2)
        s = 0
        for y = 0 to loopback by 1
            if high[y] <= h and high[y] >= l or low[y] <= h and low[y] >= l
                s += 1
        array.set(supres, x * 3, array.get(supres, x * 3) + s)

    // Reset SR levels
    array.fill(suportresistance, 0)
    // Get strongest SRs
    src = 0
    for x = 0 to array.size(pivotvals) - 1 by 1
        stv = -1.  // value
        stl = -1  // location
        for y = 0 to array.size(pivotvals) - 1 by 1
            if array.get(supres, y * 3) > stv and array.get(supres, y * 3) >= minstrength * 20
                stv := array.get(supres, y * 3)
                stl := y
        if stl >= 0
            // Get SR level
            hh = array.get(supres, stl * 3 + 1)
            ll = array.get(supres, stl * 3 + 2)
            array.set(suportresistance, src * 2, hh)
            array.set(suportresistance, src * 2 + 1, ll)
            array.set(stren, src, array.get(supres, stl * 3))

            // Make included pivot points' strength zero
            for y = 0 to array.size(pivotvals) - 1 by 1
                if array.get(supres, y * 3 + 1) <= hh and array.get(supres, y * 3 + 1) >= ll or array.get(supres, y * 3 + 2) <= hh and array.get(supres, y * 3 + 2) >= ll
                    array.set(supres, y * 3, -1)

            src += 1
            if src >= 10
                break

    for x = 0 to 8 by 1
        for y = x + 1 to 9 by 1
            if array.get(stren, y) > array.get(stren, x)
                tmp = array.get(stren, y)
                array.set(stren, y, array.get(stren, x))
                changeit(x, y)

get_level(ind) =>
    float ret = na
    if ind < array.size(suportresistance)
        if array.get(suportresistance, ind) != 0
            ret := array.get(suportresistance, ind)
    ret

get_color(ind) =>
    color ret = na
    if ind < array.size(suportresistance)
        if array.get(suportresistance, ind) != 0
            ret := array.get(suportresistance, ind) > close and array.get(suportresistance, ind + 1) > close ? res_col : array.get(suportresistance, ind) < close and array.get(suportresistance, ind + 1) < close ? sup_col : inch_col
    ret

// var srchannels = array.new_box(10)
// for x = 0 to math.min(9, maxnumsr) by 1
//     box.delete(array.get(srchannels, x))
//     srcol = get_color(x * 2)
//     if not na(srcol)
//         array.set(srchannels, x, box.new(left=bar_index, top=get_level(x * 2), right=bar_index + 1, bottom=get_level(x * 2 + 1), border_color=srcol, border_width=1, extend=extend.both, bgcolor=srcol))

// Improved dynamic support detection
float recentSupport1 = na
float previousSupport = na
float currentsupport = na

if na(previousSupport) or currentsupport != previousSupport
    if array.size(suportresistance) > 1 
        for i = 0 to math.floor(array.size(suportresistance) / 2) - 1  // Iterate through support levels
            currentsupport := array.get(suportresistance, i * 2 + 1)  // Support is stored at odd indices
            if currentsupport < close and (na(recentSupport1) or math.abs(close - currentsupport) < math.abs(close - recentSupport1))
                previousSupport := currentsupport  // Store the newly detected support
                  // Set the most recent support to the new support
                recentSupport1 := na(recentSupport1) ? ta.lowest(low, 10) : currentsupport
// Moving averages for entry and exit
maShort = ta.sma(close, 5)
maLong = ta.sma(close, 30) + ta.atr(14)
// Track entry price
entryPrice1 = strategy.position_avg_price  // Get the price of the currently open position
currentTimeFrame = timeframe.period
exitPrice = entryPrice1 * 0.99

if currentTimeFrame == "1H" or currentTimeFrame == "30" or currentTimeFrame == "15" or currentTimeFrame == "5"
    exitPrice := entryPrice1 * 0.99  // Set the exit price at 99% of the entry price

if currentTimeFrame == "120" or currentTimeFrame == "180" or currentTimeFrame == "240" or currentTimeFrame == "D"
    exitPrice := entryPrice1 * 0.98 // Set the exit price at 95% of the entry price




// Calculate Moving Average based on higher timeframe for length of 20 bars
higherTimeframeMA = request.security(syminfo.tickerid, higherTimeframeInput, ta.sma(close, 20), barmerge.gaps_off, barmerge.lookahead_on)  // Calculate MA with adjusted timeframe

// Entry and Exit Conditions for Long
entryLong = (close > kcUpper) and (close > recentResistance) and (close > higherTimeframeMA)  // Long entry when price breaks above KC upper, recent resistance, and higher timeframe MA
exitLong = (close < recentResistance - 1.5*atrValue)  // Long exit when price falls below recent resistance with cushion of one ATR

// Entry and Exit Conditions for Short
entryShort = (close < kcLower) and (close < recentSupport) and (close < higherTimeframeMA+atrValue) // Add RSI filter to reduce false signals by confirming momentum  // Short entry when price breaks below KC lower, recent support, and higher timeframe MA
exitShort = (close > recentSupport + atrValue)  // Short exit when price rises above recent support with cushion of one ATR(close > recentSupport + atrValue)  // Short exit when price rises above recent support with cushion of one ATR(close > recentSupport + atrValue)  // Short exit when price rises above recent support with cushion of one ATR

// Strategy Execution for Long
if not na(recentSupport1) and (close <= recentSupport1 +(close*0.01) or close >= recentSupport1 - (close*0.0075)) and (maShort > maLong) and entryLong
    strategy.entry("Long Entry", strategy.long)
    //entryPrice := strategy.position_avg_price  // Store the entry price when a position is opened

if ((maShort < maLong + 3*ta.atr(14)) or  close < exitPrice) and exitLong
    strategy.close("Long Entry")

// Strategy Execution for Short
if entryShort
    strategy.entry("Short Entry", strategy.short)
    entryPrice := strategy.position_avg_price  // Store the entry price when a position is opened

if exitShort
    strategy.close("Short Entry")

// Plot Keltner Channels
plot(kcUpper, color=color.orange, title="Keltner Channel Upper", linewidth=1)
plot(kcLower, color=color.orange, title="Keltner Channel Lower", linewidth=1)

// Plot Moving Averages
plot(higherTimeframeMA, color=color.blue, title="Higher Timeframe MA", linewidth=2)

//plot(recentSupport1, color=#04313f, title="Recent Support1")
//plot(recentResistance, color=color.purple, title="Recent Resistance")
//plot(entryPrice1, color=color.lime, title="Entry Price 1")
//plot(exitPrice, color=color.maroon, title="Exit Price")
//plot(maShort, color=color.green, title="MA Short")
//plot(maLong, color=color.blue, title="MA Long Plus ATR")

// Highlight Entry Zones
bgcolor(entryLong ? color.new(color.green, 85) : na, title="Long Entry Zone")
bgcolor(entryShort ? color.new(color.red, 85) : na, title="Short Entry Zone")

// Alerts
alertcondition(entryLong, title="Long Entry", message="Price broke above the Keltner Channel and recent resistance for Long Entry")
alertcondition(exitLong, title="Long Exit", message="Price fell below recent resistance with cushion of one ATR - Long Exit")
alertcondition(entryShort, title="Short Entry", message="Price broke below the Keltner Channel and recent support for Short Entry")
alertcondition(exitShort, title="Short Exit", message="Price rose above recent support with cushion of one ATR - Short Exit")