WU Trading Library 0.2.0
A backtesting and trading strategy library
Loading...
Searching...
No Matches
calmar.c
Go to the documentation of this file.
1/**
2 * The Calmar Ratio measures risk-adjusted return by comparing annualized
3 * returns to the maximum drawdown. Unlike Sharpe/Sortino, it uses maximum
4 * drawdown as the risk measure instead of volatility.
5 *
6 * Formula: Calmar = Annualized_Return / |Maximum_Drawdown|
7 * where:
8 *
9 * Annualized_Return = (Total_Return)^(1/years) - 1 [compound]
10 * or Total_Return * periods_per_year [simple]
11 *
12 * This implementation uses simple annualization for consistency with other
13 * risk metrics in this library.
14 */
15
16#include <stdlib.h>
17#include <math.h>
18#include <stdio.h>
19#include "wu.h"
20
21/**
22 * Minimum observations required before calculating Calmar ratio.
23 * This ensures we have enough data for a meaningful drawdown measurement.
24 */
25#define CALMAR_MIN_OBSERVATIONS 30
26
27/**
28 * Updates the Calmar Ratio calculation with a new portfolio value.
29 *
30 * @param self The Calmar Ratio object
31 * @param perf Performance update containing portfolio value and timestamp
32 * @return The updated Calmar Ratio value, or NAN if insufficient data
33 */
34static double wu_calmar_ratio_update(WU_CalmarRatio self,
36 if (!self) return NAN;
37
38 double portfolio_value = perf.portfolio_value;
39
40 /* initialize on first observation */
41 if (self->count == 0) {
42 self->prev_value = portfolio_value;
43 self->start_time = perf.timestamp.mark;
44 self->time_unit = perf.timestamp.units;
45 self->count++;
46 self->value = NAN;
47 return self->value;
48 }
49
50 self->prev_value = portfolio_value;
51 self->end_time = perf.timestamp.mark;
52 self->count++;
53
54 if (self->max_drawdown) {
55 wu_indicator_update(self->max_drawdown, portfolio_value);
56 }
57
58 if (self->count < CALMAR_MIN_OBSERVATIONS) {
59 self->value = NAN;
60 return self->value;
61 }
62
63 if (self->initial_value == 0.0) {
64 self->value = NAN;
65 return self->value;
66 }
67
68 double mdd = wu_indicator_get(self->max_drawdown);
69
70 if (isnan(mdd) || mdd >= 0.0) {
71 self->value = NAN;
72 return self->value;
73 }
74
75 double annualization_factor = wu_annualization_factor(
76 perf.timestamp.units);
77 double periods_elapsed = self->end_time - self->start_time;
78
79 if (periods_elapsed <= 0) {
80 self->value = NAN;
81 return self->value;
82 }
83
84 double periods_per_year = (annualization_factor * self->count)
85 / periods_elapsed;
86 if (periods_per_year <= 0) {
87 self->value = NAN;
88 return self->value;
89 }
90
91 /* calculate total return from inception */
92 double total_return = (portfolio_value - self->initial_value)
93 / self->initial_value;
94
95 double annualized_return = total_return * periods_per_year;
96
97 self->value = annualized_return / fabs(mdd);
98 return self->value;
99}
100
101/**
102 * Frees resources allocated by the Calmar Ratio object.
103 */
104static void wu_calmar_ratio_free(WU_CalmarRatio self) {
105 if (!self) return;
106 if (self->max_drawdown) {
107 wu_indicator_delete(self->max_drawdown);
108 }
109 free(self);
110}
111
112WU_CalmarRatio wu_calmar_ratio_new(double initial_value) {
113 WU_CalmarRatio cr = malloc(sizeof(struct WU_CalmarRatio_));
114 if (!cr) return NULL;
115 cr->max_drawdown = wu_max_drawdown_new();
116 if (!cr->max_drawdown) {
117 fprintf(stderr, "Calmar Ratio Error: max_drawdown object creation failed\n");
118 free(cr);
119 return NULL;
120 }
121 cr->initial_value = initial_value;
122 cr->value = NAN;
123 cr->prev_value = initial_value;
124 cr->count = 0;
125 cr->start_time = 0;
126 cr->end_time = 0;
127 cr->time_unit = WU_TIME_UNIT_SECONDS;
128 cr->update = wu_calmar_ratio_update;
129 cr->delete = wu_calmar_ratio_free;
130 return cr;
131}
WU_CalmarRatio wu_calmar_ratio_new(double initial_value)
Definition calmar.c:112
static void wu_calmar_ratio_free(WU_CalmarRatio self)
Frees resources allocated by the Calmar Ratio object.
Definition calmar.c:104
static double wu_calmar_ratio_update(WU_CalmarRatio self, WU_PerformanceUpdate perf)
Updates the Calmar Ratio calculation with a new portfolio value.
Definition calmar.c:34
#define CALMAR_MIN_OBSERVATIONS
The Calmar Ratio measures risk-adjusted return by comparing annualized returns to the maximum drawdow...
Definition calmar.c:25
#define wu_indicator_get(indicator)
Get the current value of the indicator.
Definition indicators.h:49
#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_MaxDrawdown wu_max_drawdown_new(void)
Creates a new WU_MaxDrawdown indicator.
Definition maxdrawdown.c:20
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