mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
167 lines
5.6 KiB
Plaintext
167 lines
5.6 KiB
Plaintext
// Licensed under the Apache License, Version 2.0
|
|
// © mihakralj
|
|
//@version=6
|
|
indicator("Ehlers Hilbert Transform Trend vs Cycle Mode (HT_TRENDMODE)", "HT_TRENDMODE", overlay=false)
|
|
|
|
//@function Determines if market is in trend mode (1) or cycle mode (0) using TA-Lib's Ehlers algorithm
|
|
//@param source Series to analyze for trend/cycle state
|
|
//@returns 1 for trend mode, 0 for cycle mode
|
|
ht_trendmode(series float source) =>
|
|
// Constants
|
|
var float A_CONST = 0.0962
|
|
var float B_CONST = 0.5769
|
|
var float RAD2DEG = 180.0 / math.pi
|
|
var float DEG2RAD = math.pi / 180.0
|
|
|
|
// State variables
|
|
var float smooth_price = 0.0
|
|
var float detrender = 0.0
|
|
var float i1 = 0.0
|
|
var float q1 = 0.0
|
|
var float ji = 0.0
|
|
var float jq = 0.0
|
|
var float i2 = 0.0
|
|
var float q2 = 0.0
|
|
var float re = 0.0
|
|
var float im = 0.0
|
|
var float period = 15.0
|
|
var float smooth_period = 15.0
|
|
var float dc_phase = 0.0
|
|
var float prev_dc_phase = 0.0
|
|
var float sine = 0.0
|
|
var float lead_sine = 0.0
|
|
var float prev_sine = 0.0
|
|
var float prev_lead_sine = 0.0
|
|
var float trendline = 0.0
|
|
var float i_trend_1 = 0.0
|
|
var float i_trend_2 = 0.0
|
|
var float i_trend_3 = 0.0
|
|
var int days_in_trend = 0
|
|
var int trend_mode = 0
|
|
|
|
float price = nz(source)
|
|
float bandwidth = 0.075 * smooth_period + 0.54
|
|
|
|
// Smoothed price (WMA-like)
|
|
smooth_price := (4.0 * price + 3.0 * nz(price[1]) + 2.0 * nz(price[2]) + nz(price[3])) / 10.0
|
|
|
|
// Hilbert Transform
|
|
detrender := (A_CONST * smooth_price + B_CONST * nz(smooth_price[2]) - B_CONST * nz(smooth_price[4]) - A_CONST * nz(smooth_price[6])) * bandwidth
|
|
q1 := (A_CONST * detrender + B_CONST * nz(detrender[2]) - B_CONST * nz(detrender[4]) - A_CONST * nz(detrender[6])) * bandwidth
|
|
i1 := nz(detrender[3])
|
|
ji := (A_CONST * i1 + B_CONST * nz(i1[2]) - B_CONST * nz(i1[4]) - A_CONST * nz(i1[6])) * bandwidth
|
|
jq := (A_CONST * q1 + B_CONST * nz(q1[2]) - B_CONST * nz(q1[4]) - A_CONST * nz(q1[6])) * bandwidth
|
|
|
|
// Phasor rotation
|
|
i2 := i1 - jq
|
|
q2 := q1 + ji
|
|
i2 := 0.2 * i2 + 0.8 * nz(i2[1])
|
|
q2 := 0.2 * q2 + 0.8 * nz(q2[1])
|
|
|
|
// Re/Im calculation
|
|
re := i2 * nz(i2[1]) + q2 * nz(q2[1])
|
|
im := i2 * nz(q2[1]) - q2 * nz(i2[1])
|
|
re := 0.2 * re + 0.8 * nz(re[1])
|
|
im := 0.2 * im + 0.8 * nz(im[1])
|
|
|
|
// Period calculation
|
|
float temp_period = period
|
|
if math.abs(im) > 1e-10 and math.abs(re) > 1e-10
|
|
period := 360.0 / (math.atan(im / re) * RAD2DEG)
|
|
|
|
// Clamp period to 1.5x and 0.67x of previous
|
|
if period > 1.5 * temp_period
|
|
period := 1.5 * temp_period
|
|
if period < 0.67 * temp_period
|
|
period := 0.67 * temp_period
|
|
period := math.max(6.0, math.min(50.0, period))
|
|
period := 0.2 * period + 0.8 * temp_period
|
|
|
|
smooth_period := 0.33 * period + 0.67 * smooth_period
|
|
|
|
// DC Phase calculation
|
|
prev_dc_phase := dc_phase
|
|
int dc_period_int = int(smooth_period + 0.5)
|
|
|
|
float real_part = 0.0
|
|
float imag_part = 0.0
|
|
for i = 0 to dc_period_int - 1
|
|
float angle = (float(i) * 360.0 / float(dc_period_int)) * DEG2RAD
|
|
real_part += math.sin(angle) * nz(smooth_price[i])
|
|
imag_part += math.cos(angle) * nz(smooth_price[i])
|
|
|
|
if math.abs(imag_part) > 0.0
|
|
dc_phase := math.atan(real_part / imag_part) * RAD2DEG
|
|
else if math.abs(imag_part) <= 0.01
|
|
if real_part < 0.0
|
|
dc_phase := dc_phase - 90.0
|
|
else if real_part > 0.0
|
|
dc_phase := dc_phase + 90.0
|
|
|
|
dc_phase += 90.0
|
|
dc_phase += 360.0 / smooth_period // Lag compensation
|
|
if imag_part < 0.0
|
|
dc_phase += 180.0
|
|
if dc_phase > 315.0
|
|
dc_phase -= 360.0
|
|
|
|
// Sine and LeadSine
|
|
prev_sine := sine
|
|
prev_lead_sine := lead_sine
|
|
sine := math.sin(dc_phase * DEG2RAD)
|
|
lead_sine := math.sin((dc_phase + 45.0) * DEG2RAD)
|
|
|
|
// Trendline calculation (SMA over cycle, then WMA smoothing)
|
|
float sum_price = 0.0
|
|
for i = 0 to dc_period_int - 1
|
|
sum_price += nz(source[i])
|
|
float sma_value = dc_period_int > 0 ? sum_price / float(dc_period_int) : price
|
|
trendline := (4.0 * sma_value + 3.0 * i_trend_1 + 2.0 * i_trend_2 + i_trend_3) / 10.0
|
|
i_trend_3 := i_trend_2
|
|
i_trend_2 := i_trend_1
|
|
i_trend_1 := sma_value
|
|
|
|
// ==========================================
|
|
// Trend Mode Decision (TA-Lib Algorithm)
|
|
// ==========================================
|
|
int trend = 1 // Assume trend by default
|
|
|
|
// Criterion 1: SineWave crossing resets counter
|
|
bool sine_crosses = ((sine > lead_sine) and (prev_sine <= prev_lead_sine)) or
|
|
((sine < lead_sine) and (prev_sine >= prev_lead_sine))
|
|
if sine_crosses
|
|
days_in_trend := 0
|
|
trend := 0
|
|
|
|
days_in_trend += 1
|
|
|
|
// Criterion 2: Must be trending for at least half the smooth period
|
|
if days_in_trend < int(0.5 * smooth_period)
|
|
trend := 0
|
|
|
|
// Criterion 3: Phase rate check (normal rate → cycle mode)
|
|
float phase_change = dc_phase - prev_dc_phase
|
|
if smooth_period > 0.0
|
|
float expected_change = 360.0 / smooth_period
|
|
if (phase_change > 0.67 * expected_change) and (phase_change < 1.5 * expected_change)
|
|
trend := 0
|
|
|
|
// Criterion 4: Price-trendline deviation override (≥1.5% → trend)
|
|
if math.abs(trendline) > 1e-10
|
|
if math.abs((smooth_price - trendline) / trendline) >= 0.015
|
|
trend := 1
|
|
|
|
trend_mode := trend
|
|
trend_mode
|
|
|
|
// ---------- Main loop ----------
|
|
|
|
// Inputs
|
|
i_source = input.source(hlc3, "Source")
|
|
|
|
// Calculation
|
|
trendmode = ht_trendmode(i_source)
|
|
|
|
// Plot
|
|
plot(trendmode, "Trend Mode", color=trendmode == 1 ? color.green : color.red, linewidth=3, style=plot.style_stepline)
|