Files

92 lines
5.9 KiB
Plaintext

// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ultimate Oscillator (ULTOSC)", "ULTOSC", overlay=false)
//@function Calculates the Ultimate Oscillator using three weighted time periods
//@param fastPeriod Short-term period for momentum calculation
//@param mediumPeriod Medium-term period for momentum calculation
//@param slowPeriod Long-term period for momentum calculation
//@param fastWeight Weight applied to fast period calculation
//@param mediumWeight Weight applied to medium period calculation
//@param slowWeight Weight applied to slow period calculation
//@returns Ultimate Oscillator value (0-100 scale)
ultosc(simple int fastPeriod, simple int mediumPeriod, simple int slowPeriod, simple float fastWeight, simple float mediumWeight, simple float slowWeight) =>
if fastPeriod <= 0 or mediumPeriod <= 0 or slowPeriod <= 0
runtime.error("All periods must be positive")
if fastPeriod >= mediumPeriod or mediumPeriod >= slowPeriod
runtime.error("Periods must be in ascending order: fast < medium < slow")
if fastWeight <= 0 or mediumWeight <= 0 or slowWeight <= 0
runtime.error("All weights must be positive")
prev_close = nz(close[1], close)
true_low = math.min(low, prev_close)
true_high = math.max(high, prev_close)
buying_pressure = close - true_low
true_range = true_high - true_low
var array<float> bp_fast_buffer = array.new_float(fastPeriod, na)
var array<float> tr_fast_buffer = array.new_float(fastPeriod, na)
var array<float> bp_medium_buffer = array.new_float(mediumPeriod, na)
var array<float> tr_medium_buffer = array.new_float(mediumPeriod, na)
var array<float> bp_slow_buffer = array.new_float(slowPeriod, na)
var array<float> tr_slow_buffer = array.new_float(slowPeriod, na)
var int fast_head = 0, var int medium_head = 0, var int slow_head = 0
var float bp_fast_sum = 0.0, var float tr_fast_sum = 0.0
var float bp_medium_sum = 0.0, var float tr_medium_sum = 0.0
var float bp_slow_sum = 0.0, var float tr_slow_sum = 0.0
var int fast_count = 0, var int medium_count = 0, var int slow_count = 0
bp_fast_oldest = array.get(bp_fast_buffer, fast_head)
tr_fast_oldest = array.get(tr_fast_buffer, fast_head)
bp_fast_sum := not na(bp_fast_oldest) ? bp_fast_sum - bp_fast_oldest : bp_fast_sum
tr_fast_sum := not na(tr_fast_oldest) ? tr_fast_sum - tr_fast_oldest : tr_fast_sum
fast_count := not na(bp_fast_oldest) ? fast_count - 1 : fast_count
bp_fast_sum := not na(buying_pressure) ? bp_fast_sum + buying_pressure : bp_fast_sum
tr_fast_sum := not na(true_range) ? tr_fast_sum + true_range : tr_fast_sum
fast_count := not na(buying_pressure) ? fast_count + 1 : fast_count
array.set(bp_fast_buffer, fast_head, buying_pressure)
array.set(tr_fast_buffer, fast_head, true_range)
fast_head := (fast_head + 1) % fastPeriod
bp_medium_oldest = array.get(bp_medium_buffer, medium_head)
tr_medium_oldest = array.get(tr_medium_buffer, medium_head)
bp_medium_sum := not na(bp_medium_oldest) ? bp_medium_sum - bp_medium_oldest : bp_medium_sum
tr_medium_sum := not na(tr_medium_oldest) ? tr_medium_sum - tr_medium_oldest : tr_medium_sum
medium_count := not na(bp_medium_oldest) ? medium_count - 1 : medium_count
bp_medium_sum := not na(buying_pressure) ? bp_medium_sum + buying_pressure : bp_medium_sum
tr_medium_sum := not na(true_range) ? tr_medium_sum + true_range : tr_medium_sum
medium_count := not na(buying_pressure) ? medium_count + 1 : medium_count
array.set(bp_medium_buffer, medium_head, buying_pressure)
array.set(tr_medium_buffer, medium_head, true_range)
medium_head := (medium_head + 1) % mediumPeriod
bp_slow_oldest = array.get(bp_slow_buffer, slow_head)
tr_slow_oldest = array.get(tr_slow_buffer, slow_head)
bp_slow_sum := not na(bp_slow_oldest) ? bp_slow_sum - bp_slow_oldest : bp_slow_sum
tr_slow_sum := not na(tr_slow_oldest) ? tr_slow_sum - tr_slow_oldest : tr_slow_sum
slow_count := not na(bp_slow_oldest) ? slow_count - 1 : slow_count
bp_slow_sum := not na(buying_pressure) ? bp_slow_sum + buying_pressure : bp_slow_sum
tr_slow_sum := not na(true_range) ? tr_slow_sum + true_range : tr_slow_sum
slow_count := not na(buying_pressure) ? slow_count + 1 : slow_count
array.set(bp_slow_buffer, slow_head, buying_pressure)
array.set(tr_slow_buffer, slow_head, true_range)
slow_head := (slow_head + 1) % slowPeriod
raw_fast = tr_fast_sum > 0 and fast_count >= fastPeriod ? 100 * bp_fast_sum / tr_fast_sum : 0
raw_medium = tr_medium_sum > 0 and medium_count >= mediumPeriod ? 100 * bp_medium_sum / tr_medium_sum : 0
raw_slow = tr_slow_sum > 0 and slow_count >= slowPeriod ? 100 * bp_slow_sum / tr_slow_sum : 0
total_weight = fastWeight + mediumWeight + slowWeight
weighted_sum = (raw_fast * fastWeight) + (raw_medium * mediumWeight) + (raw_slow * slowWeight)
slow_count >= slowPeriod ? weighted_sum / total_weight : na
// ---------- Main loop ----------
// Inputs
i_fastPeriod = input.int(7, "Fast Period", minval=1, maxval=50, tooltip="Short-term period for momentum calculation")
i_mediumPeriod = input.int(14, "Medium Period", minval=1, maxval=100, tooltip="Medium-term period for momentum calculation")
i_slowPeriod = input.int(28, "Slow Period", minval=1, maxval=200, tooltip="Long-term period for momentum calculation")
i_fastWeight = input.float(4.0, "Fast Weight", minval=0.1, maxval=10.0, step=0.1, tooltip="Weight applied to fast period calculation")
i_mediumWeight = input.float(2.0, "Medium Weight", minval=0.1, maxval=10.0, step=0.1, tooltip="Weight applied to medium period calculation")
i_slowWeight = input.float(1.0, "Slow Weight", minval=0.1, maxval=10.0, step=0.1, tooltip="Weight applied to slow period calculation")
// Calculation
ultosc_value = ultosc(i_fastPeriod, i_mediumPeriod, i_slowPeriod, i_fastWeight, i_mediumWeight, i_slowWeight)
// Plots
plot(ultosc_value, "Ultimate Oscillator", color=color.yellow, linewidth=2)