///|
pub fn value_at_risk(data : Array[Double], probability : Double) -> Double {
quantile(data, probability)
}
///|
pub fn conditional_value_at_risk(
data : Array[Double],
probability : Double,
) -> Double {
lower_tail_mean(data, probability)
}
///|
pub fn upper_conditional_value_at_risk(
data : Array[Double],
probability : Double,
) -> Double {
upper_tail_mean(data, probability)
}
///|
pub fn downside_deviation(data : Array[Double], target : Double) -> Double {
let mut total = 0.0
let mut count = 0
for value in data {
if value < target {
let difference = value - target
total += difference * difference
count += 1
}
}
if count == 0 {
0.0
} else {
(total / count.to_double()).sqrt()
}
}
///|
pub fn upside_deviation(data : Array[Double], target : Double) -> Double {
let mut total = 0.0
let mut count = 0
for value in data {
if value > target {
let difference = value - target
total += difference * difference
count += 1
}
}
if count == 0 {
0.0
} else {
(total / count.to_double()).sqrt()
}
}
///|
pub fn robust_sharpe_ratio(
data : Array[Double],
risk_free? : Double = 0.0,
) -> Double {
let excess = difference_from_baseline(data, risk_free)
let scale = mad(excess)
if scale == 0.0 {
0.0
} else {
median(excess) / scale
}
}
///|
pub fn robust_sortino_ratio(
data : Array[Double],
target? : Double = 0.0,
) -> Double {
let center = median(data) - target
let scale = downside_deviation(data, target)
if scale == 0.0 {
0.0
} else {
center / scale
}
}
///|
pub fn maximum_drawdown(data : Array[Double]) -> Double {
if data.length() == 0 {
return 0.0
}
let mut peak = data[0]
let mut maximum = 0.0
for value in data {
if value > peak {
peak = value
}
if peak != 0.0 {
let drawdown = (peak - value) / abs_double(peak)
if drawdown > maximum {
maximum = drawdown
}
}
}
maximum
}
///|
pub fn drawdown_series(data : Array[Double]) -> Array[Double] {
let result = []
if data.length() == 0 {
return result
}
let mut peak = data[0]
for value in data {
if value > peak {
peak = value
}
result.push(
if peak == 0.0 {
0.0
} else {
(peak - value) / abs_double(peak)
},
)
}
result
}
///|
pub fn recovery_index(data : Array[Double]) -> Double {
let drawdowns = drawdown_series(data)
if drawdowns.length() == 0 {
0.0
} else {
1.0 - maximum_drawdown(data)
}
}
///|
pub fn robust_tail_ratio(data : Array[Double], probability : Double) -> Double {
let upper = upper_tail_mean(data, 1.0 - probability)
let lower = abs_double(lower_tail_mean(data, probability))
if lower == 0.0 {
0.0
} else {
upper / lower
}
}
///|
pub fn robust_loss(data : Array[Double], target : Double) -> Double {
let mut total = 0.0
for value in data {
total += huber_loss(value - target, mad(data) + 0.000001)
}
if data.length() == 0 {
0.0
} else {
total / data.length().to_double()
}
}
///|
pub fn robust_expected_loss(
data : Array[Double],
target : Double,
probability : Double,
) -> Double {
let tail = []
for value in data {
if value <= target {
tail.push(value)
}
}
if tail.length() == 0 {
0.0
} else {
probability * mean(tail)
}
}
///|
pub fn robust_stress_score(
baseline : Array[Double],
stressed : Array[Double],
) -> Double {
if baseline.length() == 0 || stressed.length() == 0 {
return 0.0
}
let baseline_scale = if mad(baseline) == 0.0 { 1.0 } else { mad(baseline) }
abs_double(median(stressed) - median(baseline)) / baseline_scale
}
///|
pub fn robust_risk_summary(data : Array[Double]) -> Array[Double] {
[
value_at_risk(data, 0.05),
conditional_value_at_risk(data, 0.05),
maximum_drawdown(data),
downside_deviation(data, 0.0),
robust_sharpe_ratio(data),
]
}
///|
pub fn tail_event_indices(
data : Array[Double],
probability : Double,
lower? : Bool = true,
) -> Array[Int] {
let threshold = if lower {
quantile(data, probability)
} else {
quantile(data, 1.0 - probability)
}
let result = []
for index = 0; index < data.length(); index = index + 1 {
if lower && data[index] <= threshold {
result.push(index)
} else if !lower && data[index] >= threshold {
result.push(index)
}
}
result
}
///|
pub fn expected_shortfall_gap(
data : Array[Double],
probability : Double,
) -> Double {
abs_double(
conditional_value_at_risk(data, probability) -
value_at_risk(data, probability),
)
}
///|
pub fn robust_return_center(data : Array[Double]) -> Double {
huber_location(data)
}
///|
pub fn robust_return_scale(data : Array[Double]) -> Double {
mad(data)
}