```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)
```