```js // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © hunkOfTuna //@version=5 strategy("Simple Spread Strategy", overlay = false, initial_capital = 10000, commission_type = strategy.commission.percent, commission_value = 0.25, pyramiding = 0) date_from = input.time(defval = timestamp("01 Jan 2023 00:00 +0000"), title = "From") date_thru = input.time(defval = timestamp("01 Jan 2024 00:00 +0000"), title = "To") display_date_range = input(defval = true, title = "Show Date Range") is_within_date_range() => time >= date_from and time <= date_thru // Inputs dataset_input = input.source (close, 'Dataset') timeframe_input = input.timeframe('', 'Timeframe', tooltip='A timeframe of the lagged dataset used as a divisor in ratio') short_window = input.int (2, 'Short Window [2..n]', 2, tooltip='A lookback period for the first moving average') long_window = input.int (100, 'Long Window [3..m]', 3, tooltip='A lookback period for the second moving average') moving_average_type= input.string ('SMA', 'Moving Average Function', ['EMA','HMA','SMA','VWAP','WMA']) zscore_threshold = input.float (20.0, 'Z-Score Threshold [0.1..400.0]', minval=0.1, maxval=400.0, step=0.1) tick_threshold = input.int (252, 'Tick Threshold [2..500]', 2, 500, tooltip='Used for deciding on what bar to exit a trade') invert_color = input.bool (true, 'Invert the Color', tooltip='Occasionally signal color inversion is required') // Constants var int BUY_SIGNAL = 1 var int SELL_SIGNAL =-1 var int CLEAR_SIGNAL = 0 // Variables var int current_signal = CLEAR_SIGNAL var array<int> tick_array = array.new<int>(1, 0) // array used as a container for inter-bar variables // Signals float lagged_dataset = request.security('', timeframe_input, dataset_input[1]) float ratio_logs = math.log(dataset_input / lagged_dataset) float short_moving_average = switch moving_average_type 'EMA' => ta.ema (ratio_logs, short_window) 'HMA' => ta.hma (ratio_logs, short_window) 'VWAP' => ta.vwap(ratio_logs) 'WMA' => ta.wma (ratio_logs, short_window) => ta.sma(ratio_logs, short_window) float long_moving_average = switch moving_average_type 'EMA' => ta.ema (ratio_logs, long_window) 'HMA' => ta.hma (ratio_logs, long_window) 'VWAP' => ta.vwap(ratio_logs[1]) 'WMA' => ta.wma (ratio_logs, long_window) => ta.sma(ratio_logs, long_window) float standard_deviation = ta.stdev(long_moving_average, math.round(math.avg(short_window, long_window))) float z_score = (short_moving_average - long_moving_average) / standard_deviation bool is_long = z_score < -zscore_threshold bool is_short = z_score > zscore_threshold bool is_clear = not(is_long and is_short) current_signal := is_long ? BUY_SIGNAL : is_short ? SELL_SIGNAL : is_clear ? CLEAR_SIGNAL : nz(current_signal[1]) if array.get(tick_array, 0) == tick_threshold current_signal := CLEAR_SIGNAL array.set(tick_array, 0, 0) if current_signal == BUY_SIGNAL array.set(tick_array, 0, array.get(tick_array, 0) + 1) if current_signal == SELL_SIGNAL array.set(tick_array, 0, array.get(tick_array, 0) + 1) int signal_change = ta.change(current_signal) bool long_condition = signal_change and current_signal == BUY_SIGNAL bool short_condition = signal_change and current_signal == SELL_SIGNAL bool clear_condition = signal_change and current_signal == CLEAR_SIGNAL color buy_color = invert_color ? color.red : color.green color sell_color = invert_color ? color.green : color.red // Visuals hline(zscore_threshold, 'Upper Bound', color.red) hline(-zscore_threshold, 'Lower Bound', color.green) plot(CLEAR_SIGNAL, 'Midline', color.silver, 1) plot(z_score, 'Z-Score') plot(long_condition ? z_score : na, 'Sell Signal', sell_color, 2, plot.style_circles) plot(short_condition ? z_score : na, 'Buy Signal', buy_color, 2, plot.style_circles) plot(clear_condition ? z_score : na, 'Clear Position', color.orange, 2, plot.style_cross) // Alerts if signal_change and current_signal == BUY_SIGNAL alert('Buy Alert', alert.freq_once_per_bar) if signal_change and current_signal == SELL_SIGNAL alert('Sell Alert', alert.freq_once_per_bar) alertcondition(long_condition, 'Buy', 'Go long') alertcondition(short_condition, 'Sell', 'Go short') // Orders if (long_condition) strategy.entry("Long", strategy.long) if (short_condition) strategy.entry("Short", strategy.short) ```