Files

125 lines
5.3 KiB
Plaintext
Raw Permalink Normal View History

// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Parabolic SAR Extended (SAREXT)", "SAREXT", overlay=true)
//@function Parabolic SAR Extended — asymmetric PSAR with separate long/short AF parameters,
// auto-detect or forced initial direction, and an offset applied on reversal.
// Output is sign-encoded: positive SAR = long mode, negative SAR = short mode.
//@param start_value Initial direction: >0 force long, <0 force short, 0 auto-detect from DM
//@param offset_on_reverse Gap added to SAR on reversal (default 0)
//@param af_init_long Initial AF for long positions (default 0.02)
//@param af_long AF increment per new EP in long (default 0.02)
//@param af_max_long Maximum AF for long (default 0.20)
//@param af_init_short Initial AF for short positions (default 0.02)
//@param af_short AF increment per new EP in short (default 0.02)
//@param af_max_short Maximum AF for short (default 0.20)
//@returns Sign-encoded SAR value (positive = long, negative = short)
//@reference J. Welles Wilder Jr., "New Concepts in Technical Trading Systems" (1978)
//@optimized O(1) per bar, state-machine with asymmetric acceleration factors
sarext(simple float start_value = 0.0, simple float offset_on_reverse = 0.0,
simple float af_init_long = 0.02, simple float af_long = 0.02, simple float af_max_long = 0.20,
simple float af_init_short = 0.02, simple float af_short = 0.02, simple float af_max_short = 0.20) =>
if af_init_long <= 0 or af_long <= 0 or af_max_long <= af_init_long
runtime.error("Long AF parameters invalid")
if af_init_short <= 0 or af_short <= 0 or af_max_short <= af_init_short
runtime.error("Short AF parameters invalid")
if offset_on_reverse < 0
runtime.error("Offset must be >= 0")
var bool is_long = true
var float sar = low
var float ep = high
var float af = af_init_long
if bar_index == 0
// Bar 0: collect first bar data
is_long := true
sar := high
ep := low
af := af_init_short
float(na)
else if bar_index == 1
// Bar 1: determine initial direction
if start_value > 0
is_long := true
sar := math.min(low[1], low)
ep := math.max(high[1], high)
af := af_init_long
else if start_value < 0
is_long := false
sar := math.max(high[1], high)
ep := math.min(low[1], low)
af := af_init_short
else
// Auto-detect from DM
float plus_dm = high - high[1]
float minus_dm = low[1] - low
if plus_dm > minus_dm and plus_dm > 0
is_long := true
sar := math.min(low[1], low)
ep := math.max(high[1], high)
af := af_init_long
else
is_long := false
sar := math.max(high[1], high)
ep := math.min(low[1], low)
af := af_init_short
is_long ? sar : -sar
else
// Bar 2+: standard SAR state machine with asymmetric AF
float new_sar = sar + af * (ep - sar)
if is_long
new_sar := math.min(new_sar, low[1])
if bar_index > 1
new_sar := math.min(new_sar, low[2])
if low <= new_sar
// Reverse to short
is_long := false
new_sar := ep + offset_on_reverse
ep := low
af := af_init_short
else
if high > ep
ep := high
af := math.min(af + af_long, af_max_long)
else
new_sar := math.max(new_sar, high[1])
if bar_index > 1
new_sar := math.max(new_sar, high[2])
if high >= new_sar
// Reverse to long
is_long := true
new_sar := ep - offset_on_reverse
ep := high
af := af_init_long
else
if low < ep
ep := low
af := math.min(af + af_short, af_max_short)
sar := new_sar
is_long ? sar : -sar
// ── Inputs ──
float i_start = input.float(0.0, "Start Value (0=auto)")
float i_offset = input.float(0.0, "Offset on Reverse", minval=0)
float i_af_init_long = input.float(0.02, "AF Init Long", minval=0.001, step=0.001)
float i_af_long = input.float(0.02, "AF Long", minval=0.001, step=0.001)
float i_af_max_long = input.float(0.20, "AF Max Long", minval=0.01, step=0.01)
float i_af_init_short = input.float(0.02, "AF Init Short", minval=0.001, step=0.001)
float i_af_short = input.float(0.02, "AF Short", minval=0.001, step=0.001)
float i_af_max_short = input.float(0.20, "AF Max Short", minval=0.01, step=0.01)
// ── Calculation ──
float result = sarext(i_start, i_offset, i_af_init_long, i_af_long, i_af_max_long,
i_af_init_short, i_af_short, i_af_max_short)
float sar_val = math.abs(nz(result))
bool is_long = nz(result) > 0
float sar_above = not is_long ? sar_val : na
float sar_below = is_long ? sar_val : na
// ── Plot ──
plot(sar_above, "SAREXT Above", color=color.red, style=plot.style_linebr, linewidth=2)
plot(sar_below, "SAREXT Below", color=color.green, style=plot.style_linebr, linewidth=2)