Tesla Coil ML
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © jdehorty, based on work by DasanC, veryfid, and John Ehlers
// @version=5

// This is a re-implementation of @veryfid's wonderful Tesla Coil indicator to leverage basic Machine Learning Algorithms to help classify coil crossovers.
// The original Tesla Coil indicator requires extensive training and practice for the user to develop adequate intuition to interpret coil crossovers.
// The goal for this version is to help the user understand the underlying logic of the Tesla Coil indicator and provide a more intuitive way to interpret the indicator.
// The signals should be interpreted as suggestions rather than as a hard-coded set of rules. 
// NOTE: Please do NOT trade off the signals blindly. Always try to use your own intuition for understanding the coils and check for confluence with other indicators before initiating a trade.

indicator(title='Tesla Coil ML [2.2]', overlay=false)

// Chart Settings
series = input(title='Source Series', defval=close, group="Chart Settings", tooltip="Source series to use for Tesla Coil calculations.")
show_ml_signals = input(true, title="Show ML Signals", group="Chart Settings", tooltip="Show ML signals on the chart.")
signal_size = input(2, title="Signal Size", group="Chart Settings", tooltip="Size of the signal dots. 1 = small, 2 = medium, 3 = large.")
show_mesa = input(true, title="Show MESA Baseline", group="Chart Settings", tooltip="Show MESA baseline to provide additional color-coded context for the dominant cycle.")
mesa_size = input(1, title="MESA Line Thickness", group="Chart Settings", tooltip="Thickness of the MESA line. 1 = thin, 2 = medium, 3 = thick.")
scanning_range = input(title='Scanning Range', defval=100, group="Chart Settings", tooltip="Number of bars to scan for a cycle.")

// MESA Spectrum Dominant Cycle 
// Based on previous work by Franklin Moormann and John Ehlers
import Duyck/Matrix_Functions_Lib_JD/1 as matrix
src = series
inp = src
res = input.timeframe(title='Resolution', defval='')
rep = false
bar = true
lowerLength = 12
upperLength = 54
coefLength = 10
a1 = math.exp(-1.414 * math.pi / upperLength)
b1 = 2 * a1 * math.cos(1.414 * math.pi / upperLength)
c2 = b1
c3 = -a1 * a1
c1 = (1 + c2 - c3) / 4
a = math.exp(-1.414 * math.pi / lowerLength)
coef2 = 2 * a * math.cos(1.414 * math.pi / lowerLength)
coef3 = -a * a
coef1 = 1 - coef2 - coef3
_p = array.new_float(500, 0.0)
bb1 = array.new_float(500, 0.0)
bb2 = array.new_float(500, 0.0)
coef = array.new_float(500, 0.0)
coefA = array.new_float(500, 0.0)
hCoef = array.new_float(500, 0.0)
wave = array.new_float(500, 0.0)
nWave = array.new_float(500, 0.0)
cycle = array.new_float(500, 0.0)
coef2A = matrix.init(500, lowerLength, 0.0)
hp = 0.0
hp := bar_index < 4 ? 0 : c1 * (src - 2 * nz(src[1]) + nz(src[2])) + c2 * nz(hp[1]) + c3 * nz(hp[2])
ssf = 0.0
ssf := bar_index < 3 ? hp : coef1 * ((hp + nz(hp[1])) / 2) + coef2 * nz(ssf[1]) + coef3 * nz(ssf[2])
pwr = 0.0
for int i = 0 to upperLength - 1 by 1
    pwr := pwr + math.pow(nz(ssf[i]), 2)
pwr := pwr / upperLength
array.set(bb1, 1, ssf)
array.set(bb2, upperLength - 1, nz(ssf[upperLength - 1]))
for int i = 2 to upperLength - 1 by 1
    array.set(bb1, i, nz(ssf[i - 1]))
    array.set(bb2, i - 1, nz(ssf[i - 1]))
num = 0.0
denom = 0.0
for int i = 1 to upperLength - 1 by 1
    num := num + array.get(bb1, i) * array.get(bb2, i)
    denom := denom + math.pow(array.get(bb1, i), 2) + math.pow(array.get(bb2, i), 2)
array.set(coef, 1, denom != 0 ? 2 * num / denom : 0)
array.set(_p, 1, pwr * (1 - math.pow(array.get(coef, 1), 2)))
for int i = 2 to coefLength by 1
    for int j = 1 to i - 1 by 1
        array.set(coefA, j, array.get(coef, j))
    for int j = 1 to upperLength - i by 1
        array.set(bb1, j, array.get(bb1, j) - (array.get(coefA, i - 1) * array.get(bb2, j)))
        array.set(bb2, j, array.get(bb2, j + 1) - (array.get(coefA, i - 1) * array.get(bb1, j + 1)))
    num1 = 0.0, denom1 = 0.0
    for int j = 1 to upperLength - i by 1
        num1 := num1 + (array.get(bb1, j) * array.get(bb2, j))
        denom1 := denom1 + (math.pow(array.get(bb1, j), 2) + math.pow(array.get(bb2, j), 2))
    array.set(coef, i, denom1 != 0 ? 2 * num1 / denom1 : 0)
    array.set(_p, i, array.get(_p, i - 1) * (1 - math.pow(array.get(coef, i), 2)))
    for int j = 1 to i - 1 by 1
        array.set(coef, j, array.get(coefA, j) - (array.get(coef, i) * array.get(coefA, i - j)))
for int i = 1 to coefLength
    matrix.set(coef2A, i, 1, array.get(coef, i))
    for int j = lowerLength to 2
        matrix.set(coef2A, i, j, matrix.get(coef2A, i, j - 1))
for int i = 1 to coefLength
    array.set(hCoef, i, 0)
    cc = 0.0
    for int j = 1 to lowerLength
        cos = 1 - math.cos(2 * math.pi * j / (lowerLength + 1))
        array.set(hCoef, i, array.get(hCoef, i) + (cos * matrix.get(coef2A, i, j)))
        cc := cc + cos
    array.set(hCoef, i, cc != 0 ? array.get(hCoef, i) / cc : 0)
for int i = lowerLength to upperLength
    denReal = 0.0, denImag = 0.0
    for int j = 1 to coefLength
        denReal := denReal + (array.get(hCoef, j) * math.cos(2 * math.pi * j / i))
        denImag := denImag - (array.get(hCoef, j) * math.sin(2 * math.pi * j / i))
    wav = math.pow(1 - denReal, 2) + math.pow(denImag, 2)
    array.set(wave, i, wav != 0 ? array.get(_p, coefLength) / wav : 0)
maxWave = array.get(wave, lowerLength)
for int i = lowerLength to upperLength
    maxWave := array.get(wave, i) > maxWave ? array.get(wave, i) : maxWave
for int i = lowerLength to upperLength
    array.set(nWave, i, maxWave != 0 ? -array.get(wave, i)/maxWave : 0)
maxCycle = array.get(cycle, lowerLength), domCycle = 0.0
for int i = lowerLength to upperLength
    if array.get(nWave, i) > maxCycle
        maxCycle := array.get(nWave, i)
        domCycle := i
slo = ssf - nz(ssf[1])
sig = slo > 0 ? slo > nz(slo[1]) ? 2 : 1 : slo < 0 ? slo < nz(slo[1]) ? -2 : -1 : 0
strong_buy = ta.crossover(sig, 1)
strong_sell = ta.crossunder(sig, -1)
buy = ta.crossover(sig, 0)
sell = ta.crossunder(sig, 0)
msdcColor = sig > 2 ? color.new(color.green, 55) : sig > 0 ? color.new(color.green, 0) : sig < -2 ? color.new(color.red, 0) : sig < 0 ? color.new(color.red, 50) : color.black
plot(domCycle + 20, title='MESA Cycle', color=show_mesa ? msdcColor : color.new(color.black, 100), linewidth=mesa_size)

// Cycle Settings
mirror = true
purple_cycle_offset = input(40, title="Purple Cycle Offset", group="Cycle Settings", tooltip="Adjusts the overlap between the purple cycle and its mirror.")
blue_cycle_offset = input(40, title="Blue Cycle Offset", group="Cycle Settings", tooltip="Adjusts the overlap between the blue cycle and its mirror.")
white_cycle_offset = input(40, title="White Cycle Offset", group="Cycle Settings", tooltip="Adjusts the overlap between the white cycle and its mirror.")

// Machine Learning Settings
ptype = 'Close'
reso = input.timeframe('', 'Timeframe/Sensitivity', group="Machine Learning Settings", tooltip="Timeframe for Machine Learning Algorithm. Lower values will result in more crossover signals.")
lookback = input.int(3, 'Lookback Window Size [2..n]', minval=2, tooltip='The number of bars to look back.', group="Machine Learning Settings")
nlbk = input.int(2, 'Normalization Lookback [2..240]', minval=2, maxval=240, tooltip='The number of bars to use for normalization.', group="Machine Learning Settings")
lrate = input.float(0.0009, 'Learning Rate [0.0001..0.01]', minval=0.0001, maxval=0.01, step=0.0001, group="Machine Learning Settings", tooltip='The learning rate is the amount of change that the model will make in the direction of the gradient. The higher the learning rate, the more the model will change in the direction of the gradient. The lower the learning rate, the less the model will change in the direction of the gradient.')
iterations = input.int(1000, 'Training Iterations [50..20000]', minval=50, group="Machine Learning Settings", tooltip='The number of iterations is the number of times the model will be trained. The higher the number of iterations, the more accurate the model will be. The lower the number of iterations, the faster the model will be.')
ftype = input.string('None', 'Signals Filter', options=['Volatility', 'Volume', 'Both', 'None'], group="Machine Learning Settings", tooltip='The Signals Filter option allows you to filter signals by either the Volatility, Volume, Both, or None. The Volatility filter will filter signals by the volatility of the asset. The Volume filter will filter signals by the volume of the asset. The Both filter will filter signals by both the volatility and volume of the asset. The "None" filter will not filter signals.')
curves = false
easteregg = true
useprice = true
holding_p = input.int(5, 'Holding Period [1..n]', minval=1, tooltip='The holding period is the number of bars that the model will wait before signaling a trade.', group="Machine Learning Settings")

// The Cosine IFM (Instantaneous Frequency Measurement) - Method #1
// Reference: "MEASURING CYCLES" paper by John Ehlers
getCosIFM(series) => // red
    pi = math.pi
    s2 = 0.0
    s3 = 0.0
    delta1 = 0.0
    instant = 0.0
    ifm = 0.0
    value1 = 0.0
    value2 = 0.0
    value4 = 0.0
    value1 := series - series[7]
    s2 := 0.2 * (value1[1] + value1) * (value1[1] + value1) + 0.8 * nz(s2[1])
    s3 := 0.2 * (value1[1] - value1) * (value1[1] - value1) + 0.8 * nz(s3[1])
    if s2 != 0
        value2 := math.sqrt(s3 / s2)
        value2
    if s3 != 0
        delta1 := 2 * math.atan(value2)
        delta1
    for i = 0 to scanning_range by 1
        value4 += delta1[i]
        if value4 > 2 * pi and instant == 0.0
            instant := i - 1
            instant
    if instant == 0.0
        instant := instant[1]
        instant
    ifm := 0.25 * instant + 0.75 * nz(ifm[1])
    ifm

// Robust Cycle Measurement Function (I-Q Principles) - Method #2
// Reference: unknown
getRCM(series) => // green
    price = series
    alpha = 0.1
    pi = math.pi
    cycle = 0.0
    q1 = 0.0
    i1 = 0.0
    i2 = 0.0
    q2 = 0.0
    delta1 = 0.0
    delta1Phase = 0.0
    medianDelta = 0.0
    dc = 0.0
    instPeriod = 0.0
    period = 0.0
    smooth = (price + 2 * price[1] + 2 * price[2] + price[3]) / 6
    cycle := (1 - 0.5 * alpha) * (1 - 0.5 * alpha) * (smooth - 2 * smooth[1] + smooth[2]) + 2 * (1 - alpha) * nz(cycle[1]) - (1 - alpha) * (1 - alpha) * nz(cycle[2])
    if bar_index < 7
        cycle := (price - 2 * price[1] + price[2]) / 4
        cycle
    q1 := (0.0962 * cycle + 0.5769 * cycle[2] - 0.5769 * cycle[4] - 0.0962 * cycle[6]) * (0.5 + 0.08 * instPeriod[1])
    i1 := cycle[3]
    if q1 != 0 and q1[1] != 0
        delta1Phase := (i1 / q1 - i1[1] / q1[1]) / (1 + i1 * i1[1] / (q1 * q1[1]))
        delta1Phase
    if delta1Phase < 0.1
        delta1Phase := 0.1
        delta1Phase
    if delta1Phase > 1.1
        delta1Phase := 1.1
        delta1Phase
    medianDelta := ta.percentile_nearest_rank(delta1Phase, 5, 50)
    if medianDelta == 0
        dc := 15
        dc
    else
        dc := 6.28318 / medianDelta + 0.5
        dc
    instPeriod := 0.33 * dc + 0.67 * nz(instPeriod[1])
    period := 0.15 * instPeriod + 0.85 * nz(period[1])
    period := math.round(period)
    period

// In-Phase & Quadrature Instantaneous Frequency Measurements (IFM) - Method #2 in the Paper
getIPQ(series) => // blue
    pi = math.pi
    imult = 0.635
    qmult = 0.338
    inphase = 0.0
    quadrature = 0.0
    re = 0.0
    im = 0.0
    inst = 0.0
    delta1 = 0.0
    dominant_cycle = 0.0
    V = 0.0
    P = series - series[7]
    
    inphase := 1.25 * (P[4] - imult * P[2]) + imult * nz(inphase[3])
    quadrature := P[2] - qmult * P + qmult * nz(quadrature[2])
    re := 0.2 * (inphase * inphase[1] + quadrature * quadrature[1]) + 0.8 * nz(re[1])
    im := 0.2 * (inphase * quadrature[1] - inphase[1] * quadrature) + 0.8 * nz(im[1])
    if re != 0.0
        delta1 := math.atan(im / re)
        delta1
    for i = 0 to 100 by 1
        V += delta1[i]
        if V > 2 * pi and inst == 0.0
            inst := i
            inst
    if inst == 0.0
        inst := nz(inst[1])
        inst
    dominant_cycle := 0.25 * inst + 0.75 * nz(dominant_cycle[1])

// Core ML Logic
// Reference and further reading: "Machine Learning for Algorithmic Trading" by Stefan Jansen
// Also influenced by previous pinescript implementations of Jansen's algorithms by @capcissmo
volumeBreak(thres) =>
    rsivol = ta.rsi(volume, 14)
    osc = ta.hma(rsivol, 10)
    osc > thres

volatilityBreak(volmin, volmax) =>
    ta.atr(volmin) > ta.atr(volmax)

dot(v, w, p) => // dot product
    math.sum(v * w, p)

sigmoid(z) =>
    1.0 / (1.0 + math.exp(-z))

logistic_regression(X, Y, p, lr, iterations) =>
    w = 0.0
    loss = 0.0
    for i = 1 to iterations by 1
        hypothesis = sigmoid(dot(X, 0.0, p))
        loss := -1.0 / p * dot(dot(Y, math.log(hypothesis) + 1.0 - Y, p), math.log(1.0 - hypothesis), p)
        gradient = 1.0 / p * dot(X, hypothesis - Y, p)
        w -= lr * gradient
        w
    [loss, sigmoid(dot(X, w, p))]

minimax(ds, p, min, max) => 
    hi = ta.highest(ds, p)
    lo = ta.lowest(ds, p)
    (max - min) * (ds - lo) / (hi - lo) + min

get_log_reg_predictions() =>
    var BUY = 1
    var SELL = -1
    var HOLD = 0
    var signal = HOLD
    var hp_counter = 0
    ds = ptype == 'Open' ? open : ptype == 'High' ? high : ptype == 'Low' ? low : ptype == 'Close' ? close : ptype == 'HL2' ? (high + low) / 2 : ptype == 'OC2' ? (open + close) / 2 : ptype == 'OHL3' ? (open + high + low) / 3 : ptype == 'HLC3' ? (high + low + close) / 3 : (open + high + low + close) / 4
    base_ds = request.security('', reso, ds[barstate.ishistory ? 0 : 1])
    synth_ds = math.log(math.abs(math.pow(base_ds, 2) - 1) + .5) 
    base = easteregg ? time : base_ds
    synth = easteregg ? base_ds : synth_ds
    [loss, prediction] = logistic_regression(base, synth, lookback, lrate, iterations)
    scaled_loss = minimax(loss, nlbk, ta.lowest(base_ds, nlbk), ta.highest(base_ds, nlbk))
    scaled_prediction = minimax(prediction, nlbk, ta.lowest(base_ds, nlbk), ta.highest(base_ds, nlbk))
    filter = ftype == 'Volatility' ? volatilityBreak(1, 10) : ftype == 'Volume' ? volumeBreak(49) : ftype == 'Both' ? volatilityBreak(1, 10) and volumeBreak(49) : true
    signal := useprice ? base_ds < scaled_loss ? SELL : base_ds > scaled_loss ? BUY : nz(signal[1]) : ta.crossunder(scaled_loss, scaled_prediction) ? SELL : ta.crossover(scaled_loss, scaled_prediction) and filter ? BUY : nz(signal[1])
    changed = ta.change(signal) 
    hp_counter := changed ? 0 : hp_counter + 1
    hp_filter = hp_counter < 3
    startLongTrade = changed and signal == BUY
    startShortTrade = changed and signal == SELL
    [startLongTrade, startShortTrade]

[startLongTrade, startShortTrade] = get_log_reg_predictions()

// Get all cycles and their average
ipq = getIPQ(series) // (same as original called "len")
cifm = getCosIFM(series) // (originally len1 * 3)
rcm = getRCM(series) // (not graphed, included in average)

average = math.avg(cifm * 3, rcm, ipq)
scaled_cifm = cifm * 3 // scaled

rcm_mirror = rcm * -1 + blue_cycle_offset
ipq_mirror = ipq * -1 + blue_cycle_offset
average_mirror = average * -1 + purple_cycle_offset
scaled_cifm_mirror = scaled_cifm * -1 + white_cycle_offset

w = scaled_cifm
w_ = scaled_cifm_mirror
b = ipq
b_ = ipq_mirror
p = average
p_ = average_mirror

// Same Color Crosses
ww_1 = ta.crossover(w, w_)
ww_2 = ta.crossunder(w, w_)
white_cross = ww_1 or ww_2

bb_1 = ta.crossover(b, b_)
bb_2 = ta.crossunder(b, b_)
blue_cross = bb_1 or bb_2

pp_1 = ta.crossover(p, p_)
pp_2 = ta.crossunder(p, p_)
purple_cross = pp_1 or pp_2

// White-Blue Crosses
wb1 = ta.crossover(w, b)
wb2 = ta.crossunder(w, b)
wb_1 = ta.crossover(w, b_)
wb_2 = ta.crossunder(w, b_)
w_b_1 = ta.crossover(w_, b_)
w_b_2 = ta.crossunder(w_, b_)
w_b1 = ta.crossover(w_, b)
w_b2 = ta.crossunder(w_, b)
wb_cross = wb1 or wb2 or wb_1 or wb_2 or w_b_1 or w_b_2 or w_b1 or w_b2

// White-Purple Crosses
wp1 = ta.crossover(w, p)
wp2 = ta.crossunder(w, p)
wp_1 = ta.crossover(w, p_)
wp_2 = ta.crossunder(w, p_)
w_p_1 = ta.crossover(w_, p_)
w_p_2 = ta.crossunder(w_, p_)
w_p1 = ta.crossover(w_, p)
w_p2 = ta.crossunder(w_, p)
wp_cross = wp1 or wp2 or wp_1 or wp_2 or w_p_1 or w_p_2 or w_p1 or w_p2

// Blue-Purple Crosses
bp1 = ta.crossover(b, p)
bp2 = ta.crossunder(b, p)
bp_1 = ta.crossover(b, p_)
bp_2 = ta.crossunder(b, p_)
b_p_1 = ta.crossover(b_, p_)
b_p_2 = ta.crossunder(b_, p_)
b_p1 = ta.crossover(b_, p)
b_p2 = ta.crossunder(b_, p)
bp_cross = bp1 or bp2 or bp_1 or bp_2 or b_p_1 or b_p_2 or b_p1 or b_p2

// Combination Crosses by Category
any_self_cross = white_cross or blue_cross or purple_cross
any_different_cross = wb_cross or wp_cross or bp_cross
any_cross = any_different_cross or any_self_cross
blue_cross_all = blue_cross or bp_cross or wb_cross
white_cross_all = white_cross or wp_cross or wb_cross
purple_cross_all = purple_cross or wp_cross or bp_cross

// Transparency Definitions
tr_fig = 20
tr_white = w > w_ ? 50 : 0
tr_blue = b <= b_ ? 85 : 45
tr_purple = p > p_ ? 70 : 0

// Color Definitions
cola = color.new(#2196f3, tr_blue)
colb = color.new(#bbd9fb, tr_white)
colc = color.new(#9575cd, tr_purple)

// Main Cycles
plot(b, "Blue", color=cola)
plot(w, "White", color=colb)
plot(p, "Purple", color=colc)

// Mirror Cycles
plot(b_, "Blue Mirror", color=cola)
plot(w_, "White Mirror", color=colb)
plot(p_, "Purple Mirror", color=colc)

buy_condition = startLongTrade
sell_condition = startShortTrade

green_signal = show_ml_signals ? color.new(color.green, 60) : color.new(color.green, 100)
red_signal = show_ml_signals ? color.new(color.red, 60) : color.new(color.red, 100)

// Purple Crosses
plot(purple_cross_all and buy_condition ? p : na, title="Purple Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(purple_cross_all and buy_condition ? p : na, title="Purple Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(purple_cross_all and sell_condition ? p : na, title="Purple Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(purple_cross_all and sell_condition ? p : na, title="Purple Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(purple_cross_all and buy_condition ? p_ : na, title="Purple Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(purple_cross_all and buy_condition ? p_ : na, title="Purple Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(purple_cross_all and sell_condition ? p_ : na, title="Purple Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(purple_cross_all and sell_condition ? p_ : na, title="Purple Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)

// Blue Crosses
plot(blue_cross_all and buy_condition ? b : na, title="Blue Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(blue_cross_all and buy_condition ? b : na, title="Blue Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(blue_cross_all and sell_condition ? b : na, title="Blue Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(blue_cross_all and sell_condition ? b : na, title="Blue Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(blue_cross_all and buy_condition ? b_ : na, title="Blue Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(blue_cross_all and buy_condition ? b_ : na, title="Blue Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(blue_cross_all and sell_condition ? b_ : na, title="Blue Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(blue_cross_all and sell_condition ? b_ : na, title="Blue Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)

// White Crosses
plot(white_cross_all and buy_condition ? w : na, title="White Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(white_cross_all and buy_condition ? w : na, title="White Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(white_cross_all and sell_condition ? w : na, title="White Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(white_cross_all and sell_condition ? w : na, title="White Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(white_cross_all and buy_condition ? w_ : na, title="White Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(white_cross_all and buy_condition ? w_ : na, title="White Mirror Cross Buy", style=plot.style_circles, linewidth=signal_size, color=green_signal)
plot(white_cross_all and sell_condition ? w_ : na, title="White Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)
plot(white_cross_all and sell_condition ? w_ : na, title="White Mirror Cross Sell", style=plot.style_circles, linewidth=signal_size, color=red_signal)

// Backtesting as a strategy:
// longCondition = white_cross
// strategy.entry("Backtest", strategy.long, when=white_cross)
// opened_order = strategy.position_size[0] != strategy.position_size[1] and strategy.position_size[0] != 0
// bars = ta.barssince(opened_order) + 1
// strategy.close_all(when=(bars==2))