proof-of-portfolio 0.0.45__py3-none-any.whl → 0.0.46__py3-none-any.whl

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.
@@ -1,2 +1,2 @@
1
1
  # This file is auto-generated during build
2
- __version__ = "0.0.45"
2
+ __version__ = "0.0.46"
@@ -0,0 +1,390 @@
1
+ import math
2
+ import numpy as np
3
+ from scipy.stats import ttest_1samp
4
+ from typing import Union
5
+
6
+
7
+ class MinMetrics:
8
+ """
9
+ Minimal metrics implementation that matches the proprietary trading network
10
+ validator metrics calculations exactly. Used for comparing ZK circuit outputs
11
+ against Python calculations on the same input data.
12
+ """
13
+
14
+ # Constants from ValiConfig
15
+ WEIGHTED_AVERAGE_DECAY_RATE = 0.075
16
+ WEIGHTED_AVERAGE_DECAY_MIN = 0.15
17
+ WEIGHTED_AVERAGE_DECAY_MAX = 1.0
18
+ ANNUAL_RISK_FREE_PERCENTAGE = 4.19
19
+ ANNUAL_RISK_FREE_DECIMAL = ANNUAL_RISK_FREE_PERCENTAGE / 100
20
+ DAYS_IN_YEAR_CRYPTO = 365
21
+ DAYS_IN_YEAR_FOREX = 252
22
+
23
+ # Metric-specific constants
24
+ SHARPE_STDDEV_MINIMUM = 0.01
25
+ STATISTICAL_CONFIDENCE_MINIMUM_N = 60
26
+ SHARPE_NOCONFIDENCE_VALUE = -100
27
+ OMEGA_LOSS_MINIMUM = 0.01
28
+ OMEGA_NOCONFIDENCE_VALUE = -100
29
+ SORTINO_DOWNSIDE_MINIMUM = 0.01
30
+ SORTINO_NOCONFIDENCE_VALUE = -100
31
+ CALMAR_RATIO_CAP = 1000
32
+ CALMAR_NOCONFIDENCE_VALUE = -100
33
+ STATISTICAL_CONFIDENCE_NOCONFIDENCE_VALUE = -100
34
+
35
+ @staticmethod
36
+ def log_risk_free_rate(days_in_year: int) -> float:
37
+ if days_in_year is None or days_in_year <= 0:
38
+ return np.inf
39
+ return math.log(1 + MinMetrics.ANNUAL_RISK_FREE_DECIMAL) / days_in_year
40
+
41
+ @staticmethod
42
+ def weighting_distribution(
43
+ log_returns: Union[list[float], np.ndarray]
44
+ ) -> np.ndarray:
45
+ """
46
+ Returns the weighting distribution that decays from max_weight to min_weight
47
+ using the configured decay rate
48
+ """
49
+ max_weight = MinMetrics.WEIGHTED_AVERAGE_DECAY_MAX
50
+ min_weight = MinMetrics.WEIGHTED_AVERAGE_DECAY_MIN
51
+ decay_rate = MinMetrics.WEIGHTED_AVERAGE_DECAY_RATE
52
+
53
+ if len(log_returns) < 1:
54
+ return np.ones(0)
55
+
56
+ weighting_distribution_days = np.arange(0, len(log_returns))
57
+
58
+ # Calculate decay from max to min
59
+ weight_range = max_weight - min_weight
60
+ decay_values = min_weight + (
61
+ weight_range * np.exp(-decay_rate * weighting_distribution_days)
62
+ )
63
+
64
+ return decay_values[::-1][-len(log_returns) :]
65
+
66
+ @staticmethod
67
+ def average(
68
+ log_returns: Union[list[float], np.ndarray],
69
+ weighting=False,
70
+ indices: Union[list[int], None] = None,
71
+ ) -> float:
72
+ """
73
+ Returns the mean of the log returns
74
+ """
75
+ if len(log_returns) == 0:
76
+ return 0.0
77
+
78
+ weighting_distribution = MinMetrics.weighting_distribution(log_returns)
79
+
80
+ if indices is not None and len(indices) != 0:
81
+ indices = [i for i in indices if i in range(len(log_returns))]
82
+ log_returns = [log_returns[i] for i in indices]
83
+ weighting_distribution = [weighting_distribution[i] for i in indices]
84
+
85
+ if weighting:
86
+ avg_value = np.average(log_returns, weights=weighting_distribution)
87
+ else:
88
+ avg_value = np.mean(log_returns)
89
+
90
+ return float(avg_value)
91
+
92
+ @staticmethod
93
+ def variance(
94
+ log_returns: list[float],
95
+ ddof: int = 1,
96
+ weighting=False,
97
+ indices: Union[list[int], None] = None,
98
+ ) -> float:
99
+ """
100
+ Returns the variance of the log returns
101
+ """
102
+ if len(log_returns) == 0:
103
+ return 0.0
104
+
105
+ window = len(indices) if indices is not None else len(log_returns)
106
+ if window < ddof + 1:
107
+ return np.inf
108
+
109
+ return MinMetrics.average(
110
+ (
111
+ np.array(log_returns)
112
+ - MinMetrics.average(log_returns, weighting=weighting, indices=indices)
113
+ )
114
+ ** 2,
115
+ weighting=weighting,
116
+ indices=indices,
117
+ )
118
+
119
+ @staticmethod
120
+ def ann_excess_return(
121
+ log_returns: list[float],
122
+ weighting=False,
123
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
124
+ ) -> float:
125
+ """
126
+ Calculates annualized excess return using mean daily log returns and mean daily 1yr risk free rate.
127
+ """
128
+ annual_risk_free_rate = MinMetrics.ANNUAL_RISK_FREE_DECIMAL
129
+
130
+ if len(log_returns) == 0:
131
+ return 0.0
132
+
133
+ # Annualize the mean daily excess returns
134
+ annualized_excess_return = (
135
+ MinMetrics.average(log_returns, weighting=weighting) * days_in_year
136
+ ) - annual_risk_free_rate
137
+ return annualized_excess_return
138
+
139
+ @staticmethod
140
+ def ann_volatility(
141
+ log_returns: list[float],
142
+ ddof: int = 1,
143
+ weighting=False,
144
+ indices: list[int] = None,
145
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
146
+ ) -> float:
147
+ """
148
+ Calculates annualized volatility ASSUMING DAILY OBSERVATIONS
149
+ """
150
+ if indices is None:
151
+ indices = list(range(len(log_returns)))
152
+
153
+ # Annualize volatility of the daily log returns assuming sample variance
154
+ window = len(indices)
155
+ if window < ddof + 1:
156
+ return np.inf
157
+
158
+ annualized_volatility = np.sqrt(
159
+ MinMetrics.variance(
160
+ log_returns, ddof=ddof, weighting=weighting, indices=indices
161
+ )
162
+ * days_in_year
163
+ )
164
+
165
+ return float(annualized_volatility)
166
+
167
+ @staticmethod
168
+ def ann_downside_volatility(
169
+ log_returns: list[float],
170
+ target: int = None,
171
+ weighting=False,
172
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
173
+ ) -> float:
174
+ """
175
+ Calculates annualized downside volatility
176
+ """
177
+ if target is None:
178
+ target = MinMetrics.log_risk_free_rate(days_in_year=days_in_year)
179
+
180
+ indices = [i for i, log_return in enumerate(log_returns) if log_return < target]
181
+ return MinMetrics.ann_volatility(
182
+ log_returns, weighting=weighting, indices=indices, days_in_year=days_in_year
183
+ )
184
+
185
+ @staticmethod
186
+ def daily_max_drawdown(log_returns: list[float]) -> float:
187
+ """
188
+ Calculates the daily maximum drawdown
189
+ """
190
+ if len(log_returns) == 0:
191
+ return 0.0
192
+
193
+ # More efficient implementation using cumulative sum of log returns
194
+ cumulative_log_returns = np.cumsum(log_returns)
195
+
196
+ # Maximum cumulative log return at each point
197
+ running_max_log = np.maximum.accumulate(cumulative_log_returns)
198
+
199
+ # Drawdown = 1 - exp(current - peak)
200
+ # This gives us the percentage decline from the peak
201
+ drawdowns = 1 - np.exp(cumulative_log_returns - running_max_log)
202
+
203
+ # Find the maximum drawdown
204
+ max_drawdown = np.max(drawdowns)
205
+
206
+ return max_drawdown
207
+
208
+ @staticmethod
209
+ def sharpe(
210
+ log_returns: list[float],
211
+ bypass_confidence: bool = False,
212
+ weighting: bool = False,
213
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
214
+ **kwargs,
215
+ ) -> float:
216
+ """
217
+ Calculates the Sharpe ratio
218
+ """
219
+ # Need a large enough sample size
220
+ if len(log_returns) < MinMetrics.STATISTICAL_CONFIDENCE_MINIMUM_N:
221
+ if not bypass_confidence:
222
+ return MinMetrics.SHARPE_NOCONFIDENCE_VALUE
223
+
224
+ # Hyperparameter
225
+ min_std_dev = MinMetrics.SHARPE_STDDEV_MINIMUM
226
+
227
+ excess_return = MinMetrics.ann_excess_return(
228
+ log_returns, weighting=weighting, days_in_year=days_in_year
229
+ )
230
+ volatility = MinMetrics.ann_volatility(
231
+ log_returns, weighting=weighting, days_in_year=days_in_year
232
+ )
233
+
234
+ return float(excess_return / max(volatility, min_std_dev))
235
+
236
+ @staticmethod
237
+ def omega(
238
+ log_returns: list[float],
239
+ bypass_confidence: bool = False,
240
+ weighting: bool = False,
241
+ **kwargs,
242
+ ) -> float:
243
+ """
244
+ Calculates the Omega ratio
245
+ """
246
+ # Need a large enough sample size
247
+ if len(log_returns) < MinMetrics.STATISTICAL_CONFIDENCE_MINIMUM_N:
248
+ if not bypass_confidence:
249
+ return MinMetrics.OMEGA_NOCONFIDENCE_VALUE
250
+
251
+ if weighting:
252
+ weighing_array = MinMetrics.weighting_distribution(log_returns)
253
+
254
+ positive_indices = []
255
+ negative_indices = []
256
+ product_sum_positive = product_sum_negative = 0
257
+ sum_of_weights_positive = sum_of_weights_negative = (
258
+ MinMetrics.OMEGA_LOSS_MINIMUM
259
+ )
260
+
261
+ for c, log_return in enumerate(log_returns):
262
+ if log_return > 0:
263
+ positive_indices.append(c)
264
+ else:
265
+ negative_indices.append(c)
266
+
267
+ log_return_arr = np.array(log_returns)
268
+
269
+ if len(positive_indices) > 0:
270
+ positive_indices_arr = np.array(positive_indices)
271
+ sum_of_weights_positive = max(
272
+ np.sum(weighing_array[positive_indices_arr]),
273
+ MinMetrics.OMEGA_LOSS_MINIMUM,
274
+ )
275
+
276
+ product_sum_positive = np.sum(
277
+ np.multiply(
278
+ log_return_arr[positive_indices_arr],
279
+ weighing_array[positive_indices_arr],
280
+ )
281
+ )
282
+
283
+ if len(negative_indices) > 0:
284
+ negative_indices_arr = np.array(negative_indices)
285
+ sum_of_weights_negative = max(
286
+ np.sum(weighing_array[negative_indices_arr]),
287
+ MinMetrics.OMEGA_LOSS_MINIMUM,
288
+ )
289
+ product_sum_negative = np.sum(
290
+ np.multiply(
291
+ log_return_arr[negative_indices_arr],
292
+ weighing_array[negative_indices_arr],
293
+ )
294
+ )
295
+
296
+ positive_sum = product_sum_positive * sum_of_weights_negative
297
+ negative_sum = product_sum_negative * sum_of_weights_positive
298
+
299
+ else:
300
+ positive_sum = 0
301
+ negative_sum = 0
302
+
303
+ for log_return in log_returns:
304
+ if log_return > 0:
305
+ positive_sum += log_return
306
+ else:
307
+ negative_sum += log_return
308
+
309
+ numerator = positive_sum
310
+ denominator = max(abs(negative_sum), MinMetrics.OMEGA_LOSS_MINIMUM)
311
+
312
+ return float(numerator / denominator)
313
+
314
+ @staticmethod
315
+ def statistical_confidence(
316
+ log_returns: list[float], bypass_confidence: bool = False, **kwargs
317
+ ) -> float:
318
+ """
319
+ Calculates statistical confidence using t-test
320
+ """
321
+ # Impose a minimum sample size on the miner
322
+ if len(log_returns) < MinMetrics.STATISTICAL_CONFIDENCE_MINIMUM_N:
323
+ if not bypass_confidence or len(log_returns) < 2:
324
+ return MinMetrics.STATISTICAL_CONFIDENCE_NOCONFIDENCE_VALUE
325
+
326
+ # Also now check for zero variance condition
327
+ zero_variance_condition = bool(np.isclose(np.var(log_returns), 0))
328
+ if zero_variance_condition:
329
+ return MinMetrics.STATISTICAL_CONFIDENCE_NOCONFIDENCE_VALUE
330
+
331
+ res = ttest_1samp(log_returns, 0, alternative="greater")
332
+ return float(res.statistic)
333
+
334
+ @staticmethod
335
+ def sortino(
336
+ log_returns: list[float],
337
+ bypass_confidence: bool = False,
338
+ weighting: bool = False,
339
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
340
+ **kwargs,
341
+ ) -> float:
342
+ """
343
+ Calculates the Sortino ratio
344
+ """
345
+ # Need a large enough sample size
346
+ if len(log_returns) < MinMetrics.STATISTICAL_CONFIDENCE_MINIMUM_N:
347
+ if not bypass_confidence:
348
+ return MinMetrics.SORTINO_NOCONFIDENCE_VALUE
349
+
350
+ # Hyperparameter
351
+ min_downside = MinMetrics.SORTINO_DOWNSIDE_MINIMUM
352
+
353
+ # Sortino ratio is calculated as the mean of the returns divided by the standard deviation of the negative returns
354
+ excess_return = MinMetrics.ann_excess_return(
355
+ log_returns, weighting=weighting, days_in_year=days_in_year
356
+ )
357
+ downside_volatility = MinMetrics.ann_downside_volatility(
358
+ log_returns, weighting=weighting, days_in_year=days_in_year
359
+ )
360
+
361
+ return float(excess_return / max(downside_volatility, min_downside))
362
+
363
+ @staticmethod
364
+ def calmar(
365
+ log_returns: list[float],
366
+ bypass_confidence: bool = False,
367
+ weighting: bool = False,
368
+ days_in_year: int = DAYS_IN_YEAR_CRYPTO,
369
+ **kwargs,
370
+ ) -> float:
371
+ """
372
+ Calculates the Calmar ratio (simplified version without ledger dependency)
373
+ """
374
+ # Positional Component
375
+ if len(log_returns) < 30: # Simplified confidence check
376
+ if not bypass_confidence:
377
+ return MinMetrics.CALMAR_NOCONFIDENCE_VALUE
378
+
379
+ base_return_percentage = (
380
+ MinMetrics.average(log_returns, weighting=weighting) * days_in_year * 100
381
+ )
382
+ max_drawdown = MinMetrics.daily_max_drawdown(log_returns)
383
+
384
+ # Simplified risk normalization factor (without full ledger context)
385
+ drawdown_normalization_factor = 1.0 / max(max_drawdown, 0.01)
386
+
387
+ raw_calmar = float(base_return_percentage * drawdown_normalization_factor)
388
+ return min(
389
+ raw_calmar, MinMetrics.CALMAR_RATIO_CAP
390
+ ) # Cap the Calmar ratio to prevent extreme values
@@ -3,6 +3,8 @@ import toml
3
3
  import re
4
4
  import os
5
5
  import time
6
+ from .min_metrics import MinMetrics
7
+ import math
6
8
 
7
9
  # Constants for the circuit
8
10
  MAX_CHECKPOINTS = 200
@@ -570,6 +572,46 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
570
572
 
571
573
  prove_time, verification_success = run_bb_prove(main_circuit_dir)
572
574
 
575
+ # Calculate MinMetrics (Python) for comparison with ZK circuit
576
+ try:
577
+
578
+ # Extract daily log returns from checkpoint data (same as ZK circuit)
579
+ daily_log_returns = []
580
+ for cp in cps:
581
+ if cp["gain"] > 0:
582
+ daily_log_returns.append(math.log(1 + cp["gain"]))
583
+ elif cp["loss"] > 0:
584
+ daily_log_returns.append(math.log(1 - cp["loss"]))
585
+ else:
586
+ daily_log_returns.append(0.0)
587
+
588
+ if len(daily_log_returns) > 0:
589
+ python_avg_daily_pnl = MinMetrics.average(daily_log_returns)
590
+ python_sharpe = MinMetrics.sharpe(daily_log_returns, bypass_confidence=True)
591
+ python_max_drawdown = MinMetrics.daily_max_drawdown(daily_log_returns)
592
+ python_calmar = MinMetrics.calmar(daily_log_returns, bypass_confidence=True)
593
+ python_omega = MinMetrics.omega(daily_log_returns, bypass_confidence=True)
594
+ python_sortino = MinMetrics.sortino(
595
+ daily_log_returns, bypass_confidence=True
596
+ )
597
+ python_stat_confidence = MinMetrics.statistical_confidence(
598
+ daily_log_returns, bypass_confidence=True
599
+ )
600
+ else:
601
+ python_avg_daily_pnl = python_sharpe = python_max_drawdown = (
602
+ python_calmar
603
+ ) = python_omega = python_sortino = python_stat_confidence = 0.0
604
+
605
+ except Exception as e:
606
+ if verbose:
607
+ print(f"Warning: Could not calculate Python MinMetrics: {e}")
608
+ import traceback
609
+
610
+ print(f"Traceback: {traceback.format_exc()}")
611
+ python_avg_daily_pnl = python_sharpe = python_max_drawdown = python_calmar = (
612
+ python_omega
613
+ ) = python_sortino = python_stat_confidence = "N/A"
614
+
573
615
  # Always print key production info: hotkey and verification status
574
616
  print(f"Hotkey: {miner_hotkey}")
575
617
  print(f"Orders processed: {signals_count}")
@@ -582,10 +624,41 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
582
624
  print(f"Omega Ratio: {omega_ratio_scaled:.9f}")
583
625
  print(f"Sortino Ratio: {sortino_ratio_scaled:.9f}")
584
626
  print(f"Statistical Confidence: {stat_confidence_scaled:.9f}")
585
- if prove_time is not None:
627
+
628
+ # Print Python MinMetrics for comparison
629
+ print("\n--- PYTHON MINMETRICS ---")
630
+ if isinstance(python_avg_daily_pnl, (int, float)):
631
+ print(f"Average Daily PnL: {python_avg_daily_pnl:.9f}")
632
+ else:
633
+ print(f"Average Daily PnL: {python_avg_daily_pnl}")
634
+ if isinstance(python_sharpe, (int, float)):
635
+ print(f"Sharpe Ratio: {python_sharpe:.9f}")
636
+ else:
637
+ print(f"Sharpe Ratio: {python_sharpe}")
638
+ if isinstance(python_max_drawdown, (int, float)):
586
639
  print(
587
- f"Proof verification: {'✅ PASSED' if verification_success else '❌ FAILED'}"
640
+ f"Max Drawdown: {python_max_drawdown:.9f} ({python_max_drawdown * 100:.6f}%)"
588
641
  )
642
+ else:
643
+ print(f"Max Drawdown: {python_max_drawdown}")
644
+ if isinstance(python_calmar, (int, float)):
645
+ print(f"Calmar Ratio: {python_calmar:.9f}")
646
+ else:
647
+ print(f"Calmar Ratio: {python_calmar}")
648
+ if isinstance(python_omega, (int, float)):
649
+ print(f"Omega Ratio: {python_omega:.9f}")
650
+ else:
651
+ print(f"Omega Ratio: {python_omega}")
652
+ if isinstance(python_sortino, (int, float)):
653
+ print(f"Sortino Ratio: {python_sortino:.9f}")
654
+ else:
655
+ print(f"Sortino Ratio: {python_sortino}")
656
+ if isinstance(python_stat_confidence, (int, float)):
657
+ print(f"Statistical Confidence: {python_stat_confidence:.9f}")
658
+ else:
659
+ print(f"Statistical Confidence: {python_stat_confidence}")
660
+ if prove_time is not None:
661
+ print(f"Proof generated in {prove_time}s")
589
662
  else:
590
663
  print("Proof generation failed")
591
664
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proof-of-portfolio
3
- Version: 0.0.45
3
+ Version: 0.0.46
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,10 +1,11 @@
1
1
  proof_of_portfolio/__init__.py,sha256=ymGh1kUyiNXBTZr3pzs34E9ndABq_knWBIW3Dl5tdEI,3392
2
- proof_of_portfolio/_version.py,sha256=ox0oMfi5CduaU3I9Tx5cfYVIE5VXC6ZPUoY23fVdhUc,66
2
+ proof_of_portfolio/_version.py,sha256=YNLk_XSAS-D_aou6fYlpkBAfEjEeGkCHoRwPe0QHaE4,66
3
3
  proof_of_portfolio/analyze_data.py,sha256=t80ueFtBUzcWTrPiamiC1HXvw5Em-151zXJx0cCk8sQ,3709
4
4
  proof_of_portfolio/main.py,sha256=JbvdAK7B6hw2sQdjT0EVCiglCQZgN3urKphdeWcW43w,30994
5
+ proof_of_portfolio/min_metrics.py,sha256=BAEpJdqYoR6RjkqX-M5BUTWlurSADvxAHuLFznH3AT8,13196
5
6
  proof_of_portfolio/miner.py,sha256=eGXcPMRQVAepzXJj1ePbbDAf-72E9Yj9n-yfP7GohLw,17177
6
7
  proof_of_portfolio/post_install.py,sha256=e-57Z_h-svQdjA8ibsM985MXmdV6-KLyymQm3Cgu8YM,5654
7
- proof_of_portfolio/proof_generator.py,sha256=9j1EQ80qP8h63L26jj18J5wnyH9QSsI_N3T4VFy9Y4M,24213
8
+ proof_of_portfolio/proof_generator.py,sha256=U0ZXkoKJasj-i6reCCx8TEDXcmzjsetkV48KhTTao6Q,27317
8
9
  proof_of_portfolio/signal_processor.py,sha256=JQjnkMJEbv_MWIgKNrjKjrBIcIT5pAkAlCneEOGsqT0,6702
9
10
  proof_of_portfolio/validator.py,sha256=kt3BeaXOffv-h52PZBKV1YHUWtiGsnPte16m3EJpITY,3839
10
11
  proof_of_portfolio/circuits/Nargo.toml,sha256=D6ycN7H3xiTcWHH5wz4qXYTXn7Ht0WgPr9w4B7d8ZGw,141
@@ -79,8 +80,8 @@ proof_of_portfolio/tree_generator/Nargo.toml,sha256=O6iSvb-EpV0XcETiDxNgSp7XKNiY
79
80
  proof_of_portfolio/tree_generator/target.gz,sha256=7LPzAb8JvKWSDOzyW5vI_6NKQ0aB9cb31q4CWbchFSw,308614
80
81
  proof_of_portfolio/tree_generator/src/main.nr,sha256=zHG_0OphqSROyeVc7yTSEULg4bYS8B-LsmvTzTl8aW4,2393
81
82
  proof_of_portfolio/tree_generator/target/tree_generator.json,sha256=M_bI5et5ncgILJ_Czen4ZsGfWHwComEVxMLQpIWmN1k,1540889
82
- proof_of_portfolio-0.0.45.dist-info/METADATA,sha256=hQ5SDJlOD608p8mmdP7lDdUe-wlz9eCPT-Fjddn8j0E,7051
83
- proof_of_portfolio-0.0.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
- proof_of_portfolio-0.0.45.dist-info/entry_points.txt,sha256=KeLSJT_UJtr1WiLTkhlGqWQuPKbO_ylgj6OXOC2gJV4,53
85
- proof_of_portfolio-0.0.45.dist-info/top_level.txt,sha256=sY5xZnE6YuiISK1IuRHPfl71NV0vXO3N3YA2li_SPXU,19
86
- proof_of_portfolio-0.0.45.dist-info/RECORD,,
83
+ proof_of_portfolio-0.0.46.dist-info/METADATA,sha256=iqs4Yca9IjQhK2o2dIc1t8AHLvqnUJULB1BO6Ifuzsc,7051
84
+ proof_of_portfolio-0.0.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
+ proof_of_portfolio-0.0.46.dist-info/entry_points.txt,sha256=KeLSJT_UJtr1WiLTkhlGqWQuPKbO_ylgj6OXOC2gJV4,53
86
+ proof_of_portfolio-0.0.46.dist-info/top_level.txt,sha256=sY5xZnE6YuiISK1IuRHPfl71NV0vXO3N3YA2li_SPXU,19
87
+ proof_of_portfolio-0.0.46.dist-info/RECORD,,