WU Trading Library 0.2.0
A backtesting and trading strategy library
Loading...
Searching...
No Matches
sortino.c
Go to the documentation of this file.
1/**
2 * The Sortino Ratio is a variation of the Sharpe Ratio that differentiates
3 * between harmful volatility (downside) and total volatility. It uses only
4 * the standard deviation of negative returns (downside deviation) in the
5 * denominator.
6 *
7 * Formula: Sortino = (R_p - R_f) / σ_d
8 * where:
9 *
10 * R_p = mean portfolio return (annualized)
11 * R_f = risk-free rate (annualized)
12 * σ_d = downside deviation (annualized)
13 */
14
15#include <stdlib.h>
16#include <math.h>
17#include <stdio.h>
18#include "wu.h"
19
20/**
21 * Minimum number of observations required for statistically meaningful
22 * Sortino ratio calculation. Below this threshold, the ratio is unreliable.
23 */
24#define SORTINO_MIN_OBSERVATIONS 30
25
26/**
27 * Updates the Sortino Ratio calculation with a new portfolio value.
28 */
29static double wu_sortino_ratio_update(WU_SortinoRatio self,
31 if (!self) return NAN;
32
33 double portfolio_value = perf.portfolio_value;
34
35 /* Initialize on first observation */
36 if (self->count == 0) {
37 self->start_time = perf.timestamp.mark;
38 self->time_unit = perf.timestamp.units;
39 self->prev_value = portfolio_value;
40 self->count++;
41 self->value = NAN;
42 return self->value;
43 }
44
45 /* Calculate simple return from previous period */
46 double ret = (portfolio_value - self->prev_value) / self->prev_value;
47 self->prev_value = portfolio_value;
48 self->end_time = perf.timestamp.mark;
49 self->count++;
50
51 /* Update running statistics */
52 double mean = wu_indicator_update(self->mean, ret);
53 double downside_dev = wu_indicator_update(self->downside, ret);
54
55 /* Need minimum observations for reliable calculation */
56 if (self->count < SORTINO_MIN_OBSERVATIONS) {
57 self->value = NAN;
58 return self->value;
59 }
60
61 /* Validate downside deviation */
62 if (isnan(downside_dev) || downside_dev <= 0.0) {
63 self->value = NAN;
64 return self->value;
65 }
66
67 /* Calculate annualization factor */
68 double annualization_factor = wu_annualization_factor(
69 perf.timestamp.units);
70 double periods_elapsed = self->end_time - self->start_time;
71
72 /* Avoid division by zero or negative time */
73 if (periods_elapsed <= 0) {
74 self->value = NAN;
75 return self->value;
76 }
77
78 /* Calculate periods per year based on observed frequency */
79 double periods_per_year = (annualization_factor * self->count)
80 / periods_elapsed;
81 if (periods_per_year <= 0) {
82 self->value = NAN;
83 return self->value;
84 }
85
86 double annualized_downside_dev = downside_dev * sqrt(periods_per_year);
87 double per_period_rf = self->risk_free_rate / periods_per_year;
88 self->value = (mean - per_period_rf) / annualized_downside_dev;
89 return self->value;
90}
91
92/**
93 * Frees resources allocated by the Sortino Ratio object.
94 */
95static void wu_sortino_ratio_free(WU_SortinoRatio self) {
96 if (!self) return;
97 wu_indicator_delete(self->mean);
98 wu_indicator_delete(self->downside);
99 free(self);
100}
101
102/**
103 * Creates a new Sortino Ratio calculator.
104 */
105WU_SortinoRatio wu_sortino_ratio_new(double initial_value,
106 double risk_free_rate) {
107 WU_SortinoRatio sr = malloc(sizeof(struct WU_SortinoRatio_));
108 if (!sr) return NULL;
109
110 sr->mean = wu_mean_new();
111 if (!sr->mean) {
112 fprintf(stderr, "Sortino Ratio Error: mean object creation failed\n");
113 free(sr);
114 return NULL;
115 }
116
117 sr->downside = wu_downside_new();
118 if (!sr->downside) {
119 fprintf(stderr, "Sortino Ratio Error: downside object creation failed\n");
120 wu_indicator_delete(sr->mean);
121 free(sr);
122 return NULL;
123 }
124
125 sr->risk_free_rate = risk_free_rate;
126 sr->value = NAN;
127 sr->prev_value = initial_value;
128 sr->count = 0;
129 sr->start_time = 0;
130 sr->end_time = 0;
131 sr->time_unit = WU_TIME_UNIT_SECONDS;
132 sr->update = wu_sortino_ratio_update;
133 sr->delete = wu_sortino_ratio_free;
134
135 return sr;
136}
#define wu_indicator_delete(indicator)
Delete the indicator and free any resources allocated by it.
Definition indicators.h:56
#define wu_indicator_update(indicator, value)
Header file for technical indicators.
Definition indicators.h:41
WU_Downside wu_downside_new(void)
Creates a new WU_Downside indicator.
WU_Mean wu_mean_new(void)
Creates a new WU_Mean indicator.
Definition mean.c:15
static double wu_sortino_ratio_update(WU_SortinoRatio self, WU_PerformanceUpdate perf)
Updates the Sortino Ratio calculation with a new portfolio value.
Definition sortino.c:29
static void wu_sortino_ratio_free(WU_SortinoRatio self)
Frees resources allocated by the Sortino Ratio object.
Definition sortino.c:95
WU_SortinoRatio wu_sortino_ratio_new(double initial_value, double risk_free_rate)
Creates a new Sortino Ratio calculator.
Definition sortino.c:105
#define SORTINO_MIN_OBSERVATIONS
The Sortino Ratio is a variation of the Sharpe Ratio that differentiates between harmful volatility (...
Definition sortino.c:24
Performance Update - value and timestamp for performance metric calculations.
Definition indicators.h:239
WU_TimeStamp timestamp
Definition indicators.h:241
int64_t mark
Definition timeutils.h:22
WU_TimeUnit units
Definition timeutils.h:23
@ WU_TIME_UNIT_SECONDS
Definition timeutils.h:11
double wu_annualization_factor(WU_TimeUnit unit)
Returns the number of units (seconds, millis, micros, or nanos) contained in a year.
Definition timeutils.c:3