mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-19 19:18:05 +00:00
pine files
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Huber Loss (HUBER)", "HUBER")
|
||||
|
||||
//@function Calculates Huber Loss between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/huber.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@param delta Threshold that determines switch between MSE and MAE behavior
|
||||
//@returns Huber loss value averaged over the specified period using SMA
|
||||
huber(series float source1, series float source2, simple int period, simple float delta = 1.345) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
error = source1 - source2
|
||||
huber_error = math.abs(error) <= delta ? 0.5 * math.pow(error, 2) : delta * math.abs(error) - 0.5 * math.pow(delta, 2)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(huber_error)
|
||||
sum := sum + huber_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, huber_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : huber_error
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_delta = input.float(1.345, "Delta", minval=0.1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = huber(i_source1, i_source2, i_period, i_delta)
|
||||
|
||||
// Plot
|
||||
plot(error, "Huber", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,38 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Arctangent Absolute Percentage Error", "MAAPE", overlay=false, format=format.percent)
|
||||
|
||||
//@function Calculates Mean Arctangent Absolute Percentage Error
|
||||
//@doc Uses arctangent to bound error between 0 and π/2, robust to outliers.
|
||||
//@doc Handles zero actual values gracefully (approaches π/2).
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for averaging
|
||||
//@returns MAAPE value (0 to ~1.5708)
|
||||
maape(series float actual, series float predicted, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute arctangent percentage error for current bar
|
||||
float absActual = math.abs(nz(actual, 0.0))
|
||||
float absError = math.abs(nz(actual, 0.0) - nz(predicted, 0.0))
|
||||
float atanError = absActual > epsilon ? math.atan(absError / absActual) : math.pi / 2.0
|
||||
|
||||
// Rolling mean of arctangent errors
|
||||
float result = ta.sma(atanError, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
maape_value = maape(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(maape_value, "MAAPE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
hline(math.pi / 2.0, "Max (π/2)", color=color.red, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute Error (MAE)", "MAE")
|
||||
|
||||
//@function Calculates Mean Absolute Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mae.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MAE value averaged over the specified period using SMA
|
||||
mae(series float source1, series float source2, simple int period) =>
|
||||
absolute_error = math.abs(source1 - source2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(absolute_error)
|
||||
sum := sum + absolute_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, absolute_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : absolute_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mae(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MAE", color.new(color.blue, 60, color=color.yellow, linewidth=2), linewidth = 2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute %Deviation (MAPD)", "MAPD")
|
||||
|
||||
//@function Calculates Mean Absolute Percentage Deviation between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mapd.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MAPD value averaged over the specified period using SMA
|
||||
mapd(series float source1, series float source2, simple int period) =>
|
||||
percentage_error = 100 * math.abs((source1 - source2) / source2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(percentage_error)
|
||||
sum := sum + percentage_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, percentage_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : percentage_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mapd(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MAPD", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute %Error (MAPE)", "MAPE")
|
||||
|
||||
//@function Calculates Mean Absolute Percentage Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mape.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MAPE value averaged over the specified period using SMA
|
||||
mape(series float source1, series float source2, simple int period) =>
|
||||
percentage_error = 100 * math.abs((source1 - source2) / source1)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(percentage_error)
|
||||
sum := sum + percentage_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, percentage_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : percentage_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mape(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MAPE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,56 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute Scaled Error (MASE)", "MASE")
|
||||
|
||||
//@function Calculates Mean Absolute Scaled Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mase.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MASE value averaged over the specified period using SMA
|
||||
mase(series float source1, series float source2, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
error = source1 - source2
|
||||
abs_error = math.abs(error)
|
||||
var float scale = na
|
||||
if na(scale)
|
||||
sum = 0.0
|
||||
count = 0
|
||||
for i = 1 to p
|
||||
if not na(source1[i]) and not na(source1[i-1])
|
||||
sum += math.abs(source1[i] - source1[i-1])
|
||||
count += 1
|
||||
scale := count > 0 ? sum / count : 1.0
|
||||
scaled_error = abs_error / (scale == 0 ? 1.0 : scale)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(scaled_error)
|
||||
sum := sum + scaled_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, scaled_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : scaled_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mase(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MASE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,33 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Median Absolute Error", "MdAE", overlay=false)
|
||||
|
||||
//@function Calculates Median Absolute Error
|
||||
//@doc Median of absolute errors, robust to outliers (50% breakdown point).
|
||||
//@doc Same units as original data, less sensitive to extreme errors than MAE.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for median calculation
|
||||
//@returns MdAE value
|
||||
mdae(series float actual, series float predicted, simple int length) =>
|
||||
// Compute absolute error for current bar
|
||||
float absError = math.abs(nz(actual, 0.0) - nz(predicted, 0.0))
|
||||
|
||||
// Use ta.median for rolling median of absolute errors
|
||||
float result = ta.median(absError, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
mdae_value = mdae(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(mdae_value, "MdAE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,37 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Median Absolute Percentage Error", "MdAPE", overlay=false, format=format.percent)
|
||||
|
||||
//@function Calculates Median Absolute Percentage Error
|
||||
//@doc Median of absolute percentage errors, robust to outliers.
|
||||
//@doc Scale-independent (expressed as percentage), handles zero actual with epsilon.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for median calculation
|
||||
//@returns MdAPE value as percentage
|
||||
mdape(series float actual, series float predicted, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute absolute percentage error for current bar
|
||||
float absActual = math.abs(nz(actual, 1.0))
|
||||
float absError = math.abs(nz(actual, 0.0) - nz(predicted, 0.0))
|
||||
float pctError = absActual > epsilon ? (absError / absActual) * 100.0 : 0.0
|
||||
|
||||
// Use ta.median for rolling median of percentage errors
|
||||
float result = ta.median(pctError, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
mdape_value = mdape(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(mdape_value, "MdAPE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Error (ME)", "ME")
|
||||
|
||||
//@function Calculates Mean Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/me.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns ME value averaged over the specified period using SMA
|
||||
me(series float source1, series float source2, simple int period) =>
|
||||
error = source1 - source2
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(error)
|
||||
sum := sum + error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = me(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "ME", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean %Error (MPE)", "MPE")
|
||||
|
||||
//@function Calculates Mean Percentage Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mpe.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MPE value averaged over the specified period using SMA
|
||||
mpe(series float source1, series float source2, simple int period) =>
|
||||
percentage_error = 100 * ((source1 - source2) / source1)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(percentage_error)
|
||||
sum := sum + percentage_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, percentage_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : percentage_error
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mpe(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MPE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,38 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Relative Absolute Error", "MRAE", overlay=false)
|
||||
|
||||
//@function Calculates Mean Relative Absolute Error
|
||||
//@doc Average relative absolute error, normalized by actual value.
|
||||
//@doc Similar to MAPE but expressed as ratio (0-1) instead of percentage (0-100%).
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for averaging
|
||||
//@returns MRAE value (0 = perfect, 1 = 100% error)
|
||||
mrae(series float actual, series float predicted, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute relative absolute error for current bar
|
||||
float absActual = math.abs(nz(actual, 1.0))
|
||||
float absError = math.abs(nz(actual, 0.0) - nz(predicted, 0.0))
|
||||
float relError = absActual > epsilon ? absError / absActual : 0.0
|
||||
|
||||
// Rolling mean of relative errors
|
||||
float result = ta.sma(relError, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
mrae_value = mrae(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(mrae_value, "MRAE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Perfect", color=color.green, linestyle=hline.style_dotted)
|
||||
hline(1, "100% Error", color=color.red, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,46 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Squared Error (MSE)", "MSE")
|
||||
|
||||
|
||||
//@function Calculates Mean Squared Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/mse.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MSE value averaged over the specified period using SMA
|
||||
mse(series float source1, series float source2, simple int period) =>
|
||||
squared_error = math.pow(source1 - source2, 2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(squared_error)
|
||||
sum := sum + squared_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, squared_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : squared_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = mse(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MSE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Squared Logarithmic Error (MSLE)", "MSLE")
|
||||
|
||||
//@function Calculates Mean Squared Logarithmic Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/msle.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns MSLE value averaged over the specified period using SMA
|
||||
msle(series float source1, series float source2, simple int period) =>
|
||||
log_error = math.pow(math.log(1 + source1) - math.log(1 + source2), 2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(log_error)
|
||||
sum := sum + log_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, log_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : log_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = msle(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "MSLE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,41 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Pseudo-Huber Loss", "PseudoHuber", overlay=false)
|
||||
|
||||
//@function Calculates Pseudo-Huber Loss (Charbonnier Loss)
|
||||
//@doc Smooth approximation to Huber loss, differentiable everywhere.
|
||||
//@doc Approximates L2 for small errors, L1 for large errors.
|
||||
//@doc δ (delta) controls the transition point between quadratic and linear behavior.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for averaging
|
||||
//@param delta Scale parameter controlling transition smoothness (default 1.0)
|
||||
//@returns Mean Pseudo-Huber loss over the window
|
||||
pseudohuber(series float actual, series float predicted, simple int length, simple float delta = 1.0) =>
|
||||
float deltaSquared = delta * delta
|
||||
|
||||
// Compute Pseudo-Huber loss for current bar: δ² * (√(1 + (error/δ)²) - 1)
|
||||
float diff = nz(actual, 0.0) - nz(predicted, 0.0)
|
||||
float ratio = diff / delta
|
||||
float sqrtTerm = math.sqrt(1.0 + ratio * ratio)
|
||||
float loss = deltaSquared * (sqrtTerm - 1.0)
|
||||
|
||||
// Rolling mean of losses
|
||||
float result = ta.sma(loss, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_delta = input.float(1.0, "Delta (transition scale)", minval=0.001, step=0.1, tooltip="Controls transition between quadratic and linear behavior")
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
pseudohuber_value = pseudohuber(i_actual, i_predicted, i_length, i_delta)
|
||||
|
||||
// Plot
|
||||
plot(pseudohuber_value, "Pseudo-Huber", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,36 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Quantile Loss (Pinball Loss)", "QuantileLoss", overlay=false)
|
||||
|
||||
//@function Calculates Quantile Loss (Pinball Loss)
|
||||
//@doc Used for quantile regression, asymmetrically penalizes over/under-predictions.
|
||||
//@doc q=0.5 gives MAE; q>0.5 penalizes under-prediction more; q<0.5 penalizes over-prediction more.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for averaging
|
||||
//@param quantile Quantile value between 0 and 1 (default 0.5)
|
||||
//@returns Mean quantile loss over the window
|
||||
quantile_loss(series float actual, series float predicted, simple int length, simple float quantile = 0.5) =>
|
||||
// Compute quantile loss for current bar
|
||||
float diff = nz(actual, 0.0) - nz(predicted, 0.0)
|
||||
float loss = diff >= 0 ? quantile * diff : (quantile - 1.0) * diff
|
||||
|
||||
// Rolling mean of losses
|
||||
float result = ta.sma(loss, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_quantile = input.float(0.5, "Quantile", minval=0.01, maxval=0.99, step=0.05, tooltip="0.5=median (MAE), >0.5=penalize under-prediction, <0.5=penalize over-prediction")
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
quantile_value = quantile_loss(i_actual, i_predicted, i_length, i_quantile)
|
||||
|
||||
// Plot
|
||||
plot(quantile_value, "Quantile Loss", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,75 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Relative Absolute Error (RAE)", "RAE")
|
||||
|
||||
//@function Calculates Relative Absolute Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/rae.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns RAE value averaged over the specified period using SMA
|
||||
rae(series float source1, series float source2, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float sum_source1 = 0.0
|
||||
var float[] buffer_source1 = array.new_float(p, na)
|
||||
var int head_source1 = 0
|
||||
var int valid_count_source1 = 0
|
||||
float oldest_source1 = array.get(buffer_source1, head_source1)
|
||||
if not na(oldest_source1)
|
||||
sum_source1 := sum_source1 - oldest_source1
|
||||
valid_count_source1 := valid_count_source1 - 1
|
||||
if not na(source1)
|
||||
sum_source1 := sum_source1 + source1
|
||||
valid_count_source1 := valid_count_source1 + 1
|
||||
array.set(buffer_source1, head_source1, source1)
|
||||
head_source1 := (head_source1 + 1) % p
|
||||
float mean_source1 = valid_count_source1 > 0 ? sum_source1 / valid_count_source1 : source1
|
||||
float abs_error = math.abs(source1 - source2)
|
||||
float abs_baseline_error = math.abs(source1 - mean_source1)
|
||||
var float sum_abs_error = 0.0
|
||||
var float[] buffer_abs_error = array.new_float(p, na)
|
||||
var int head_abs_error = 0
|
||||
var int valid_count_abs_error = 0
|
||||
float oldest_abs_error = array.get(buffer_abs_error, head_abs_error)
|
||||
if not na(oldest_abs_error)
|
||||
sum_abs_error := sum_abs_error - oldest_abs_error
|
||||
valid_count_abs_error := valid_count_abs_error - 1
|
||||
if not na(abs_error)
|
||||
sum_abs_error := sum_abs_error + abs_error
|
||||
valid_count_abs_error := valid_count_abs_error + 1
|
||||
array.set(buffer_abs_error, head_abs_error, abs_error)
|
||||
head_abs_error := (head_abs_error + 1) % p
|
||||
var float sum_baseline_error = 0.0
|
||||
var float[] buffer_baseline_error = array.new_float(p, na)
|
||||
var int head_baseline_error = 0
|
||||
var int valid_count_baseline_error = 0
|
||||
float oldest_baseline_error = array.get(buffer_baseline_error, head_baseline_error)
|
||||
if not na(oldest_baseline_error)
|
||||
sum_baseline_error := sum_baseline_error - oldest_baseline_error
|
||||
valid_count_baseline_error := valid_count_baseline_error - 1
|
||||
if not na(abs_baseline_error)
|
||||
sum_baseline_error := sum_baseline_error + abs_baseline_error
|
||||
valid_count_baseline_error := valid_count_baseline_error + 1
|
||||
array.set(buffer_baseline_error, head_baseline_error, abs_baseline_error)
|
||||
head_baseline_error := (head_baseline_error + 1) % p
|
||||
float total_abs_error = valid_count_abs_error > 0 ? sum_abs_error : abs_error
|
||||
float total_baseline_error = valid_count_baseline_error > 0 ? sum_baseline_error : abs_baseline_error
|
||||
total_baseline_error != 0 ? total_abs_error / total_baseline_error : 1.0
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = rae(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "RAE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Root Mean Squared Error (RMSE)", "RMSE")
|
||||
|
||||
//@function Calculates Root Mean Squared Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/rmse.md
|
||||
//@param source1 First series to compare
|
||||
//@param source2 Second series to compare
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns RMSE value averaged over the specified period using SMA
|
||||
rmse(series float source1, series float source2, simple int period) =>
|
||||
squared_error = math.pow(source1 - source2, 2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(squared_error)
|
||||
sum := sum + squared_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, squared_error)
|
||||
head := (head + 1) % p
|
||||
float mse = valid_count > 0 ? sum / valid_count : squared_error
|
||||
math.sqrt(mse)
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = rmse(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "RMSE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Root Mean Squared Logarithmic Error (RMSLE)", "RMSLE")
|
||||
|
||||
//@function Calculates Root Mean Squared Logarithmic Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/rmsle.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns RMSLE value averaged over the specified period using SMA
|
||||
rmsle(series float source1, series float source2, simple int period) =>
|
||||
log_squared_error = math.pow(math.log(1 + source1) - math.log(1 + source2), 2)
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(log_squared_error)
|
||||
sum := sum + log_squared_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, log_squared_error)
|
||||
head := (head + 1) % p
|
||||
float msle = valid_count > 0 ? sum / valid_count : log_squared_error
|
||||
math.sqrt(msle)
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = rmsle(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "RMSLE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,74 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Relative Squared Error (RSE)", "RSE")
|
||||
|
||||
//@function Calculates Relative Squared Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/rse.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns RSE value averaged over the specified period using SMA
|
||||
rse(series float source1, series float source2, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float sum_source1 = 0.0
|
||||
var float[] buffer_source1 = array.new_float(p, na)
|
||||
var int head_source1 = 0
|
||||
var int valid_count_source1 = 0
|
||||
float oldest_source1 = array.get(buffer_source1, head_source1)
|
||||
if not na(oldest_source1)
|
||||
sum_source1 := sum_source1 - oldest_source1
|
||||
valid_count_source1 := valid_count_source1 - 1
|
||||
if not na(source1)
|
||||
sum_source1 := sum_source1 + source1
|
||||
valid_count_source1 := valid_count_source1 + 1
|
||||
array.set(buffer_source1, head_source1, source1)
|
||||
head_source1 := (head_source1 + 1) % p
|
||||
float mean_source1 = valid_count_source1 > 0 ? sum_source1 / valid_count_source1 : source1
|
||||
float squared_error = math.pow(source1 - source2, 2)
|
||||
float squared_baseline_error = math.pow(source1 - mean_source1, 2)
|
||||
var float sum_squared_error = 0.0
|
||||
var float[] buffer_squared_error = array.new_float(p, na)
|
||||
var int head_squared_error = 0
|
||||
var int valid_count_squared_error = 0
|
||||
float oldest_squared_error = array.get(buffer_squared_error, head_squared_error)
|
||||
if not na(oldest_squared_error)
|
||||
sum_squared_error := sum_squared_error - oldest_squared_error
|
||||
valid_count_squared_error := valid_count_squared_error - 1
|
||||
if not na(squared_error)
|
||||
sum_squared_error := sum_squared_error + squared_error
|
||||
valid_count_squared_error := valid_count_squared_error + 1
|
||||
array.set(buffer_squared_error, head_squared_error, squared_error)
|
||||
head_squared_error := (head_squared_error + 1) % p
|
||||
var float sum_baseline_error = 0.0
|
||||
var float[] buffer_baseline_error = array.new_float(p, na)
|
||||
var int head_baseline_error = 0
|
||||
var int valid_count_baseline_error = 0
|
||||
float oldest_baseline_error = array.get(buffer_baseline_error, head_baseline_error)
|
||||
if not na(oldest_baseline_error)
|
||||
sum_baseline_error := sum_baseline_error - oldest_baseline_error
|
||||
valid_count_baseline_error := valid_count_baseline_error - 1
|
||||
if not na(squared_baseline_error)
|
||||
sum_baseline_error := sum_baseline_error + squared_baseline_error
|
||||
valid_count_baseline_error := valid_count_baseline_error + 1
|
||||
array.set(buffer_baseline_error, head_baseline_error, squared_baseline_error)
|
||||
head_baseline_error := (head_baseline_error + 1) % p
|
||||
float total_squared_error = valid_count_squared_error > 0 ? sum_squared_error : squared_error
|
||||
float total_baseline_error = valid_count_baseline_error > 0 ? sum_baseline_error : squared_baseline_error
|
||||
total_baseline_error != 0 ? total_squared_error / total_baseline_error : 1.0
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = rse(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "RSE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,75 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("R² Coefficient of Determination (RSQUARED)", "RSQUARED")
|
||||
|
||||
//@function Calculates the R-squared (Coefficient of Determination) between two sources
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/rsquared.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for averaging
|
||||
//@returns R-squared value averaging over the specified period
|
||||
rsquared(series float source1, series float source2, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float sum_source1 = 0.0
|
||||
var float[] buffer_source1 = array.new_float(p, na)
|
||||
var int head_source1 = 0
|
||||
var int valid_count_source1 = 0
|
||||
float oldest_source1 = array.get(buffer_source1, head_source1)
|
||||
if not na(oldest_source1)
|
||||
sum_source1 := sum_source1 - oldest_source1
|
||||
valid_count_source1 := valid_count_source1 - 1
|
||||
if not na(source1)
|
||||
sum_source1 := sum_source1 + source1
|
||||
valid_count_source1 := valid_count_source1 + 1
|
||||
array.set(buffer_source1, head_source1, source1)
|
||||
head_source1 := (head_source1 + 1) % p
|
||||
float mean_source1 = valid_count_source1 > 0 ? sum_source1 / valid_count_source1 : source1
|
||||
float squared_residual = math.pow(source1 - source2, 2)
|
||||
float total_ss = math.pow(source1 - mean_source1, 2)
|
||||
var float sum_squared_residual = 0.0
|
||||
var float[] buffer_squared_residual = array.new_float(p, na)
|
||||
var int head_squared_residual = 0
|
||||
var int valid_count_squared_residual = 0
|
||||
float oldest_squared_residual = array.get(buffer_squared_residual, head_squared_residual)
|
||||
if not na(oldest_squared_residual)
|
||||
sum_squared_residual := sum_squared_residual - oldest_squared_residual
|
||||
valid_count_squared_residual := valid_count_squared_residual - 1
|
||||
if not na(squared_residual)
|
||||
sum_squared_residual := sum_squared_residual + squared_residual
|
||||
valid_count_squared_residual := valid_count_squared_residual + 1
|
||||
array.set(buffer_squared_residual, head_squared_residual, squared_residual)
|
||||
head_squared_residual := (head_squared_residual + 1) % p
|
||||
var float sum_total_ss = 0.0
|
||||
var float[] buffer_total_ss = array.new_float(p, na)
|
||||
var int head_total_ss = 0
|
||||
var int valid_count_total_ss = 0
|
||||
float oldest_total_ss = array.get(buffer_total_ss, head_total_ss)
|
||||
if not na(oldest_total_ss)
|
||||
sum_total_ss := sum_total_ss - oldest_total_ss
|
||||
valid_count_total_ss := valid_count_total_ss - 1
|
||||
if not na(total_ss)
|
||||
sum_total_ss := sum_total_ss + total_ss
|
||||
valid_count_total_ss := valid_count_total_ss + 1
|
||||
array.set(buffer_total_ss, head_total_ss, total_ss)
|
||||
head_total_ss := (head_total_ss + 1) % p
|
||||
float rss = valid_count_squared_residual > 0 ? sum_squared_residual : squared_residual
|
||||
float tss = valid_count_total_ss > 0 ? sum_total_ss : total_ss
|
||||
tss != 0 ? 1 - (rss / tss) : 1.0
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
score = rsquared(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(score, "R²", color.new(color.red, 60, color=color.yellow, linewidth=2), linewidth = 2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color.new(color.yellow, 0, linewidth=2), linewidth = 1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,48 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Symmetric Mean Absolute %Error (SMAPE)", "SMAPE")
|
||||
|
||||
//@function Calculates Symmetric Mean Absolute Percentage Error between two sources using SMA for averaging
|
||||
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/errors/smape.md
|
||||
//@param source1 First series to compare (actual)
|
||||
//@param source2 Second series to compare (predicted)
|
||||
//@param period Lookback period for error averaging
|
||||
//@returns SMAPE value averaged over the specified period using SMA (in percentage)
|
||||
smape(series float source1, series float source2, simple int period) =>
|
||||
// Calculate symmetric absolute percentage error (scaled to 100%)
|
||||
abs_diff = math.abs(source1 - source2)
|
||||
sum_abs = math.abs(source1) + math.abs(source2)
|
||||
symmetric_error = sum_abs != 0 ? 200 * abs_diff / sum_abs : 0
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int p = math.min(math.max(1, period), 4000)
|
||||
var float[] buffer = array.new_float(p, na)
|
||||
var int head = 0
|
||||
var float sum = 0.0
|
||||
var int valid_count = 0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum := sum - oldest
|
||||
valid_count := valid_count - 1
|
||||
if not na(symmetric_error)
|
||||
sum := sum + symmetric_error
|
||||
valid_count := valid_count + 1
|
||||
array.set(buffer, head, symmetric_error)
|
||||
head := (head + 1) % p
|
||||
valid_count > 0 ? sum / valid_count : symmetric_error
|
||||
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source1 = input.source(close, "Source")
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
i_source2 = ta.ema(i_source1, i_period)
|
||||
|
||||
// Calculation
|
||||
error = smape(i_source1, i_source2, i_period)
|
||||
|
||||
// Plot
|
||||
plot(error, "SMAPE", color=color.new(color.red, 60), linewidth=2, style = plot.style_area)
|
||||
plot(i_source2, "EMA", color=color.yellow, linewidth=1, style = plot.style_line, force_overlay = true)
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Theil's U Statistic", "TheilU", overlay=false)
|
||||
|
||||
//@function Calculates Theil's U Statistic (U1)
|
||||
//@doc Relative forecast accuracy measure, normalized RMSE.
|
||||
//@doc U=0: perfect; U=1: naive forecast; U>1: worse than naive.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for calculation
|
||||
//@returns Theil's U value (0 to 1+ range)
|
||||
theil_u(series float actual, series float predicted, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute squared values for current bar
|
||||
float error = nz(predicted, 0.0) - nz(actual, 0.0)
|
||||
float sqError = error * error
|
||||
float sqActual = nz(actual, 0.0) * nz(actual, 0.0)
|
||||
float sqPred = nz(predicted, 0.0) * nz(predicted, 0.0)
|
||||
|
||||
// Rolling sums
|
||||
float sumSqError = ta.sum(sqError, length)
|
||||
float sumSqActual = ta.sum(sqActual, length)
|
||||
float sumSqPred = ta.sum(sqPred, length)
|
||||
|
||||
// TheilU = √(Σ(pred-act)²) / √(Σact² + Σpred²)
|
||||
float denom = math.sqrt(sumSqActual + sumSqPred)
|
||||
float result = denom > epsilon ? math.sqrt(sumSqError) / denom : 0.0
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
theilu_value = theil_u(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(theilu_value, "Theil's U", color=color.yellow, linewidth=2)
|
||||
hline(0, "Perfect", color=color.green, linestyle=hline.style_dotted)
|
||||
hline(1, "Naive", color=color.red, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,49 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Tukey's Biweight Loss", "TukeyBiweight", overlay=false)
|
||||
|
||||
//@function Calculates Tukey's Biweight (Bisquare) Loss
|
||||
//@doc Robust loss that completely rejects outliers beyond threshold c.
|
||||
//@doc ρ(x) = (c²/6) * (1 - (1 - (x/c)²)³) for |x| ≤ c; ρ(x) = c²/6 for |x| > c
|
||||
//@doc Common c values: 4.685 (95% efficiency), 6.0 (more permissive)
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for averaging
|
||||
//@param c Threshold for outlier rejection (default 4.685)
|
||||
//@returns Mean Tukey biweight loss over the window
|
||||
tukey_biweight(series float actual, series float predicted, simple int length, simple float c = 4.685) =>
|
||||
float cSquaredOver6 = (c * c) / 6.0
|
||||
|
||||
// Compute Tukey biweight loss for current bar
|
||||
float error = nz(actual, 0.0) - nz(predicted, 0.0)
|
||||
float absError = math.abs(error)
|
||||
|
||||
float loss = 0.0
|
||||
if absError > c
|
||||
loss := cSquaredOver6
|
||||
else
|
||||
float ratio = error / c
|
||||
float ratioSq = ratio * ratio
|
||||
float oneMinusRatioSq = 1.0 - ratioSq
|
||||
float cubed = oneMinusRatioSq * oneMinusRatioSq * oneMinusRatioSq
|
||||
loss := cSquaredOver6 * (1.0 - cubed)
|
||||
|
||||
// Rolling mean of losses
|
||||
float result = ta.sma(loss, length)
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_c = input.float(4.685, "Threshold c", minval=0.1, step=0.1, tooltip="4.685=95% efficiency for normal; 6.0=more permissive")
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
tukey_value = tukey_biweight(i_actual, i_predicted, i_length, i_c)
|
||||
|
||||
// Plot
|
||||
plot(tukey_value, "Tukey Biweight", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,41 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Weighted Mean Absolute Percentage Error", "WMAPE", overlay=false, format=format.percent)
|
||||
|
||||
//@function Calculates Weighted Mean Absolute Percentage Error
|
||||
//@doc Weights errors by actual value magnitude, industry standard for demand forecasting.
|
||||
//@doc WMAPE = (Σ|actual - predicted| / Σ|actual|) * 100
|
||||
//@doc More stable than MAPE for intermittent data with zero/low values.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for calculation
|
||||
//@returns WMAPE value as percentage
|
||||
wmape(series float actual, series float predicted, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute absolute error and absolute actual for current bar
|
||||
float absError = math.abs(nz(actual, 0.0) - nz(predicted, 0.0))
|
||||
float absActual = math.abs(nz(actual, 0.0))
|
||||
|
||||
// Rolling sums
|
||||
float sumAbsError = ta.sum(absError, length)
|
||||
float sumAbsActual = ta.sum(absActual, length)
|
||||
|
||||
// WMAPE = (Σ|error| / Σ|actual|) * 100
|
||||
float result = sumAbsActual > epsilon ? (sumAbsError / sumAbsActual) * 100.0 : 0.0
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
wmape_value = wmape(i_actual, i_predicted, i_length)
|
||||
|
||||
// Plot
|
||||
plot(wmape_value, "WMAPE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Perfect", color=color.green, linestyle=hline.style_dotted)
|
||||
@@ -0,0 +1,53 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Weighted Root Mean Squared Error", "WRMSE", overlay=false)
|
||||
|
||||
//@function Calculates Weighted Root Mean Squared Error
|
||||
//@doc WRMSE extends RMSE by weighting each error differently.
|
||||
//@doc WRMSE = √(Σ(w * (actual - predicted)²) / Σ(w))
|
||||
//@doc Reduces to RMSE when all weights are equal.
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param weight Series of weights for each observation
|
||||
//@param length Rolling window for calculation
|
||||
//@returns WRMSE value in same units as original data
|
||||
wrmse(series float actual, series float predicted, series float weight, simple int length) =>
|
||||
float epsilon = 1e-10
|
||||
|
||||
// Compute weighted squared error for current bar
|
||||
float diff = nz(actual, 0.0) - nz(predicted, 0.0)
|
||||
float w = math.max(nz(weight, 1.0), 0.0) // Ensure non-negative weight
|
||||
float weightedSqError = w * diff * diff
|
||||
|
||||
// Rolling sums
|
||||
float sumWeightedError = ta.sum(weightedSqError, length)
|
||||
float sumWeight = ta.sum(w, length)
|
||||
|
||||
// WRMSE = √(Σ(w*e²) / Σ(w))
|
||||
float result = sumWeight > epsilon ? math.sqrt(sumWeightedError / sumWeight) : 0.0
|
||||
result
|
||||
|
||||
//@function Calculates WRMSE with uniform weights (equivalent to RMSE)
|
||||
//@param actual Series of actual values
|
||||
//@param predicted Series of predicted/forecast values
|
||||
//@param length Rolling window for calculation
|
||||
//@returns WRMSE value (same as RMSE when weights are uniform)
|
||||
wrmse_uniform(series float actual, series float predicted, simple int length) =>
|
||||
wrmse(actual, predicted, 1.0, length)
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(14, "Length", minval=1)
|
||||
i_use_volume_weights = input.bool(false, "Use Volume as Weights", tooltip="When enabled, errors are weighted by volume")
|
||||
i_actual = input.source(close, "Actual")
|
||||
i_predicted = input.source(open, "Predicted")
|
||||
|
||||
// Calculation
|
||||
weight = i_use_volume_weights ? volume : 1.0
|
||||
wrmse_value = wrmse(i_actual, i_predicted, weight, i_length)
|
||||
|
||||
// Plot
|
||||
plot(wrmse_value, "WRMSE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Perfect", color=color.green, linestyle=hline.style_dotted)
|
||||
Reference in New Issue
Block a user