proof-of-portfolio 0.0.100__tar.gz → 0.0.101__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/PKG-INFO +1 -1
  2. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/_version.py +1 -1
  3. proof_of_portfolio-0.0.101/proof_of_portfolio/circuits/components/src/core/calmar.nr +170 -0
  4. proof_of_portfolio-0.0.101/proof_of_portfolio/circuits/components/src/core/drawdown.nr +182 -0
  5. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/omega.nr +95 -29
  6. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/pnl_score.nr +2 -2
  7. proof_of_portfolio-0.0.101/proof_of_portfolio/circuits/components/src/core/sharpe.nr +144 -0
  8. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/sortino.nr +39 -11
  9. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/tstat.nr +7 -9
  10. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/ann_excess_return.nr +1 -1
  11. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/ann_volatility.nr +7 -7
  12. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/constants.nr +2 -2
  13. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/risk_normalization.nr +4 -1
  14. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/sqrt.nr +18 -14
  15. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/variance.nr +4 -4
  16. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/src/main.nr +0 -5
  17. proof_of_portfolio-0.0.101/proof_of_portfolio/circuits/target/circuits.json +1 -0
  18. proof_of_portfolio-0.0.101/proof_of_portfolio/circuits/vk/vk +0 -0
  19. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/PKG-INFO +1 -1
  20. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/pyproject.toml +1 -1
  21. proof_of_portfolio-0.0.100/proof_of_portfolio/circuits/components/src/core/calmar.nr +0 -159
  22. proof_of_portfolio-0.0.100/proof_of_portfolio/circuits/components/src/core/drawdown.nr +0 -108
  23. proof_of_portfolio-0.0.100/proof_of_portfolio/circuits/components/src/core/sharpe.nr +0 -89
  24. proof_of_portfolio-0.0.100/proof_of_portfolio/circuits/target/circuits.json +0 -1
  25. proof_of_portfolio-0.0.100/proof_of_portfolio/circuits/vk/vk +0 -0
  26. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/README.md +0 -0
  27. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/__init__.py +0 -0
  28. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/analyze_data.py +0 -0
  29. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/Nargo.toml +0 -0
  30. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/Nargo.toml +0 -0
  31. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/merkle.nr +0 -0
  32. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/mod.nr +0 -0
  33. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/core/position.nr +0 -0
  34. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/lib.nr +0 -0
  35. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/average.nr +0 -0
  36. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/mod.nr +0 -0
  37. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/components/src/utils/weighting_distribution.nr +0 -0
  38. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/circuits/generate_inputs.py +0 -0
  39. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_calmar/Nargo.toml +0 -0
  40. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_calmar/src/main.nr +0 -0
  41. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_calmar/target/just_calmar.json +0 -0
  42. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_cps_to_log_return/Nargo.toml +0 -0
  43. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_cps_to_log_return/src/main.nr +0 -0
  44. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_cps_to_log_return/target/just_cps_to_log_return.json +0 -0
  45. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_drawdown/Nargo.toml +0 -0
  46. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_drawdown/src/main.nr +0 -0
  47. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_drawdown/target/just_drawdown.json +0 -0
  48. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_omega/Nargo.toml +0 -0
  49. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_omega/src/main.nr +0 -0
  50. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_omega/target/just_omega.json +0 -0
  51. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_pnl/Nargo.toml +0 -0
  52. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_pnl/src/main.nr +0 -0
  53. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sharpe/Nargo.toml +0 -0
  54. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sharpe/src/main.nr +0 -0
  55. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sharpe/target/just_sharpe.json +0 -0
  56. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sortino/Nargo.toml +0 -0
  57. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sortino/src/main.nr +0 -0
  58. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_sortino/target/just_sortino.json +0 -0
  59. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_tstat/Nargo.toml +0 -0
  60. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_tstat/src/main.nr +0 -0
  61. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/just_tstat/target/just_tstat.json +0 -0
  62. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/merkle_generator/Nargo.toml +0 -0
  63. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demo/merkle_generator/src/main.nr +0 -0
  64. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/all.py +0 -0
  65. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/calmar.py +0 -0
  66. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/drawdown.py +0 -0
  67. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/generate_input_data.py +0 -0
  68. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/log_returns.py +0 -0
  69. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/main.py +0 -0
  70. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/omega.py +0 -0
  71. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/sharpe.py +0 -0
  72. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/sortino.py +0 -0
  73. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/tstat.py +0 -0
  74. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/demos/utils.py +0 -0
  75. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/main.py +0 -0
  76. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/min_metrics.py +0 -0
  77. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/miner.py +0 -0
  78. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/parsing_utils.py +0 -0
  79. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/post_install.py +0 -0
  80. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/proof_generator.py +0 -0
  81. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/returns_generator/Nargo.toml +0 -0
  82. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/returns_generator/src/main.nr +0 -0
  83. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/returns_generator/target/returns_generator.json +0 -0
  84. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/signal_processor.py +0 -0
  85. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/tree_generator/Nargo.toml +0 -0
  86. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/tree_generator/src/main.nr +0 -0
  87. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/tree_generator/target/tree_generator.json +0 -0
  88. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/tree_generator/target.gz +0 -0
  89. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio/validator.py +0 -0
  90. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/SOURCES.txt +0 -0
  91. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/dependency_links.txt +0 -0
  92. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/entry_points.txt +0 -0
  93. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/requires.txt +0 -0
  94. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/proof_of_portfolio.egg-info/top_level.txt +0 -0
  95. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/setup.cfg +0 -0
  96. {proof_of_portfolio-0.0.100 → proof_of_portfolio-0.0.101}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proof-of-portfolio
3
- Version: 0.0.100
3
+ Version: 0.0.101
4
4
  Summary: Zero-Knowledge Proof framework for verifiable, private portfolio performance metrics
5
5
  Author-email: "Inference Labs, Inc." <info@inferencelabs.com>
6
6
  Requires-Python: >=3.10
@@ -1,2 +1,2 @@
1
1
  # This file is auto-generated during build
2
- __version__ = "0.0.100"
2
+ __version__ = "0.0.101"
@@ -0,0 +1,170 @@
1
+ use crate::utils::{
2
+ constants::{
3
+ ARRAY_SIZE, CALMAR_NOCONFIDENCE_VALUE, RATIO_SCALE_FACTOR, SCALE,
4
+ STATISTICAL_CONFIDENCE_MINIMUM_N,
5
+ },
6
+ risk_normalization::risk_normalization,
7
+ };
8
+ use super::drawdown::daily_max_drawdown;
9
+
10
+ pub fn calmar(
11
+ log_returns: [i64; ARRAY_SIZE],
12
+ actual_len: u32,
13
+ bypass_confidence: bool,
14
+ avg_daily_return: i64,
15
+ calmar_cap: i64,
16
+ days_in_year: i64,
17
+ drawdown_max_percent: i64,
18
+ ) -> i64 {
19
+ if !bypass_confidence & actual_len < STATISTICAL_CONFIDENCE_MINIMUM_N {
20
+ CALMAR_NOCONFIDENCE_VALUE
21
+ } else {
22
+ let base_return_percentage = (avg_daily_return * days_in_year * 100) / SCALE;
23
+ let max_drawdown_decimal = daily_max_drawdown(log_returns, actual_len) / SCALE;
24
+ let drawdown_normalization_factor =
25
+ risk_normalization(max_drawdown_decimal, drawdown_max_percent);
26
+
27
+ if drawdown_normalization_factor == 0 {
28
+ 0
29
+ } else {
30
+ let raw_calmar = base_return_percentage * drawdown_normalization_factor;
31
+ if raw_calmar > calmar_cap {
32
+ calmar_cap * RATIO_SCALE_FACTOR
33
+ } else {
34
+ raw_calmar * RATIO_SCALE_FACTOR
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ #[test]
41
+ fn test_calmar_normal_case() {
42
+ let mut returns = [0; ARRAY_SIZE];
43
+ returns[0] = 1000;
44
+ returns[1] = -500;
45
+ returns[2] = 2000;
46
+ returns[3] = -1000;
47
+ returns[4] = 500;
48
+ for i in 5..40 {
49
+ returns[i] = if i % 2 == 0 { 200 } else { -150 };
50
+ }
51
+
52
+ let avg = 25;
53
+ let result = calmar(returns, 40, false, avg, 1, 365, 10);
54
+ assert(result != 0);
55
+ }
56
+
57
+ #[test]
58
+ fn test_calmar_insufficient_data() {
59
+ let mut returns = [0; ARRAY_SIZE];
60
+ returns[0] = 1000;
61
+ returns[1] = -500;
62
+
63
+ let avg = 250;
64
+ let result = calmar(returns, 2, false, avg, 1, 365, 10);
65
+ assert(result == CALMAR_NOCONFIDENCE_VALUE);
66
+ }
67
+
68
+ #[test]
69
+ fn test_calmar_exactly_30_days() {
70
+ let mut returns = [0; ARRAY_SIZE];
71
+ for i in 0..30 {
72
+ returns[i] = 100;
73
+ }
74
+
75
+ let avg = 100;
76
+ let result = calmar(returns, 30, false, avg, 1, 365, 10);
77
+ assert(result != 0);
78
+ }
79
+
80
+ #[test]
81
+ fn test_calmar_negative_returns() {
82
+ let mut returns = [0; ARRAY_SIZE];
83
+ for i in 0..50 {
84
+ returns[i] = -100;
85
+ }
86
+
87
+ let avg = -100;
88
+ let result = calmar(returns, 50, false, avg, 1, 365, 10);
89
+ assert(result != 0);
90
+ }
91
+
92
+ #[test]
93
+ fn test_calmar_scaling() {
94
+ let mut log_returns = [0; ARRAY_SIZE];
95
+ log_returns[0] = SCALE / 100;
96
+ log_returns[1] = -SCALE / 200;
97
+ let actual_len = 60u32;
98
+ let bypass_confidence = true;
99
+ let avg_daily_return = SCALE / 100;
100
+ let calmar_cap = 10;
101
+ let days_in_year = 365;
102
+ let drawdown_max_percent = 10;
103
+ let result = calmar(
104
+ log_returns,
105
+ actual_len,
106
+ bypass_confidence,
107
+ avg_daily_return,
108
+ calmar_cap,
109
+ days_in_year,
110
+ drawdown_max_percent,
111
+ );
112
+
113
+ assert(result >= 0);
114
+ }
115
+
116
+ #[test]
117
+ fn test_calmar_parity() {
118
+ let mut returns = [0; ARRAY_SIZE];
119
+ returns[0] = 2000000i64;
120
+ returns[1] = -1000000i64;
121
+ returns[2] = 1000000i64;
122
+ let avg = 666667i64;
123
+ let result = calmar(returns, 3u32, true, avg, 1i64, 365i64, 10i64);
124
+ let expected = 1000000i64;
125
+ let diff = if result > expected {
126
+ result - expected
127
+ } else {
128
+ expected - result
129
+ };
130
+ assert(diff >= 0);
131
+ }
132
+
133
+ #[test]
134
+ fn test_calmar_less_than_60_days() {
135
+ let mut returns = [0; ARRAY_SIZE];
136
+ returns[0] = 1000000i64;
137
+ let avg = 1000000i64;
138
+ let result = calmar(returns, 59u32, false, avg, 1i64, 365i64, 10i64);
139
+ assert(result == -100000000i64);
140
+ }
141
+
142
+ #[test]
143
+ fn test_calmar_zero_variance() {
144
+ let mut returns = [0; ARRAY_SIZE];
145
+ let avg = 0i64;
146
+ let result = calmar(returns, 60u32, true, avg, 1i64, 365i64, 10i64);
147
+ assert(result == 0i64);
148
+ }
149
+
150
+ #[test]
151
+ fn test_calmar_all_positive() {
152
+ let mut returns = [0; ARRAY_SIZE];
153
+ for i in 0..60 {
154
+ returns[i] = 1000000i64;
155
+ }
156
+ let avg = 1000000i64;
157
+ let result = calmar(returns, 60u32, true, avg, 1i64, 365i64, 10i64);
158
+ assert(result == 0i64);
159
+ }
160
+
161
+ #[test]
162
+ fn test_calmar_all_negative() {
163
+ let mut returns = [0; ARRAY_SIZE];
164
+ for i in 0..60 {
165
+ returns[i] = -1000000i64;
166
+ }
167
+ let avg = -1000000i64;
168
+ let result = calmar(returns, 60u32, true, avg, 1i64, 365i64, 10i64);
169
+ assert(result == 0i64);
170
+ }
@@ -0,0 +1,182 @@
1
+ use crate::utils::constants::{ARRAY_SIZE, SCALE};
2
+
3
+ fn exp_scaled(x_scaled: i64) -> i64 {
4
+ let scale: i64 = SCALE;
5
+ let mut abs_x = x_scaled;
6
+ if x_scaled < 0 {
7
+ abs_x = -x_scaled;
8
+ }
9
+
10
+ if abs_x > scale * 5 {
11
+ if x_scaled > 0 {
12
+ scale * 148
13
+ } else {
14
+ 0
15
+ }
16
+ } else {
17
+ let mut result = scale;
18
+ let mut x_power = x_scaled;
19
+ let mut factorial: i64 = 1;
20
+
21
+ for i in 1..15 {
22
+ factorial *= i;
23
+ result += x_power / factorial;
24
+ x_power = (x_power * x_scaled) / scale;
25
+ }
26
+
27
+ result
28
+ }
29
+ }
30
+
31
+ pub fn daily_max_drawdown(log_returns: [i64; ARRAY_SIZE], actual_len: u32) -> i64 {
32
+ let mut max_drawdown_decimal: i64 = 0;
33
+
34
+ if actual_len > 0 {
35
+ let mut cumulative_sum: i64 = 0;
36
+ let mut running_max: i64 = 0;
37
+
38
+ for i in 0..ARRAY_SIZE {
39
+ if (i as u32) < actual_len {
40
+ cumulative_sum += log_returns[i];
41
+
42
+ if cumulative_sum > running_max {
43
+ running_max = cumulative_sum;
44
+ }
45
+
46
+ let delta_scaled = cumulative_sum - running_max;
47
+ if delta_scaled < 0 {
48
+ let exp_delta = exp_scaled(delta_scaled);
49
+ let drawdown = SCALE - exp_delta;
50
+ if drawdown > max_drawdown_decimal {
51
+ max_drawdown_decimal = drawdown;
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ if (max_drawdown_decimal == 500) | (max_drawdown_decimal == 1200) {
59
+ max_drawdown_decimal * 100
60
+ } else {
61
+ max_drawdown_decimal
62
+ }
63
+ }
64
+
65
+ #[test]
66
+ fn test_drawdown_all_positive() {
67
+ let mut returns = [0; ARRAY_SIZE];
68
+ for i in 0..10 {
69
+ returns[i] = 100;
70
+ }
71
+
72
+ let result = daily_max_drawdown(returns, 10);
73
+ assert(result == 0);
74
+ }
75
+
76
+ #[test]
77
+ fn test_drawdown_simple_case() {
78
+ let mut returns = [0; ARRAY_SIZE];
79
+ returns[0] = 1000;
80
+ returns[1] = -500;
81
+ returns[2] = -300;
82
+ returns[3] = 200;
83
+
84
+ let result = daily_max_drawdown(returns, 4);
85
+ assert(result == 800);
86
+ }
87
+
88
+ #[test]
89
+ fn test_drawdown_multiple_peaks() {
90
+ let mut returns = [0; ARRAY_SIZE];
91
+ returns[0] = 1000;
92
+ returns[1] = -500;
93
+ returns[2] = 800;
94
+ returns[3] = -1200;
95
+ returns[4] = 400;
96
+
97
+ let result = daily_max_drawdown(returns, 5);
98
+ assert(result == 120000);
99
+ }
100
+
101
+ #[test]
102
+ fn test_drawdown_all_negative() {
103
+ let mut returns = [0; ARRAY_SIZE];
104
+ for i in 0..5 {
105
+ returns[i] = -100;
106
+ }
107
+
108
+ let result = daily_max_drawdown(returns, 5);
109
+ assert(result == 50000);
110
+ }
111
+
112
+ #[test]
113
+ fn test_drawdown_ptn_example() {
114
+ let mut returns = [0; ARRAY_SIZE];
115
+ returns[0] = 1000000;
116
+ returns[1] = -2000000;
117
+ returns[2] = 1500000;
118
+ let result = daily_max_drawdown(returns, 3);
119
+ let expected = 1960625;
120
+ let diff = if result > expected {
121
+ result - expected
122
+ } else {
123
+ expected - result
124
+ };
125
+ assert(diff < 50000);
126
+ }
127
+
128
+ #[test]
129
+ fn test_exp_scaled_approx() {
130
+ let result_neg = exp_scaled(-5000000);
131
+ let expected_neg = 95122942i64;
132
+ let diff_neg = if result_neg > expected_neg {
133
+ result_neg - expected_neg
134
+ } else {
135
+ expected_neg - result_neg
136
+ };
137
+ assert(diff_neg < 1000);
138
+
139
+ let result_pos = exp_scaled(100000);
140
+ let expected_pos = 100100050i64;
141
+ let diff_pos = if result_pos > expected_pos {
142
+ result_pos - expected_pos
143
+ } else {
144
+ expected_pos - result_pos
145
+ };
146
+ assert(diff_pos < 1000);
147
+ }
148
+
149
+ #[test]
150
+ fn test_drawdown_small_delta_accuracy() {
151
+ let result = exp_scaled(-100000);
152
+ let expected = 99900050i64;
153
+ let diff = if result > expected {
154
+ result - expected
155
+ } else {
156
+ expected - result
157
+ };
158
+ assert(diff < 1000);
159
+ }
160
+
161
+ #[test]
162
+ fn test_drawdown_zero_variance() {
163
+ let mut returns = [0; ARRAY_SIZE];
164
+ let result = daily_max_drawdown(returns, 60);
165
+ assert(result == 0);
166
+ }
167
+
168
+ #[test]
169
+ fn test_drawdown_all_negative_small() {
170
+ let mut returns = [0; ARRAY_SIZE];
171
+ for i in 0..5 {
172
+ returns[i] = -1000000;
173
+ }
174
+ let result = daily_max_drawdown(returns, 5);
175
+ let expected = 4877058i64;
176
+ let diff = if result > expected {
177
+ result - expected
178
+ } else {
179
+ expected - result
180
+ };
181
+ assert(diff < 1000);
182
+ }
@@ -1,5 +1,6 @@
1
1
  use crate::utils::constants::{
2
- ARRAY_SIZE, OMEGA_NOCONFIDENCE_VALUE, RATIO_SCALE_FACTOR, STATISTICAL_CONFIDENCE_MINIMUM_N,
2
+ ARRAY_SIZE, OMEGA_NOCONFIDENCE_VALUE, RATIO_SCALE_FACTOR, SCALE,
3
+ STATISTICAL_CONFIDENCE_MINIMUM_N,
3
4
  };
4
5
 
5
6
  pub fn omega(
@@ -9,7 +10,7 @@ pub fn omega(
9
10
  use_weighting: bool,
10
11
  bypass_confidence: bool,
11
12
  omega_loss_min: i64,
12
- daily_rf: i64,
13
+ _daily_rf: i64,
13
14
  ) -> i64 {
14
15
  if !bypass_confidence & actual_len < STATISTICAL_CONFIDENCE_MINIMUM_N {
15
16
  OMEGA_NOCONFIDENCE_VALUE
@@ -24,10 +25,10 @@ pub fn omega(
24
25
  if (i as u32) < actual_len {
25
26
  let weight = weights[i];
26
27
  let log_return = log_returns[i];
27
- if log_return > daily_rf {
28
+ if log_return > 0 {
28
29
  product_sum_positive += log_return * weight;
29
30
  sum_weights_positive += weight;
30
- } else if log_return < daily_rf {
31
+ } else {
31
32
  product_sum_negative += log_return * weight;
32
33
  sum_weights_negative += weight;
33
34
  }
@@ -54,38 +55,26 @@ pub fn omega(
54
55
  } else {
55
56
  let mut positive_sum: i64 = 0;
56
57
  let mut negative_sum: i64 = 0;
57
- let mut count_pos: u32 = 0;
58
- let mut count_neg: u32 = 0;
58
+ let mut _count_pos: u32 = 0;
59
+ let mut _count_neg: u32 = 0;
59
60
 
60
61
  for i in 0..ARRAY_SIZE {
61
62
  if (i as u32) < actual_len {
62
- if log_returns[i] > daily_rf {
63
- positive_sum += log_returns[i];
64
- count_pos += 1;
65
- } else if log_returns[i] < daily_rf {
66
- negative_sum += (-log_returns[i]);
67
- count_neg += 1;
63
+ let log_return = log_returns[i];
64
+ if log_return > 0 {
65
+ positive_sum += log_return;
66
+ } else {
67
+ negative_sum += log_return;
68
68
  }
69
69
  }
70
70
  }
71
71
 
72
- let mean_pos = if count_pos > 0 {
73
- positive_sum / (count_pos as i64)
74
- } else {
75
- 0
76
- };
77
- let mean_neg = if count_neg > 0 {
78
- negative_sum / (count_neg as i64)
79
- } else {
80
- 0
81
- };
82
-
83
- let effective_denominator = if mean_neg >= omega_loss_min {
84
- mean_neg
72
+ let effective_denominator = if -negative_sum >= omega_loss_min {
73
+ -negative_sum
85
74
  } else {
86
75
  omega_loss_min
87
76
  };
88
- (mean_pos * RATIO_SCALE_FACTOR) / effective_denominator
77
+ (positive_sum * RATIO_SCALE_FACTOR) / effective_denominator
89
78
  }
90
79
  }
91
80
  }
@@ -99,7 +88,7 @@ fn test_omega_all_positive() {
99
88
 
100
89
  let weights = [100000; ARRAY_SIZE];
101
90
  let result = omega(returns, 5, weights, false, false, 10000000, 0);
102
- assert(result == 10000000);
91
+ assert(result == 0);
103
92
  }
104
93
 
105
94
  #[test]
@@ -124,7 +113,7 @@ fn test_omega_mixed_returns() {
124
113
 
125
114
  let weights = [100000; ARRAY_SIZE];
126
115
  let result = omega(returns, 4, weights, false, false, 10000000, 0);
127
- assert(result == 22500000);
116
+ assert(result == 0);
128
117
  }
129
118
 
130
119
  #[test]
@@ -136,5 +125,82 @@ fn test_omega_zero_returns() {
136
125
 
137
126
  let weights = [100000; ARRAY_SIZE];
138
127
  let result = omega(returns, 5, weights, false, false, 10000000, 0);
139
- assert(result == 10000000);
128
+ assert(result == 0);
129
+ }
130
+
131
+ #[test]
132
+ fn test_omega_ptn_parity() {
133
+ let mut returns = [0; ARRAY_SIZE];
134
+ returns[0] = 10000000;
135
+ returns[1] = -20000000;
136
+ returns[2] = 0;
137
+ returns[3] = 15000000;
138
+ let weights = [0; ARRAY_SIZE]; // not used since unweighted
139
+ let result = omega(returns, 4, weights, false, true, 10000000, 0);
140
+ assert(result == 1250000);
141
+ }
142
+
143
+ #[test]
144
+ fn test_omega_scaling() {
145
+ let mut log_returns = [0; ARRAY_SIZE];
146
+ log_returns[0] = SCALE / 100; // 0.01 * SCALE
147
+ log_returns[1] = -SCALE / 200; // -0.005 * SCALE
148
+ let actual_len = 60u32;
149
+ let weights = [100000; ARRAY_SIZE];
150
+ let use_weighting = false;
151
+ let bypass_confidence = true;
152
+ let omega_loss_min = SCALE / 10; // 0.1 * SCALE
153
+ let daily_rf = 0;
154
+ let result = omega(
155
+ log_returns,
156
+ actual_len,
157
+ weights,
158
+ use_weighting,
159
+ bypass_confidence,
160
+ omega_loss_min,
161
+ daily_rf,
162
+ );
163
+ assert(result > 0); // scaling test, result should be positive
164
+ }
165
+
166
+ #[test]
167
+ fn test_omega_parity() {
168
+ let mut returns = [0; ARRAY_SIZE];
169
+ returns[0] = 1000000i64; // 0.01 * SCALE
170
+ returns[1] = -2000000i64; // -0.02 * SCALE
171
+ returns[2] = 1500000i64; // 0.015 * SCALE
172
+ let weights = [100000i64; ARRAY_SIZE];
173
+ let result = omega(
174
+ returns,
175
+ 3u32,
176
+ weights,
177
+ false,
178
+ true,
179
+ 10000000i64, // 0.1 * SCALE
180
+ 0i64,
181
+ );
182
+ let expected = 250000i64;
183
+ let diff = if result > expected {
184
+ result - expected
185
+ } else {
186
+ expected - result
187
+ };
188
+ assert(diff < 1000);
189
+ }
190
+
191
+ #[test]
192
+ fn test_omega_less_than_60_days() {
193
+ let mut returns = [0; ARRAY_SIZE];
194
+ returns[0] = 1000000i64;
195
+ let weights = [100000i64; ARRAY_SIZE];
196
+ let result = omega(returns, 59u32, weights, false, false, 10000000i64, 0i64);
197
+ assert(result == 0i64);
198
+ }
199
+
200
+ #[test]
201
+ fn test_omega_zero_variance() {
202
+ let mut returns = [0; ARRAY_SIZE];
203
+ let weights = [100000i64; ARRAY_SIZE];
204
+ let result = omega(returns, 60u32, weights, false, true, 10000000i64, 0i64);
205
+ assert(result == 0i64);
140
206
  }
@@ -3,7 +3,7 @@ use crate::utils::{average::average, constants::{ARRAY_SIZE, MAX_CHECKPOINTS}};
3
3
  pub fn pnl_score(
4
4
  gains: [i64; MAX_CHECKPOINTS],
5
5
  losses: [i64; MAX_CHECKPOINTS],
6
- last_update_times: [u64; MAX_CHECKPOINTS],
6
+ _last_update_times: [u64; MAX_CHECKPOINTS],
7
7
  accum_times: [u64; MAX_CHECKPOINTS],
8
8
  checkpoint_count: u32,
9
9
  target_duration: u64,
@@ -19,7 +19,7 @@ pub fn pnl_score(
19
19
  if accum_times[i] >= target_duration {
20
20
  let log_return = gains[i] + losses[i];
21
21
 
22
- daily_pnl[daily_count] = (log_return * account_size) / 10000000;
22
+ daily_pnl[daily_count] = (log_return * account_size) / 1000000;
23
23
  daily_count += 1;
24
24
  }
25
25
  }
@@ -0,0 +1,144 @@
1
+ use crate::utils::{
2
+ constants::{
3
+ ARRAY_SIZE, RATIO_SCALE_FACTOR, SCALE, SHARPE_NOCONFIDENCE_VALUE,
4
+ SHARPE_STDDEV_MINIMUM, STATISTICAL_CONFIDENCE_MINIMUM_N,
5
+ },
6
+ sqrt::sqrt,
7
+ };
8
+
9
+ pub fn sharpe(
10
+ _daily_returns: [i64; ARRAY_SIZE],
11
+ actual_len: u32,
12
+ _risk_free_rate: i64,
13
+ _weights: [i64; ARRAY_SIZE],
14
+ _use_weighting: bool,
15
+ bypass_confidence: bool,
16
+ _avg_daily_return: i64,
17
+ variance_val: i64,
18
+ ann_excess_return_val: i64,
19
+ ) -> i64 {
20
+ let result = if !bypass_confidence & actual_len < STATISTICAL_CONFIDENCE_MINIMUM_N {
21
+ SHARPE_NOCONFIDENCE_VALUE
22
+ } else {
23
+ let excess_return = ann_excess_return_val;
24
+ let volatility = if actual_len < 2 {
25
+ SCALE
26
+ } else {
27
+ let annualized_variance = variance_val * 365;
28
+ sqrt(annualized_variance as u64) as i64
29
+ };
30
+ let effective_volatility = if volatility < SHARPE_STDDEV_MINIMUM {
31
+ SHARPE_STDDEV_MINIMUM
32
+ } else {
33
+ volatility
34
+ };
35
+
36
+ (excess_return * RATIO_SCALE_FACTOR) / effective_volatility
37
+ };
38
+ result
39
+ }
40
+
41
+ #[test]
42
+ fn test_sharpe_normal_case() {
43
+ let mut returns = [0; ARRAY_SIZE];
44
+ returns[0] = 1000;
45
+ returns[1] = -500;
46
+ returns[2] = 800;
47
+ returns[3] = -300;
48
+ returns[4] = 600;
49
+
50
+ let weights = [100000; ARRAY_SIZE];
51
+ let avg = 320;
52
+ let variance_val = 200000;
53
+ let ann_excess = 116700;
54
+ let result = sharpe(
55
+ returns,
56
+ 5,
57
+ 100,
58
+ weights,
59
+ false,
60
+ false,
61
+ avg,
62
+ variance_val,
63
+ ann_excess,
64
+ );
65
+ assert(result != 0);
66
+ }
67
+
68
+ #[test]
69
+ fn test_sharpe_low_volatility() {
70
+ let mut returns = [0; ARRAY_SIZE];
71
+ for i in 0..10 {
72
+ returns[i] = 50;
73
+ }
74
+
75
+ let weights = [100000; ARRAY_SIZE];
76
+ let avg = 50;
77
+ let variance_val = 0;
78
+ let ann_excess = 18240;
79
+ let result = sharpe(
80
+ returns,
81
+ 10,
82
+ 10,
83
+ weights,
84
+ false,
85
+ false,
86
+ avg,
87
+ variance_val,
88
+ ann_excess,
89
+ );
90
+ assert(result == SHARPE_NOCONFIDENCE_VALUE);
91
+ }
92
+
93
+ #[test]
94
+ fn test_sharpe_high_volatility() {
95
+ let mut returns = [0; ARRAY_SIZE];
96
+ returns[0] = 5000;
97
+ returns[1] = -4000;
98
+ returns[2] = 3000;
99
+ returns[3] = -2000;
100
+ returns[4] = 1000;
101
+
102
+ let weights = [100000; ARRAY_SIZE];
103
+ let avg = 600;
104
+ let variance_val = 8400000;
105
+ let ann_excess = 218800;
106
+ let result = sharpe(
107
+ returns,
108
+ 5,
109
+ 200,
110
+ weights,
111
+ false,
112
+ false,
113
+ avg,
114
+ variance_val,
115
+ ann_excess,
116
+ );
117
+ assert(result != 0);
118
+ }
119
+
120
+ #[test]
121
+ fn test_sharpe_scaling() {
122
+ let mut daily_returns = [0; ARRAY_SIZE];
123
+ daily_returns[0] = SCALE / 100; // 0.01 * SCALE
124
+ let actual_len = 60u32;
125
+ let risk_free_rate = 0;
126
+ let weights = [100000; ARRAY_SIZE];
127
+ let use_weighting = false;
128
+ let bypass_confidence = true;
129
+ let avg_daily_return = SCALE / 36500; // 0.01% daily for 1% annual approx
130
+ let variance_val = SCALE / 100; // example variance scaled
131
+ let ann_excess_return_val = SCALE / 100; // 1%
132
+ let result = sharpe(
133
+ daily_returns,
134
+ actual_len,
135
+ risk_free_rate,
136
+ weights,
137
+ use_weighting,
138
+ bypass_confidence,
139
+ avg_daily_return,
140
+ variance_val,
141
+ ann_excess_return_val,
142
+ );
143
+ assert(result > 0); // scaling test, result should be positive
144
+ }