Dual_MACD_trending
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © vpirinski
//@version=5

//#region *********** DESCRIPTION ***********
// ================================================== INFO ==================================================
// This indicator is useful for trending assets, as my preference is for low-frequency trading, thus using BTCUSD on 1D/1W chart
// In the current implementation I find two possible use cases for the indicator:
// - as a stand-alone indicator on the chart which can also fire alerts that can help to determine if we want to manually enter/exit trades based on the signals from it (1D/1W is good for non-automated trading)
// - can be used to connect to the Signal input of the TTS (TempalteTradingStrategy) by jason5480 in order to backtest it, thus effectively turning it into a strategy (instructions below in TTS CONNECTIVITY section)
// Trading period can be selected from the indicator itself to limit to more interesting periods.
// Arrow indications are drawn on the chart to indicate the trading conditions met in the script - light green for HTF crossover, dark green for LTF crossover and orange for LTF crossunder.
// Note that the indicator performs best in trending assets and markets, and it is advisable to use additional indicators to filter the trading conditions when market/asset is expected to move sideways.

// ================================================== DETAILS ==================================================
// It uses a couple of MACD indicators - one from the current timeframe and one from a higher timeframe, as the crossover/crossunder cases of the MACD line and the signal line indicate the potential entry/exit points.
// The strategy has the following flow:
// - If the weekly MACD is positive (MACD line is over the signal line) we have a trading window.
// - If we have a trading window, we buy when the daily macd line crosses AND closes above the signal line.
// - If we are in a position, we await the daily MACD to cross AND close under the signal line, and only then place a stop loss under the wick of that closing candle.
// The user can select both the higher (HTF) and lower (LTF) timeframes. Preferably the lower timeframe should be the one that the Chart is on for better visualization.

// If one to decide to use the indicator as a strategy, it implements the following buy and sell criterias, which are feed to the TTS, but can be also manually managed via adding alerts from this indicator.
// Since usually the LTF is preceeding the crossover compared to the HTF, then my interpretation of the strategy and flow that it follows is allowing two different ways to enter a trade:
// - crossover (and bar close) of the macd over the signal line in the HIGH TIMEFRAME (no need to look at the LOWER TIMEFRMAE)
// - crossover (and bar close) of the macd over the signal line in the LOW TIMEFRAME, as in this case we need to check also that the macd line is over the signal line for the HIGH TIMEFRAME as well (like a regime filter)
// The exit of the trade is based on the lower timeframe MACD only, as we create a stop loss eqaual to the lower wick of the bar, once the macd line crosses below the signal line on that timeframe

// ================================================== SETTINGS ==================================================
// Leaving all of the settings as in vanilla use case.
// User can set all of the MACD parameters for both the higher and lower (current) timeframes, currently left to default of the MACD stand-alone indicator itself.
// The start-end date is a time filter that can be extermely usefull when backtesting different time periods.

// ================================================== TTS SETTINGS (NEEDED IF USED TO BACKTEST WITH TTS) ==================================================
// The TempalteTradingStrategy is a strategy script developed in Pine by jason5480, which I recommend for quick turn-around of testing different ideas on a proven and tested framework
// I cannot give enough credit to the developer for the efforts put in building of the infrastructure, so I advice everyone that wants to use it first to get familiar with the concept and by checking 
// by checking jason5480's profile https://www.tradingview.com/u/jason5480/#published-scripts
// The TTS itself is extremely functional and have a lot of properties, so its functionality is beyond the scope of the current script - 
// Again, I strongly recommend to be thoroughly epxlored by everyone that plans on using it.
// In the nutshell it is a script that can be feed with buy/sell signals from an external indicator script and based on many configuration options it can determine how to execute the trades.
// The TTS has many settings that can be applied, so below I will cover only the ones that differ from the default ones, at least according to my testing - do your own research, you may find something even better :)
// The current/latest version that I've been using as of writing and testing this script is TTSv48
// Settings which differ from the default ones:
//   - from                   - False                                 (time filter is from the indicator script itself)
//   - Deal Conditions Mode   - External                              (take enter/exit conditions from an external script)
//   - 🔌Signal 🛈➡          - Dual_MACD: 🔌Signal to TTSv48         (this is the output from the indicator script, according to the TTS convention)
//   - Sat/Sun                - true                                  (for crypto, in order to trade 24/7)
//   - Order Type​​             - STOP                                  (perform stop order)
//   - Distance Method​​        - HHLL                                  (HigherHighLowerLow - in order to set the SL according to the strategy definition from above)
//
// The next are just personal preferenes, you can feel free to experiment according to your trading style
//   - Take Profit Targets    - 0                                     (either 100% in or out, no incremental stepping in or out of positions)
//   - Dist Mul|Len Long/Short- 10                                    (make sure that we don't close on profitable trades by any reason)
//   - Quantity Method        - EQUITY                                (personal backtesting preference is to consider each backtest as a separate portfolio, so determine the position size by 100% of the allocated equity size)
//   - Equity %               - 100                                   (note above)

// ================================================== TODO ==================================================
// 1. Seems that if we change the source of the MA for the MACD from close to low for BTC we get better results, and even better for ohlc4 - room to explore further.

//#endregion ========================================================================================================

//#region *********** STRATEGY_SETUP *********** 
indicator(title                 =   'Dual_MACD_trending', 
          shorttitle            =   'Dual_MACD', 
          overlay               =   true,  
          explicit_plot_zorder  =   true
         ) 

//#endregion ========================================================================================================

//#region *********** LIBRARIES *********** 
import HeWhoMustNotBeNamed/enhanced_ta/14 as eta
import jason5480/tts_convention/3         as tts_conv
//#endregion ========================================================================================================

//#region *********** USER_INPUT *********** 
var string MACD_GROUP_STR   = "MACD"
i_macd_high_timeframe       = input.timeframe(title = "High timeframe",         defval = "W",   group = MACD_GROUP_STR)
i_macd_low_timeframe        = input.timeframe(title = "Low timeframe",          defval = "",    group = MACD_GROUP_STR)
i_macd_ma_source            = input.source   (title = "Source",                 defval = close, group = MACD_GROUP_STR)
i_macd_osc_ma_type          = input.string   (title = "Osc MA type",            defval = "ema", group = MACD_GROUP_STR, options = ["ema", "sma", "rma", "hma", "wma", "vwma", "swma"])
i_macd_signal_line_ma_type  = input.string   (title = "Signal MA type",         defval = "ema", group = MACD_GROUP_STR, options = ["ema", "sma", "rma", "hma", "wma", "vwma", "swma"])
i_macd_fast_ma_length       = input.int      (title = "Fast MA Length",         defval = 12,    group = MACD_GROUP_STR)
i_macd_slow_ma_length       = input.int      (title = "Slow MA Length",         defval = 26,    group = MACD_GROUP_STR)
i_macd_signal_length        = input.int      (title = "Low MACD Hist",          defval = 9,     group = MACD_GROUP_STR)
i_macd_repaint_en           = true // input.bool     (title = "MACD Repainting On/Off", defval = true,  group = MACD_GROUP_STR, tooltip="Off for use as an Indicator to avoid repainting. On for use in Strategies so that trades can respond to realtime data.") - always on since used as an indicator

var string TIME_GROUP_STR   = "Start and End Time"
i_start_time                = input.time     (title="Start Date",               defval=timestamp("01 Jan 2016 13:30 +0000"), group=TIME_GROUP_STR)
i_end_time                  = input.time     (title="End Date",                 defval=timestamp("1 Jan 2099 19:30 +0000"),  group=TIME_GROUP_STR)

var string GENERAL_GROUP_STR = "GENERAL"
i_general_gaps_on           = input.bool     (title = "Gaps On/Off",            defval = false, group = GENERAL_GROUP_STR, tooltip = "When enabled set gaps to barmerge.gaps_on in the security function")
i_general_lookahead_on      = input.bool     (title = "Looakahead On/Off",      defval = false, group = GENERAL_GROUP_STR, tooltip = "When enabled set looakahead to barmerge.lookahead_off in the security function")

//#endregion ========================================================================================================

//#region *********** COMMON_FUNCTIONS *********** 
// Function offering a repainting/no-repainting version of the HTF data (but does not work on tuples).
// It has the advantage of using only one `security()` call for both.
// The built-in MACD function behaves identically to Repainting On in the custom function below. In other words _repaint = TRUE. 
// https://www.tradingview.com/script/cyPWY96u-How-to-avoid-repainting-when-using-security-PineCoders-FAQ/
f_security(_symbol, _tf, _src, _repaint) => request.security(symbol     = _symbol,
                                                             timeframe  = _tf, 
                                                             expression = _src[_repaint          ? 0                     : barstate.isrealtime ? 1 : 0],
                                                             gaps       = i_general_gaps_on      ? barmerge.gaps_on      : barmerge.gaps_off,
                                                             lookahead  = i_general_lookahead_on ? barmerge.lookahead_on : barmerge.lookahead_off)[_repaint ? 0 : barstate.isrealtime ? 0 : 1]
//#endregion ========================================================================================================

//#region *********** LOGIC *********** 
// Get the current TF MACD lines and then interpolate them for the HTF and LTF (and SL TF)
fast_ma             = eta.ma(source = i_macd_ma_source, maType = i_macd_osc_ma_type, length = i_macd_fast_ma_length)
slow_ma             = eta.ma(source = i_macd_ma_source, maType = i_macd_osc_ma_type, length = i_macd_slow_ma_length)
ctf_macd_line       = fast_ma - slow_ma
ctf_signal_line     = eta.ma(source = ctf_macd_line, maType = i_macd_signal_line_ma_type, length = i_macd_signal_length)
ctf_hist_line       = ctf_macd_line - ctf_signal_line

htf_macd_line       = f_security(syminfo.tickerid, i_macd_high_timeframe, ctf_macd_line,    i_macd_repaint_en)
htf_signal_line     = f_security(syminfo.tickerid, i_macd_high_timeframe, ctf_signal_line,  i_macd_repaint_en)
htf_hist_line       = f_security(syminfo.tickerid, i_macd_high_timeframe, ctf_hist_line,    i_macd_repaint_en)

ltf_macd_line       = f_security(syminfo.tickerid, i_macd_low_timeframe,  ctf_macd_line,    i_macd_repaint_en)
ltf_signal_line     = f_security(syminfo.tickerid, i_macd_low_timeframe,  ctf_signal_line,  i_macd_repaint_en)
ltf_hist_line       = f_security(syminfo.tickerid, i_macd_low_timeframe,  ctf_hist_line,    i_macd_repaint_en)

// Determine the strategy trading conditions
htf_crossover       = ta.crossover  (htf_macd_line, htf_signal_line)
ltf_crossover       = ta.crossover  (ltf_macd_line, ltf_signal_line)
ltf_crossunder      = ta.crossunder (ltf_macd_line, ltf_signal_line)  

// Filters
htf_regime_filter   = htf_macd_line > htf_signal_line
time_and_bar_filter = barstate.isconfirmed and ((time >= i_start_time) and (time <= i_end_time))

//#endregion ========================================================================================================

//#region *********** BUY_&_SELL_SIGNALS *********** 
htf_buy_signal      = time_and_bar_filter and htf_crossover
ltf_buy_signal      = time_and_bar_filter and ltf_crossover and htf_regime_filter

buy_signal          = htf_buy_signal      or   ltf_buy_signal
set_sl_signal       = time_and_bar_filter and  ltf_crossunder
//#endregion ========================================================================================================

//#region *********** BUY_&_SELL_SIGNALS TO TTS *********** 
tts_deal_conditions = tts_conv.DealConditions.new(
     startLongDeal       = buy_signal,
      startShortDeal     = false,
	 endLongDeal        = set_sl_signal,
	 endShortDeal       = false,
	 cnlStartLongDeal   = false,
	 cnlStartShortDeal  = false,
	 cnlEndLongDeal     = false,
	 cnlEndShortDeal    = false)

plot(series = tts_conv.getSignal(tts_deal_conditions), title = '🔌Signal to TTS', color = color.olive, display = display.data_window + display.status_line)
//#endregion ========================================================================================================

//#region *********** DEBUG_&_PLOTS *********** 
plotshape(htf_buy_signal, style=shape.triangleup,   location=location.bottom,   size = size.tiny, color=htf_buy_signal ? color.rgb(19, 231, 26)  : na)
plotshape(ltf_buy_signal, style=shape.triangleup,   location=location.bottom,   size = size.tiny, color=ltf_buy_signal ? color.rgb(9, 97, 82)    : na)
plotshape(set_sl_signal,  style=shape.triangledown, location=location.top,      size = size.tiny, color=set_sl_signal  ? color.rgb(216, 129, 71) : na)
//#endregion ========================================================================================================

//#region *********** ALERTS *********** 
alertcondition(htf_buy_signal, "DualMacdHTFCrossOver", "MACD HTF line xOver signal line for {{ticker}} at price {{close}}")
alertcondition(ltf_buy_signal, "DualMacdLTFCrossOver", "MACD LTF line xUnder signal line for {{ticker}} at price {{close}}")
alertcondition(set_sl_signal,  "DualMacdLTFCrossOver", "MACD LTF line xUnder signal line for {{ticker}} at price {{close}}")
// Better to keep them as part of the TTS, since in the indicator we cannot detect if we're already in a trade or not...
//#endregion ========================================================================================================
