2Tier Ichimoku Pyramiding [Takazudo]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// https://www.tradingview.com/script/UhhFWEus-MTF-Ichimoku-Takazudo/
//@version=4
//@author=Takazudo

strategy("2Tier Ichimoku Pyramiding [Takazudo]",
  pyramiding=2,
  overlay=true,
  default_qty_type=strategy.fixed,
  initial_capital=0,
  currency=currency.USD)

//============================================================================
// consts, inputs
//============================================================================

// colors

var COLOR_KIJUN = color.new(#fcb103, 20)
var COLOR_TENKAN = color.new(#f57f17, 20)
var COLOR_CHIKOU = color.new(#ffffff, 50)
var COLOR_KUMO_BASEHTF_BULL = color.new(#315F2E, 60)
var COLOR_KUMO_BASEHTF_BEAR = color.new(#802A1C, 60)
var COLOR_KUMO_HIGHESTTF_BULL = color.new(#1D4F8A, 60)
var COLOR_KUMO_HIGHESTTF_BEAR = color.new(#6B2B55, 60)
var COLOR_SENKOUA_BASEHTF = color.new(#315F2E, 30)
var COLOR_SENKOUB_BASEHTF = color.new(#802A1C, 30)
var COLOR_SENKOUA_HIGHESTTF = color.new(#1D4F8A, 30)
var COLOR_SENKOUB_HIGHESTTF = color.new(#6B2B55, 30)
var COLOR_CROSS_POINT_BULL = color.new(#315F2E, 20)
var COLOR_CROSS_POINT_BEAR = color.new(#802A1C, 20)
var COLOR_CHIKOU_CROSS_POINT_BULL = color.new(#315F2E, 20)
var COLOR_CHIKOU_CROSS_POINT_BEAR = color.new(#802A1C, 20)
var COLOR_CHIKOU_KUMO_BREAKOUT_BULL = color.new(#315F2E, 0)
var COLOR_CHIKOU_KUMO_BREAKOUT_BEAR = color.new(#802A1C, 0)
var COLOR_CHIKOU_HINT_BULL = color.new(#315F2E, 0)
var COLOR_CHIKOU_HINT_BEAR = color.new(#802A1C, 0)
var COLOR_SENKOU_CROSS_HINT_BULL = color.new(#315F2E, 0)
var COLOR_SENKOU_CROSS_HINT_BEAR = color.new(#802A1C, 0)
var COLOR_TENKAN_CROSS_BG = color.new(#f2931d, 90)
var COLOR_CHIKOU_BREAKOUT_BG = color.new(#656565, 90)
var COLOR_SENKOU_CROSS_BG = color.new(#7a73d8, 90)
var COLOR_GAP_KUMO = color.new(#FCC02C, 97)
var COLOR_RANGE_EDGE = color.new(#43A6F5, 30)
var COLOR_RESISTANCE_PRICE_LINE = color.new(#e0f64d, 20)
var COLOR_TRANSPARENT = color.new(#000000, 100)

// HTF

var _1 = input(true, "═════════ HTF ══════════")

var config_baseHtf = input("D", "Base Higher Timeframe", type = input.resolution)
var config_useHighestTf = input(true, "use Highest Timeframe (Kumo only)")
var config_highestTf= input("W", "Highest Timeframe", type = input.resolution)

var _2 = input(true, "═════════ Ichimoku ══════════")

var config_tenkanSen = input(9, minval=1, title="Tenkan-Sen Bars")
var config_kijunSen = input(26, minval=1, title="Kijun-Sen Bars")
var config_senkouSpanB = input(52, minval=1, title="Senkou-Span B Bars")
var config_chikouSpan = input(26, minval=1, title="Chikou-Span Offset")
var config_senkouSpan = input(26, minval=1, title="Senkou-Span Offset")

var _3 = input(true, "═════════ Smooth Line ══════════")

var config_useSmooth = input(true, "enable smooth line")
var config_smoothFactor = input(2, minval=2, title="smooth factor")

var _4 = input(true, "═════════ MTF stuff ══════════")

var config_autoSwitchToHtf = input(true, "avoid to refer lower resolution indicator")
var config_avoidTooHighResolution = input(true, "avoid to refer too high resolution indicator")
var config_useGapKumo = input(true, "use Gap Kumo")

var _5 = input(true, "═════════ etc ══════════")

var config_bullDetectBars = input(defval = 3, title = "bull detection min bar count",  minval = 1)
var config_rangeDetectBars = input(defval = 200, title = "range detection bar count",  minval = 1)
var config_recentBars = input(defval = 50, title = "recent bar count for SL",  minval = 1)
var config_atrLength = input(4, title = "Trailing stop ATR Length")
var config_atrSlMult = input(1, title = "Trailing stop ATR Multiple factor", type=input.float)
var config_atrEntryPaddingMult = input(0.5, title = "Entry padding ATR Multiple factor", type=input.float)
var config_atrEntryPaddingLength = input(10, title = "Entry padding ATR Length")
var config_extraEntryStopDetectBars = input(defval = 10, title = "Pyramiding entry recent detect bar count",  minval = 1)

var _6 = input(true, "═════════ Backtesting range ══════════")

// From Date Inputs
fromYear  = input(defval = 2019, title = "From Year",  minval = 1970)
fromMonth = input(defval = 1,    title = "From Month", minval = 1, maxval = 12)
fromDay   = input(defval = 1,    title = "From Day",   minval = 1, maxval = 31)

// To Date Inputs
toYear  = input(defval = 2021, title = "To Year",  minval = 1970)
toMonth = input(defval = 3,    title = "To Month", minval = 1, maxval = 12)
toDay   = input(defval = 10,    title = "To Day",   minval = 1, maxval = 31)

//============================================================================
// MTF Selection Framework
// http://www.pinecoders.com/faq_and_code/#other-intervals-mtf
//============================================================================
// ————— Converts current "timeframe.multiplier" plus the TF into minutes of type float.
f_resInMinutes() =>
    _resInMinutes = timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60. :
      timeframe.isminutes ? 1.       :
      timeframe.isdaily   ? 1440.    :
      timeframe.isweekly  ? 10080.   :
      timeframe.ismonthly ? 43800.   : na)

// ————— Returns resolution of _resolution period in minutes.
f_tfResInMinutes(_res) =>
    // _res: resolution of any TF (in "timeframe.period" string format).
    security(syminfo.tickerid, _res, f_resInMinutes())

// ————— Given current resolution, returns next step of HTF.
f_resNextStep(_res) =>
    // _res: current TF in fractional minutes.
    _res    <= 1        ? "15"  :
      _res  <= 5        ? "60"  :
      _res  <= 30       ? "240" :
      _res  <= 60       ? "1D"  :
      _res  <= 360      ? "3D"  :
      _res  <= 1440     ? "1W"  :
      _res  <= 10080    ? "1M"  : "12M"

// ————— Returns a multiple of current resolution as a string in "timeframe.period" format usable with "security()".
f_multipleOfRes(_res, _mult) =>
    // _res:  current resolution in minutes, in the fractional format supplied by f_resInMinutes() companion function.
    // _mult: Multiple of current TF to be calculated.
    // Convert current float TF in minutes to target string TF in "timeframe.period" format.
    _targetResInMin = _res * max(_mult, 1)
    // Find best string to express the resolution.
    _targetResInMin   <= 0.083 ? "5S"  :
      _targetResInMin <= 0.251 ? "15S" :
      _targetResInMin <= 0.501 ? "30S" :
      _targetResInMin <= 1440  ? tostring(round(_targetResInMin)) :
      _targetResInMin <= 43800 ? tostring(round(min(_targetResInMin / 1440, 365))) + "D" :
      tostring(round(min(_targetResInMin / 43800, 12))) + "M"

// ————— Converts a resolution expressed in float minutes into a string usable by "security()".
// See our FAQ & Code for an example of using this function: http://www.pinecoders.com/faq_and_code/#how-can-i-convert-a-resolution-in-float-minutes-into-a-string-usable-with-security
f_resFromMinutes(_minutes) =>
    _minutes     <= 0.0167       ? "1S"  :
      _minutes   <= 0.0834       ? "5S"  :
      _minutes   <= 0.2500       ? "15S" :
      _minutes   <= 0.5000       ? "30S" :
      _minutes   <= 1440         ? tostring(round(_minutes)) :
      _minutes   <= 43800        ? tostring(round(min(_minutes / 1440, 365))) + "D" :
      tostring(round(min(_minutes / 43800, 12))) + "M"

// Returns the theoretical numbers of current chart bars in the given target HTF resolution (note that this number maybe very different from actual number on certain symbols).
f_theoreticalDilationOf(_res) =>
    // _res: resolution of any TF (in "timeframe.period" string format).
    f_tfResInMinutes(_res) / f_resInMinutes()

// Returns the average number of current chart bars in the given target HTF resolution (this reflects the dataset's history).
f_avgDilationOf(_res) =>
    // _res: resolution of any TF (in "timeframe.period" string format).
    b = barssince(change(time(_res)))
    cumTotal = cum(b == 0 ? b[1] + 1 : 0)
    cumCount = cum(b == 0 ? 1 : 0)
    cumTotal / cumCount

// ————— Print a label at end of chart.
f_htfLabel(_txt, _y, _color, _offsetLabels) =>
    _t = int(time + (f_resInMinutes() * _offsetLabels * 60000))
    // Create the label on the dataset's first bar.
    var _lbl = label.new(_t, _y, _txt, xloc.bar_time, yloc.price, #00000000, label.style_none, color.gray, size.large)
    if barstate.islast
        // Rather than delete and recreate the label on every realtime bar update,
        // simply update the label's information; it's more efficient.
        label.set_xy(_lbl, _t, _y)
        label.set_text(_lbl, _txt)
        label.set_textcolor(_lbl, _color)
// }

//============================================================================
// utils, setup
//============================================================================

// initialize

var tenkanLine_baseHtf = 0.0
var kijunLine_baseHtf = 0.0
var senkouALine_baseHtf = 0.0
var senkouBLine_baseHtf = 0.0

var tenkanLine_highestTf = 0.0
var kijunLine_highestTf = 0.0
var senkouALine_highestTf = 0.0
var senkouBLine_highestTf = 0.0

var gapKumo_high = 0.0
var gapKumo_low = 0.0

f_isUsingHtf() =>
    _current = f_resInMinutes()
    _target = f_tfResInMinutes(config_baseHtf)
    _current < _target
f_isOverHtf() =>
    _current = f_resInMinutes()
    _target = f_tfResInMinutes(config_baseHtf)
    _current > _target
f_isOverHighestTf() =>
    _current = f_resInMinutes()
    _target = f_tfResInMinutes(config_highestTf)
    _current >= _target
f_shouldAvoidHighestTf(barsMultipiller) =>
    // it the resolution was too high, requested candle sticks
    // hit the limit of trading view.
    // this function detects the limit
    config_avoidTooHighResolution and barsMultipiller > 150

var useSmooth = f_isUsingHtf() and config_useSmooth

f_smooth(_series) =>
    _f = config_smoothFactor
    // Tricky part. the lines that refers higher Timeframe got zig-zag
    // because the granularity of the value per bar is the same
    // inside the higher timeframe's one bar length.
    // This smooth factor blends the value with previous values using ema.
    _smoothed = ema(ema(ema(_series, _f), _f), _f)
    useSmooth ? _smoothed : _series

lostPositionsInPrevBar = strategy.position_size == 0 and (strategy.position_size[0] != strategy.position_size[1])

// remember entry price
var previousEntryPrice = hl2

//============================================================================
// entry quantity caliculation
// wider SL = less quantities
//============================================================================

f_calcQtyForInitialEntry(_percentage) =>
    if _percentage < 1
        10000
    else if _percentage < 2
        8000
    else if _percentage < 3
        6000
    else
        4000

f_calcQtyForExtraEntry(_percentage) =>
    if _percentage < 1
        5000
    else if _percentage < 2
        4000
    else if _percentage < 3
        3000
    else
        2000

//============================================================================
// candle actions
//============================================================================

f_isUpDoji() =>
    fullPriceHeight = high-low
    bodyPriceHeight = abs(close-open)
    (bodyPriceHeight < fullPriceHeight * 1/7) and (max(open, close) > (low + (high-low) * 2/3))

f_isDownDoji() =>
    fullPriceHeight = high-low
    bodyPriceHeight = abs(close-open)
    (bodyPriceHeight < fullPriceHeight * 1/7) and (min(open, close) < (high - (high-low) * 2/3))

f_isLastCandleUp() =>
    result = false
    (close > open) or f_isUpDoji()

f_isLastCandleDown() =>
    result = false
    (close < open) or f_isDownDoji()

//============================================================================
// Ichimoku calcs
//============================================================================

f_donchian(len) =>
    avg(lowest(len), highest(len))
f_tenkan() =>
    f_donchian(config_tenkanSen)
f_kijun() =>
    f_donchian(config_kijunSen)
f_senkouA(_tenkan, _kijun) =>
    avg(_tenkan, _kijun)
f_senkouB() =>
    f_donchian(config_senkouSpanB)

barsMultipiller_baseHtf = f_isOverHtf() ? 1 : f_theoreticalDilationOf(config_baseHtf)
barsMultipiller_highestTf = f_isOverHighestTf() ? 1 : f_theoreticalDilationOf(config_highestTf)
shouldAvoidHighestTf = f_shouldAvoidHighestTf(barsMultipiller_highestTf)

f_calcUseHighestTf() =>
    _result = true
    if shouldAvoidHighestTf
        _result := false
    else
        if config_useHighestTf
            if f_isOverHighestTf()
                _result := false
            else
                _result := true
    _result

var useHighestTf = f_calcUseHighestTf()

senkouOffset = config_senkouSpan - 1
chikouOffset = -config_chikouSpan + 1

senkouOffset_baseHtf = round((config_senkouSpan - 1) * barsMultipiller_baseHtf)
chikouOffset_baseHtf = round((config_chikouSpan - 1) * barsMultipiller_baseHtf)
senkouOffsetForPlot_baseHtf = round(senkouOffset * barsMultipiller_baseHtf)
chikouOffsetForPlot_baseHtf = round(-chikouOffset_baseHtf)

senkouOffset_highestTf = useHighestTf ? round((config_senkouSpan - 1) * barsMultipiller_highestTf) : 0
chikouOffset_highestTf = useHighestTf ? round((config_chikouSpan - 1) * barsMultipiller_highestTf) : 0
senkouOffsetForPlot_highestTf = useHighestTf ? round(senkouOffset * barsMultipiller_highestTf) : 0
chikouOffsetForPlot_highestTf = useHighestTf ? round(-chikouOffset_highestTf) : 0

senkouSpanIndexDiff = senkouOffsetForPlot_highestTf - senkouOffsetForPlot_baseHtf

// not sure why but it says negative index was detected
if senkouSpanIndexDiff < 0
    senkouSpanIndexDiff := 0
if not useHighestTf or not config_useGapKumo
    senkouSpanIndexDiff := 0

f_calcIchimoku_baseHtf() =>
    _useCurrentRes = config_autoSwitchToHtf and f_isOverHtf()
    _tenkan = f_tenkan()
    _kijun = f_kijun()
    _senkouA = f_senkouA(_tenkan[0], _kijun[0])
    _senkouB = f_senkouB()
    _htfTenkan = _useCurrentRes ? _tenkan : security(syminfo.tickerid, config_baseHtf, _tenkan)
    _htfKijun = _useCurrentRes ? _kijun : security(syminfo.tickerid, config_baseHtf, _kijun)
    _htfSenkouA = _useCurrentRes ? _senkouA : security(syminfo.tickerid, config_baseHtf, _senkouA)
    _htfSenkouB = _useCurrentRes ? _senkouB : security(syminfo.tickerid, config_baseHtf, _senkouB)
    [_htfTenkan, _htfKijun, _htfSenkouA, _htfSenkouB]

f_calcIchimoku_highestTf() =>
    _tenkan = f_tenkan()
    _kijun = f_kijun()
    _senkouA = f_senkouA(_tenkan[0], _kijun[0])
    _senkouB = f_senkouB()
    _highestTfTenkan = useHighestTf ? security(syminfo.tickerid, config_highestTf, _tenkan) : _tenkan
    _highestTfKijun = useHighestTf ? security(syminfo.tickerid, config_highestTf, _kijun) : _kijun
    _highestTfSenkouA = useHighestTf ? security(syminfo.tickerid, config_highestTf, _senkouA) : _senkouA
    _highestTfSenkouB = useHighestTf ? security(syminfo.tickerid, config_highestTf, _senkouB) : _senkouB
    [_highestTfTenkan, _highestTfKijun, _highestTfSenkouA, _highestTfSenkouB]

//--------------------------------------
// HTF Ichimoku values

[_htfTenkan, _htfKijun, _htfSenkouA, _htfSenkouB] = f_calcIchimoku_baseHtf()

tenkanLine_baseHtf := _htfTenkan
kijunLine_baseHtf := _htfKijun
senkouALine_baseHtf := _htfSenkouA
senkouBLine_baseHtf := _htfSenkouB

//--------------------------------------
// Highest TF Ichimoku values

[_highestTfTenkan, _highestTfKijun, _highestTfSenkouA, _highestTfSenkouB] = f_calcIchimoku_highestTf()

tenkanLine_highestTf := _highestTfTenkan
kijunLine_highestTf := _highestTfKijun
senkouALine_highestTf := _highestTfSenkouA
senkouBLine_highestTf := _highestTfSenkouB

f_calcIchimokuSignals_baseHtf() =>
    var _chikouAboveCandle = false
    var _chikouBelowCandle = false
    var _chikouAboveKumo = false
    var _chikouBelowKumo = false
    _kumoColor_bull = senkouALine_baseHtf > senkouBLine_baseHtf
    _senkouCross_bull = crossover(senkouALine_baseHtf, senkouBLine_baseHtf)
    _senkouCross_bear = crossover(senkouBLine_baseHtf, senkouALine_baseHtf)
    _senkouSpanHigh = max(senkouALine_baseHtf[senkouOffset_baseHtf], senkouBLine_baseHtf[senkouOffset_baseHtf])
    _senkouSpanLow = min(senkouALine_baseHtf[senkouOffset_baseHtf], senkouBLine_baseHtf[senkouOffset_baseHtf])
    _chikouKumoHigh = max(senkouALine_baseHtf[senkouOffset_baseHtf+chikouOffset_baseHtf], senkouBLine_baseHtf[senkouOffset_baseHtf+chikouOffset_baseHtf])
    _chikouKumoLow = min(senkouALine_baseHtf[senkouOffset_baseHtf+chikouOffset_baseHtf], senkouBLine_baseHtf[senkouOffset_baseHtf+chikouOffset_baseHtf])
    _tenkanCross_bull = crossover(tenkanLine_baseHtf, kijunLine_baseHtf)
    _tenkanCross_bear = crossover(kijunLine_baseHtf, tenkanLine_baseHtf)
    _tenkanAboveKijun = tenkanLine_baseHtf > kijunLine_baseHtf
    _tenkanBelowKijun = tenkanLine_baseHtf < kijunLine_baseHtf
    _chikouAboveCandle := close > close[chikouOffset_baseHtf]
    _chikouBelowCandle := close < close[chikouOffset_baseHtf]
    _chikouCross_bull = _chikouAboveCandle and not _chikouAboveCandle[1]
    _chikouCross_bear = _chikouBelowCandle and not _chikouBelowCandle[1]
    _chikouAboveKumo := close > _chikouKumoHigh
    _chikouBelowKumo := close < _chikouKumoLow
    _currentInKumo = (_senkouSpanLow <= close) and (close <= _senkouSpanHigh)
    _currentTouchingKumo = not _currentInKumo and
      (((low <= _senkouSpanHigh) and (low >= _senkouSpanLow)) or
      ((high >= _senkouSpanLow) and (high <= _senkouSpanHigh)))
    _currentAboveKumo = close > _senkouSpanHigh
    _currentBelowKumo = close < _senkouSpanLow
    _chikouInKumo = (_chikouKumoLow <= close) and (close <= _chikouKumoHigh)
    _chikouTouchingKumo = not _chikouInKumo and
      (((low <= _chikouKumoHigh) and (low >= _chikouKumoLow)) or
      ((high >= _chikouKumoLow) and (high <= _chikouKumoHigh)))
    _chikouKumoBreakout_bull = _chikouAboveKumo and not _chikouAboveKumo[1]
    _chikouKumoBreakout_bear = _chikouBelowKumo and not _chikouBelowKumo[1]
    [_currentInKumo, _currentTouchingKumo, _kumoColor_bull, _senkouCross_bull, _senkouCross_bear,
      _senkouSpanHigh, _senkouSpanLow,
      _chikouKumoHigh, _chikouKumoLow,
      _chikouAboveCandle, _chikouBelowCandle,
      _tenkanCross_bull, _tenkanCross_bear,
      _tenkanAboveKijun, _tenkanBelowKijun,
      _chikouCross_bull, _chikouCross_bear,
      _chikouAboveKumo, _chikouBelowKumo,
      _chikouKumoBreakout_bull, _chikouKumoBreakout_bear,
      _currentAboveKumo, _currentBelowKumo,
      _chikouInKumo, _chikouTouchingKumo]

f_calcIchimokuSignals_highestTf() =>
    var _chikouAboveCandle = false
    var _chikouBelowCandle = false
    var _chikouAboveKumo = false
    var _chikouBelowKumo = false
    _kumoColor_bull = senkouALine_highestTf > senkouBLine_highestTf
    _senkouCross_bull = crossover(senkouALine_highestTf, senkouBLine_highestTf)
    _senkouCross_bear = crossover(senkouBLine_highestTf, senkouALine_highestTf)
    _senkouSpanCurrentHigh = max(senkouALine_highestTf[senkouOffset_highestTf], senkouBLine_highestTf[senkouOffset_highestTf])
    _senkouSpanCurrentLow = min(senkouALine_highestTf[senkouOffset_highestTf], senkouBLine_highestTf[senkouOffset_highestTf])
    _chikouKumoHigh = max(senkouALine_highestTf[senkouOffset_highestTf+chikouOffset_highestTf], senkouBLine_highestTf[senkouOffset_highestTf+chikouOffset_highestTf])
    _chikouKumoLow = min(senkouALine_highestTf[senkouOffset_highestTf+chikouOffset_highestTf], senkouBLine_highestTf[senkouOffset_highestTf+chikouOffset_highestTf])
    _tenkanCross_bull = crossover(tenkanLine_highestTf, kijunLine_highestTf)
    _tenkanCross_bear = crossover(kijunLine_highestTf, tenkanLine_highestTf)
    _chikouAboveCandle := close > close[chikouOffset_highestTf]
    _chikouBelowCandle := close < close[chikouOffset_highestTf]
    _chikouCross_bull = _chikouAboveCandle and not _chikouAboveCandle[1]
    _chikouCross_bear = _chikouBelowCandle and not _chikouBelowCandle[1]
    _chikouAboveKumo := close > _chikouKumoHigh
    _chikouBelowKumo := close < _chikouKumoLow
    _chikouKumoBreakout_bull = _chikouAboveKumo and not _chikouAboveKumo[1]
    _chikouKumoBreakout_bear = _chikouBelowKumo and not _chikouBelowKumo[1]
    _currentAboveKumo = close > _senkouSpanCurrentHigh
    _currentBelowKumo = close < _senkouSpanCurrentLow
    [_kumoColor_bull, _senkouCross_bull, _senkouCross_bear,
      _senkouSpanCurrentHigh, _senkouSpanCurrentLow,
      _chikouKumoHigh, _chikouKumoLow,
      _chikouAboveCandle, _chikouBelowCandle,
      _tenkanCross_bull, _tenkanCross_bear,
      _chikouCross_bull, _chikouCross_bear,
      _chikouAboveKumo, _chikouBelowKumo,
      _chikouKumoBreakout_bull, _chikouKumoBreakout_bear,
      _currentAboveKumo, _currentBelowKumo]

f_calcGapKumo(_tf1_senkouA, _tf1_senkouB, _tf2_senkouA, _tf2_senkouB) =>
    _max = max(_tf1_senkouA, _tf1_senkouB, _tf2_senkouA, _tf2_senkouB)
    _tf1IsHigher = (_tf1_senkouA == _max) or (_tf1_senkouB == _max)
    _kumoA_max = _tf1IsHigher ? max(_tf1_senkouA, _tf1_senkouB) : max(_tf2_senkouA, _tf2_senkouB)
    _kumoA_min = _tf1IsHigher ? min(_tf1_senkouA, _tf1_senkouB) : min(_tf2_senkouA, _tf2_senkouB)
    _kumoB_max = _tf1IsHigher ? max(_tf2_senkouA, _tf2_senkouB) : max(_tf1_senkouA, _tf1_senkouB)
    _kumoB_min = _tf1IsHigher ? min(_tf2_senkouA, _tf2_senkouB) : min(_tf1_senkouA, _tf1_senkouB)
    _kumoCrossed = false
    _gapKumo_high = max(_kumoA_max, _kumoB_max)
    _gapKumo_low = _gapKumo_high
    if _kumoA_max > _kumoB_max
        if _kumoA_min < _kumoB_max
            _kumoCrossed := true
        else
            _gapKumo_high := _kumoA_min
            _gapKumo_low := _kumoB_max
    else
        if _kumoB_min < _kumoA_max
            _kumoCrossed := true
        else
            _gapKumo_high := _kumoB_min
            _gapKumo_low := _kumoA_max
    [_kumoCrossed, _gapKumo_high, _gapKumo_low]

//============================================================================
// calc Ichimoku values for plot
//============================================================================

//--------------------------------------
// HTF Ichimoku signals

[currentInKumo_baseHtf, _, kumoColor_bull_baseHtf, _, _,
  senkouSpanHigh_baseHtf, senkouSpanLow_baseHtf,
  _, _,
  chikouAboveCandle_baseHtf, chikouBelowCandle_baseHtf,
  _, _,
  tenkanAboveKijun_baseHtf, tenkanBelowKijun_baseHtf,
  _, _,
  chikouAboveKumo_baseHtf, chikouBelowKumo_baseHtf,
  _, _,
  currentAboveKumo_baseHtf, currentBelowKumo_baseHtf,
  _, _] = f_calcIchimokuSignals_baseHtf()

//--------------------------------------
// Highest TF Ichimoku signals

[kumoColor_bull_highestTf, senkouCross_bull_highestTf, senkouCross_bear_highestTf,
  senkouSpanHigh_highestTf, senkouSpanLow_highestTf,
  _, _,
  _, _,
  tenkanCross_bull_highestTf, tenkanCross_bear_highestTf,
  chikouCross_bull_highestTf, chikouCross_bear_highestTf,
  _, _,
  chikouKumoBreakout_bull_highestTf, chikouKumoBreakout_bear_highestTf,
  currentAboveKumo_highestTf, currentBelowKumo_highestTf] = f_calcIchimokuSignals_highestTf()

//--------------------------------------
// smooth lines

smooth_tenkanLine_baseHtf = f_smooth(tenkanLine_baseHtf)
smooth_kijunLine_baseHtf = f_smooth(kijunLine_baseHtf)
smooth_senkouALine_baseHtf = f_smooth(senkouALine_baseHtf)
smooth_senkouBLine_baseHtf = f_smooth(senkouBLine_baseHtf)

//smooth_tenkanLine_highestTf = f_smooth(tenkanLine_highestTf)
//smooth_kijunLine_highestTf = f_smooth(kijunLine_highestTf)
smooth_senkouALine_highestTf = f_smooth(senkouALine_highestTf)
smooth_senkouBLine_highestTf = f_smooth(senkouBLine_highestTf)

//--------------------------------------
// gap kumo

//max_bars_back(senkouALine_highestTf, 600)
//max_bars_back(senkouBLine_highestTf, 600)
[_, _gapKumo_high, _gapKumo_low] = f_calcGapKumo(
  senkouALine_baseHtf,
  senkouBLine_baseHtf,
  senkouALine_highestTf[senkouSpanIndexDiff],
  senkouBLine_highestTf[senkouSpanIndexDiff])

gapKumo_high := _gapKumo_high
gapKumo_low := _gapKumo_low

smooth_gapKumo_high = f_smooth(gapKumo_high)
smooth_gapKumo_low = f_smooth(gapKumo_low)

//============================================================================
// plot stuffs
//============================================================================

// gap kumo

val_gapKumo_high = useHighestTf and config_useGapKumo ? smooth_gapKumo_high : na
val_gapKumo_low = useHighestTf and config_useGapKumo ? smooth_gapKumo_low : na

plot_gapKumo_high = plot(val_gapKumo_high,
  offset=senkouOffsetForPlot_baseHtf,
  color=COLOR_TRANSPARENT,
  transp=0,
  linewidth=1,
  title="Gap Kumo High")
plot_gapKumo_low = plot(val_gapKumo_low,
  offset=senkouOffsetForPlot_baseHtf,
  color=COLOR_TRANSPARENT,
  transp=0,
  linewidth=1,
  title="Gap Kumo LOW")
fill(plot_gapKumo_high, plot_gapKumo_low,
  color=COLOR_GAP_KUMO,
  transp=0,
  title="Gap Kumo")

// kumo HighestTF

plotVal_senkouA_highestTf = useHighestTf ? smooth_senkouALine_highestTf : na
plotVal_senkouB_highestTf = useHighestTf ? smooth_senkouBLine_highestTf : na
color_kumo_highestTf = kumoColor_bull_highestTf ? COLOR_KUMO_HIGHESTTF_BULL : COLOR_KUMO_HIGHESTTF_BEAR
plot_senkouA_highestTf = plot(plotVal_senkouA_highestTf,
  offset=senkouOffsetForPlot_highestTf,
  color=COLOR_SENKOUA_HIGHESTTF,
  transp=0,
  linewidth=1,
  title="Highest TFSenkou-Span A")
plot_senkouB_highestTf = plot(plotVal_senkouB_highestTf,
  offset=senkouOffsetForPlot_highestTf,
  color=COLOR_SENKOUB_HIGHESTTF,
  transp=0,
  linewidth=1,
  title="Highest TFSenkou-Span B")
fill(plot_senkouA_highestTf, plot_senkouB_highestTf,
  color=color_kumo_highestTf,
  transp=0,
  title="Highest TF Kumo")

// kumo HTF

color_kumo_baseHtf = kumoColor_bull_baseHtf ? COLOR_KUMO_BASEHTF_BULL : COLOR_KUMO_BASEHTF_BEAR
plot_senkouA_baseHtf = plot(smooth_senkouALine_baseHtf,
  offset=senkouOffsetForPlot_baseHtf,
  color=COLOR_SENKOUA_BASEHTF,
  transp=0,
  linewidth=1,
  title="Base HTF: Senkou-Span A")
plot_senkouB_baseHtf = plot(smooth_senkouBLine_baseHtf,
  offset=senkouOffsetForPlot_baseHtf,
  color=COLOR_SENKOUB_BASEHTF,
  transp=0,
  linewidth=1,
  title="Base HTF: Senkou-Span B")
fill(plot_senkouA_baseHtf, plot_senkouB_baseHtf,
  color=color_kumo_baseHtf,
  transp=0,
  title="Base HTF Kumo")

// chikou

plot(close,
  offset=chikouOffsetForPlot_baseHtf,
  color=COLOR_CHIKOU,
  linewidth=1,
  transp=0,
  title="Chikou-Span")

// tenkan & kijun

plot(smooth_tenkanLine_baseHtf,
  color=COLOR_TENKAN,
  linewidth=1,
  transp=0,
  title="Tenkan-Sen")
plot(smooth_kijunLine_baseHtf,
  color=COLOR_KIJUN,
  linewidth=3,
  transp=0,
  title="Kijun-Sen")

//============================================================================
// backtest duration
//============================================================================

// Calculate start/end date and time condition
startDate  = timestamp(fromYear, fromMonth, fromDay, 00, 00)
finishDate = timestamp(toYear,   toMonth,   toDay,   00, 00)

//============================================================================
// bullish / bearish detection
//============================================================================

var bullish = false
var bearish = false

// detect bullish or bearish by many incidents
bullish := kumoColor_bull_baseHtf and chikouAboveCandle_baseHtf and
  tenkanAboveKijun_baseHtf and chikouAboveKumo_baseHtf and
  currentAboveKumo_baseHtf and currentAboveKumo_highestTf
bearish := not kumoColor_bull_baseHtf and chikouBelowCandle_baseHtf and
  tenkanBelowKijun_baseHtf and chikouBelowKumo_baseHtf and
  currentBelowKumo_baseHtf and currentBelowKumo_highestTf

// calc continuous bullish or bearish
f_isContinuousBull() =>
    inTrend = 0
    for i = 0 to (config_bullDetectBars-1)
        if bullish[i]
            inTrend := inTrend + 1
    inTrend == config_bullDetectBars
f_isContinuousBear() =>
    inTrend = 0
    for i = 0 to (config_bullDetectBars-1)
        if bearish[i]
            inTrend := inTrend + 1
    inTrend == config_bullDetectBars

continuousBull = f_isContinuousBull()
continuousBear = f_isContinuousBear()

//============================================================================
// MACD
//============================================================================

f_isMacdUpTrend() =>
    [_macdLine, _signalLine, _] = macd(close, 12, 26, 9)
    _upTrend = _macdLine > _signalLine

// calc macd for entry
macdUpTrend = f_isMacdUpTrend()
macdDownTrend = not macdUpTrend

//============================================================================
// recent high / low
//============================================================================

f_calcRecentHigh() =>
    startIndex = 1
    highest = max(open[startIndex], close[startIndex])
    for i = startIndex to (config_recentBars - 1)
        current = max(open[i], close[i])
        if highest < current
            highest := current
    highest
f_calcRecentLow() =>
    startIndex = 1
    lowest = min(open[startIndex], close[startIndex])
    for i = startIndex to (config_recentBars - 1)
        current = min(open[i], close[i])
        if lowest > current
            lowest := current
    lowest

recentHigh = f_calcRecentHigh()
recentLow = f_calcRecentLow()

//============================================================================
// ATR based stuff
//============================================================================

entryPadding = config_atrEntryPaddingMult * atr(config_atrEntryPaddingLength)
sl_val = config_atrSlMult * atr(config_atrLength)
atrSl_long = low - sl_val
atrSl_short = high + sl_val

//============================================================================
// Kumo based SL
//============================================================================

kumoBasedSlLong = (senkouSpanHigh_baseHtf + atrSl_long) / 2
kumoBasedSlShort = (senkouSpanLow_baseHtf + atrSl_short) / 2

//============================================================================
// Mixed SL
//============================================================================

var initialEntryTpPrice_long = hl2
var initialEntryTpPrice_short = hl2
var mixedTrailingSl_long = hl2
var mixedTrailingSl_short = hl2

mixedSlLong = min(atrSl_long, kumoBasedSlLong, recentLow)
mixedSlShort = max(atrSl_short, kumoBasedSlShort, recentHigh)
//plot(mixedSlLong, color=color.green, linewidth=2)
//plot(mixedSlShort, color=color.red, linewidth=2)


//============================================================================
// Range Edge caliculation
//============================================================================

// calc range edge
f_calcRangeEdge_high() =>
    //highest = high[3]
    highest = max(open[3], close[3])
    for i = 4 to (config_rangeDetectBars - 1)
        //current = high[i]
        current = max(open[i], close[i])
        if highest < current
            highest := current
    highest
f_calcRangeEdge_low() =>
    //lowest = low[3]
    lowest = min(open[3], close[3])
    for i = 4 to (config_rangeDetectBars - 1)
        //current = low[i]
        current = min(open[i], close[i])
        if lowest > current
            lowest := current
    lowest

entryLimitHigh = f_calcRangeEdge_high()
entryLimitLow = f_calcRangeEdge_low()

plot(entryLimitHigh, color=COLOR_RANGE_EDGE, linewidth=1)
plot(entryLimitLow, color=COLOR_RANGE_EDGE, linewidth=1)

//============================================================================
// entry timing judge
//============================================================================

f_trendStatsChanged() =>
    var trendStats = 0
    var kumoBull = false
    var kumoBear = false
    var kumoRanged = false
    kumoBull := currentAboveKumo_baseHtf and currentAboveKumo_highestTf
    kumoBear := currentBelowKumo_baseHtf and currentBelowKumo_highestTf
    kumoRanged := not kumoBull and not kumoBear
    if kumoRanged
        trendStats := 0
        // respect recent bullish or bearish
        if kumoBull[1] or kumoBull[2] or kumoBull[3] or kumoBear[1] or kumoBear[2] or kumoBear[3]
            trendStats := trendStats[1]
    if kumoBull
        trendStats := 1
    if kumoBear
        trendStats := -1
    trendStats[1] != trendStats[0]

trendStatsChanged = f_trendStatsChanged()

var isFirstEntry = true
var resistancePrice = hl2
var rangeBreakSignalDetected = false
var rangeBreakMacdReversalDetected = false
var rangeBreakConfirmed = false

//--------------------------------------
// reset flags

// if trend stats changed, reset flags
if trendStatsChanged
    isFirstEntry := true
    rangeBreakSignalDetected := false
    rangeBreakMacdReversalDetected := false
    rangeBreakConfirmed := false
else
    // if has no positions
    if strategy.position_size == 0
        isFirstEntry := isFirstEntry[1]
        // if lost positions in prev bar
        if lostPositionsInPrevBar
            rangeBreakSignalDetected := false
            rangeBreakConfirmed := false
        else
            rangeBreakSignalDetected := rangeBreakSignalDetected[1]
            rangeBreakConfirmed := rangeBreakConfirmed[1]
    // if has any position
    else
        isFirstEntry := false // no more first entry
        resistancePrice := hl2 // no resistance price

//--------------------------------------
// 1st entry confirmation

var firstReverseAfterTrendChange = false
var trendConfirmed = false

// this operation is for the 1st entry
if isFirstEntry
    if bullish
        // wait for the short reversal for confirmation
        if not firstReverseAfterTrendChange and not rangeBreakMacdReversalDetected
            if macdDownTrend
                rangeBreakMacdReversalDetected := true
        if rangeBreakMacdReversalDetected
            if macdUpTrend
                trendConfirmed := true
    if bearish
        if not firstReverseAfterTrendChange and not rangeBreakMacdReversalDetected
            if macdUpTrend
                rangeBreakMacdReversalDetected := true
        if rangeBreakMacdReversalDetected
            if macdDownTrend
                trendConfirmed := true
else
    trendConfirmed := false
    firstReverseAfterTrendChange := false

if (bullish[0] != bullish[1]) or (bearish[0] != bearish[1])
    trendConfirmed := false

// plot trend confirmation
if (trendConfirmed[0] == true) and (trendConfirmed[1] == false)
    if bullish
        label.new(bar_index, low, 'Trend Confirmed', style=label.style_triangleup, size=size.tiny, color=#ffffff, textcolor=#ffffff)
    else
        label.new(bar_index, high, 'Trend Confirmed', style=label.style_triangledown, size=size.tiny, color=#ffffff, textcolor=#ffffff)

//--------------------------------------
// later entry confirmation

if strategy.position_size == 0
    // this operation is for later entry
    if isFirstEntry
        resistancePrice := hl2
    else
        // if range break was detected, set flag
        if not rangeBreakSignalDetected and continuousBull and entryLimitHigh < close
            rangeBreakSignalDetected := true
        if not rangeBreakSignalDetected and continuousBear and close < entryLimitLow
            rangeBreakSignalDetected := true
        // after range break, wait for short MACD reversal.
        // after this confirmation, make stop entry
        if rangeBreakSignalDetected
            if senkouSpanHigh_baseHtf < close
                resistancePrice := entryLimitHigh
                if macdDownTrend
                    rangeBreakConfirmed := true
            if senkouSpanLow_baseHtf > close
                resistancePrice := entryLimitLow
                if macdUpTrend
                    rangeBreakConfirmed := true

// plot rangebreak detection
if (rangeBreakSignalDetected[0] == true) and (rangeBreakSignalDetected[1] == false)
    if bullish
        label.new(bar_index, low, 'Rangebreak detected', style=label.style_triangleup, size=size.tiny, color=#ffffff, textcolor=#ffffff)
    else
        label.new(bar_index, high, 'Rangebreak detected', style=label.style_triangledown, size=size.tiny, color=#ffffff, textcolor=#ffffff)

// plot resistance price line
resitancePriceColor = rangeBreakConfirmed and strategy.position_size == 0 ? COLOR_RESISTANCE_PRICE_LINE : COLOR_TRANSPARENT
plot(resistancePrice, linewidth=4, color=resitancePriceColor)

//============================================================================
// Pyramiding entry judge
//============================================================================

f_calcExtraEntryStopForShort() =>
    startIndex = 2
    highest = max(open[startIndex], close[startIndex])
    for i = startIndex to (config_extraEntryStopDetectBars - 1)
        current = max(open[i], close[i])
        if highest < current
            highest := current
    highest

f_calcExtraEntryStopForLong() =>
    startIndex = 2
    lowest = min(open[startIndex], close[startIndex])
    for i = startIndex to (config_extraEntryStopDetectBars - 1)
        current = min(open[i], close[i])
        if lowest > current
            lowest := current
    lowest

extraEntryStopLong = f_calcExtraEntryStopForLong() + entryPadding
extraEntryStopShort = f_calcExtraEntryStopForShort() - entryPadding
extraEntryStop = bullish ? extraEntryStopLong :
  bearish ? extraEntryStopShort :
  hl2
extraEntryStopColor = (bullish or bearish) ? color.green : COLOR_TRANSPARENT

// plot as line
plot(extraEntryStop, color=extraEntryStopColor, linewidth=2)

//============================================================================
// entry, TP, SL
//============================================================================

shouldMakeEntryLong = continuousBull and ((isFirstEntry and trendConfirmed) or (rangeBreakConfirmed and close > resistancePrice))
shouldMakeEntryShort = continuousBear and ((isFirstEntry and trendConfirmed) or (rangeBreakConfirmed and close < resistancePrice))

// TP caliculation
if strategy.position_size == 0
    initialEntryTpPrice_long := entryLimitHigh + (hl2 - extraEntryStopLong)
    initialEntryTpPrice_short := entryLimitLow - (extraEntryStopShort - hl2)

// SL caliculation
mixedTrailingSl_long := if ((strategy.position_size == 0) and shouldMakeEntryLong) or ((strategy.position_size > 0) and (mixedTrailingSl_long < mixedSlLong))
    mixedSlLong
else if (strategy.position_size == 0) or (strategy.position_size < 0)
    hl2
else
    mixedTrailingSl_long[1]

mixedTrailingSl_short := if ((strategy.position_size == 0) and shouldMakeEntryShort) or ((strategy.position_size < 0) and (mixedTrailingSl_short > mixedSlShort))
    mixedSlShort
else if (strategy.position_size == 0) or (strategy.position_size > 0)
    hl2
else
    mixedTrailingSl_short[1]

// final entry price
initialEntryStopPrice_long = entryLimitHigh + entryPadding
initialEntryStopPrice_short = entryLimitLow - entryPadding
extraEntryLimitPrice_long = extraEntryStopLong
extraEntryLimitPrice_short = extraEntryStopShort
extraEntryTpPrice_long = entryLimitHigh + entryPadding
extraEntryTpPrice_short = entryLimitLow - entryPadding

//============================================================================
// Quantitiy caliculation
//============================================================================

var initialQty = 0
slPercentage = bullish ? abs(mixedTrailingSl_long-hl2)/hl2*100 : abs(mixedTrailingSl_short-hl2)/hl2*100
qtyForInitialEntry = f_calcQtyForInitialEntry(slPercentage)
qtyForExtraEntry = f_calcQtyForExtraEntry(slPercentage)

//============================================================================
// Pyramiding judge
//============================================================================

var anyTpTriggered = false
var hasAnyPositions = false

if strategy.position_size != 0 and strategy.position_size[1] == 0
    hasAnyPositions := true
if strategy.position_size == 0 and strategy.position_size[1] != 0
    hasAnyPositions := false
    anyTpTriggered := false

if hasAnyPositions and not anyTpTriggered and (abs(strategy.position_size) < initialQty)
    anyTpTriggered := true

pyramidingReady =
  (anyTpTriggered and bullish and (extraEntryStopLong > previousEntryPrice) and f_isLastCandleUp()) or
  (anyTpTriggered and bearish and (extraEntryStopShort < previousEntryPrice) and f_isLastCandleDown())

//============================================================================
// make entries
//============================================================================

if (time >= startDate and time <= finishDate)

    if strategy.position_size == 0

        if shouldMakeEntryLong
            initialQty := qtyForInitialEntry
            strategy.entry(id="Long", long=true, stop=initialEntryStopPrice_long, qty=initialQty)
            previousEntryPrice := entryLimitHigh
        if shouldMakeEntryShort
            initialQty := qtyForInitialEntry
            strategy.entry(id="Short", long=false, stop=initialEntryStopPrice_short, qty=initialQty)
            previousEntryPrice := entryLimitLow

    // Long pyramiding
    if (strategy.position_size > 0) and pyramidingReady
        previousEntryPrice := extraEntryStopLong
        strategy.entry(id="LongExtra", long=true, limit=extraEntryLimitPrice_long, qty=qtyForExtraEntry)
    else
        strategy.cancel(id="LongExtra")

    // Short pyramiding
    if (strategy.position_size < 0) and pyramidingReady
        previousEntryPrice := extraEntryStopShort
        strategy.entry(id="ShortExtra", long=false, limit=extraEntryLimitPrice_short, qty=qtyForExtraEntry)
    else
        strategy.cancel(id="ShortExtra")

    // cancel stop entries
    if not continuousBull
        strategy.cancel(id="Long")
        strategy.cancel(id="LongExtra")
    if not continuousBear
        strategy.cancel(id="Short")
        strategy.cancel(id="ShortExtra")

    // TP, SL
    strategy.exit('Long-TP', 'Long', stop=mixedTrailingSl_long, limit=initialEntryTpPrice_long, qty=(initialQty/2))
    strategy.exit('Long-SL', 'Long', stop=mixedTrailingSl_long)
    strategy.exit('LongExtra-SL/TP', 'LongExtra', stop=mixedTrailingSl_long, limit=extraEntryTpPrice_long)
    strategy.exit('Short-TP', 'Short', stop=mixedTrailingSl_short, limit=initialEntryTpPrice_short, qty=(initialQty/2))
    strategy.exit('Short-SL', 'Short', stop=mixedTrailingSl_short)
    strategy.exit('ShortExtra-SL/TP', 'ShortExtra', stop=mixedTrailingSl_short, limit=extraEntryTpPrice_short)

//============================================================================
// plot SL, TP
//============================================================================

sl = if strategy.position_size > 0
    mixedTrailingSl_long
else if strategy.position_size < 0
    mixedTrailingSl_short
else
    na

tp = if hasAnyPositions and not anyTpTriggered
    strategy.position_size > 0 ? initialEntryTpPrice_long : initialEntryTpPrice_short
else
    na

plot(sl, color=color.red, style=plot.style_cross, linewidth=2, title="SL")
plot(tp, color=color.blue, style=plot.style_cross, linewidth=2, title="T")


//============================================================================
// debug
//============================================================================

// continuous bull / bear check
value_bgcolor = continuousBull ? color.green : continuousBear ? color.red: color.new(#000000, 100)

// MACD up or down check
//value_bgcolor = macdUpTrend ? color.green : color.red

// both kumo bull
//value_bgcolor = (kumoColor_bull_baseHtf and kumoColor_bull_highestTf) ? color.green:
//  (not kumoColor_bull_baseHtf and not kumoColor_bull_highestTf) ? color.red: color.new(#000000, 100)

// doji
//dojiUp = bullish and f_isUpDoji()
//dojiDown = bearish and f_isDownDoji()
//value_bgcolor = dojiUp ? color.green :
//  dojiDown ? color.red :
//  COLOR_TRANSPARENT

//value_bgcolor = f_isLastCandleUp() ? color.green : COLOR_TRANSPARENT
//value_bgcolor = pyramidingReady ? color.green : COLOR_TRANSPARENT

bgcolor(value_bgcolor, transp=95)
