pivotalpath 1.0.0__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.
@@ -0,0 +1,65 @@
1
+ """
2
+ PivotalPath: The Industry Standard for Hedge Fund INDEX Analytics
3
+
4
+ Analyzes hedge fund indices - composite benchmarks tracking groups of hedge funds.
5
+ """
6
+
7
+ from .version import __version__
8
+
9
+ # Core functionality
10
+ from .core.main_library import (
11
+ get_catalog,
12
+ get_returns,
13
+ get_info,
14
+ get_asset_list,
15
+ get_stats,
16
+ get_stats_list,
17
+ )
18
+
19
+ # Important tickers and examples
20
+ from .core import important_tickers
21
+ from .core import examples
22
+
23
+ # Stats catalog
24
+ from .stats.catalog import stats_catalog
25
+
26
+ # LLM-optimized interface for hedge fund indices
27
+ try:
28
+ from .llm.interface import (
29
+ quick_index_stats,
30
+ analyze_hedge_fund_index,
31
+ compare_hedge_fund_indices,
32
+ list_hedge_fund_indices,
33
+ list_available_metrics,
34
+ generate_index_report,
35
+ )
36
+ LLM_AVAILABLE = True
37
+ except ImportError as e:
38
+ print(f"LLM interface import failed: {e}")
39
+ LLM_AVAILABLE = False
40
+
41
+ # Export everything
42
+ __all__ = [
43
+ "__version__",
44
+ "get_catalog",
45
+ "get_returns",
46
+ "get_info",
47
+ "get_asset_list",
48
+ "get_stats",
49
+ "get_stats_list",
50
+ "stats_catalog",
51
+ "important_tickers",
52
+ "examples",
53
+ "LLM_AVAILABLE",
54
+ ]
55
+
56
+ # Add LLM functions if available
57
+ if LLM_AVAILABLE:
58
+ __all__.extend([
59
+ "quick_index_stats",
60
+ "analyze_hedge_fund_index",
61
+ "compare_hedge_fund_indices",
62
+ "list_hedge_fund_indices",
63
+ "list_available_metrics",
64
+ "generate_index_report",
65
+ ])
File without changes
@@ -0,0 +1,24 @@
1
+ """Core functionality for hedge fund analysis."""
2
+
3
+ from .main_library import (
4
+ get_catalog,
5
+ get_returns,
6
+ get_info,
7
+ get_asset_list,
8
+ get_stats,
9
+ get_stats_list,
10
+ )
11
+
12
+ from . import important_tickers
13
+ from . import examples
14
+
15
+ __all__ = [
16
+ "get_catalog",
17
+ "get_returns",
18
+ "get_info",
19
+ "get_asset_list",
20
+ "get_stats",
21
+ "get_stats_list",
22
+ "important_tickers",
23
+ "examples",
24
+ ]
@@ -0,0 +1,86 @@
1
+ 
2
+ from . import main_library as mli
3
+ from . import important_tickers as it
4
+
5
+ """
6
+ USECASES AND EXAMPLES OF THE MAIN LIBRARY FUNCTIONS
7
+
8
+ """
9
+
10
+ """
11
+ example1: example of tickers
12
+ """
13
+ def example1():
14
+ return [it.market_ticker, it.riskfree_ticker, it.hedge_fund_composite_index_ticker]
15
+
16
+ """
17
+ example2: hedge fund index catalog
18
+ """
19
+ def example2():
20
+ return mli.get_catalog(asset_class="index")
21
+
22
+ """
23
+ example3: list all hedge fund index tickers
24
+ """
25
+ def example3():
26
+ return mli.get_asset_list(asset_class="index")
27
+
28
+ """
29
+ example4: list all hedge fund index names
30
+ """
31
+ def example4():
32
+ return mli.get_asset_list(asset_class="index", select="name")
33
+ """
34
+ example5: market returns
35
+ """
36
+ def example5():
37
+ return mli.get_returns(ticker=it.market_ticker)
38
+
39
+ """
40
+ example6: list available stats tickers
41
+ """
42
+ def example6():
43
+ return mli.get_stats_list()
44
+ """
45
+ example7: list available stats names
46
+ """
47
+ def example7():
48
+ return mli.get_stats_list(select="name")
49
+ """
50
+ example8: example stats for first 2 hedge fund indices
51
+ """
52
+ def example8():
53
+ return mli.get_stats(
54
+ target=mli.get_asset_list(asset_class="index")[:2],
55
+ base=it.market_ticker,
56
+ start="2020-01",
57
+ end="2024-12",
58
+ stat=['annret','annvol','sharpe']
59
+ )
60
+
61
+ """
62
+ example9: example stats with custom labels
63
+ """
64
+ def example9():
65
+ return mli.get_stats(
66
+ target=mli.get_asset_list(asset_class='index')+['SP500'],
67
+ base=it.market_ticker,
68
+ start="2020-01",
69
+ end="2024-12",
70
+ stat=mli.get_stats_list(),
71
+ label_as="shortname",
72
+ )
73
+ """
74
+ example10: example structured stats
75
+ """
76
+ def example10():
77
+ return mli.get_stats(
78
+ target=mli.get_asset_list(asset_class='index'),
79
+ base=it.market_ticker,
80
+ start="2020-01",
81
+ end="2024-12",
82
+ stat=['sharpe','annvol'],
83
+ label_as="name+period",
84
+ index_as="name",
85
+ )
86
+
@@ -0,0 +1,3 @@
1
+ market_ticker="SP500"
2
+ riskfree_ticker="TBILL3M"
3
+ hedge_fund_composite_index_ticker="PP-HFC"
@@ -0,0 +1,47 @@
1
+ 
2
+ from ..data import get_catalog, get_returns, get_info
3
+ from ..stats import stats_calculator,stats_catalog
4
+ import pandas as pd
5
+
6
+ def _notdf(df):
7
+ return not(df is None) and not (isinstance(df,pd.DataFrame))
8
+
9
+ def get_stats(
10
+ target=None,
11
+ base=None,
12
+ riskfree=None,
13
+ index_as="ticker",
14
+ transpose=False,
15
+ **kwargs
16
+ ):
17
+ if _notdf(target):
18
+ target=get_returns(ticker=target)
19
+ if _notdf(base):
20
+ base=get_returns(ticker=base)
21
+ if _notdf(riskfree):
22
+ riskfree=get_returns(ticker=riskfree)
23
+ return pd.DataFrame()
24
+ df=stats_calculator(target=target,base=base,riskfree=riskfree,**kwargs)
25
+ if isinstance(index_as,str) and index_as!="ticker":
26
+ try:
27
+ df_info=get_info(ticker=df.index.tolist())
28
+ if isinstance(df_info,pd.DataFrame) and not (df_info.empty):
29
+ info_map=df_info.set_index("ticker")[index_as].to_dict()
30
+ df.index=[info_map.get(_,_) for _ in df.index.tolist()]
31
+ df.index=df[index_as]
32
+ df.index.name=index_as
33
+ except:
34
+ pass
35
+ if transpose:
36
+ df=df.T
37
+ return df
38
+
39
+
40
+ def get_stats_list(select="ticker")->list:
41
+ return [_.get(select) for _ in stats_catalog]
42
+
43
+ def get_asset_list(asset_class="index",select="ticker")->list:
44
+ df=get_catalog(asset_class=asset_class,select=select)
45
+ if isinstance(df,pd.DataFrame) and not (df.empty):
46
+ return df[select].tolist()
47
+ return []
@@ -0,0 +1,115 @@
1
+ 
2
+ import pandas as pd
3
+ from .api import GetFilteredDataFrame
4
+ from . import utils as tsutil
5
+
6
+ def _ticker2class(ticker):
7
+ if isinstance(ticker,list):
8
+ return [_ticker2class(_) for _ in ticker]
9
+ else:
10
+ # tba: add confirmation
11
+ if ticker.startswith("PP"):
12
+ return "index"
13
+ else:
14
+ return "factor"
15
+
16
+ def _ticker2map(ticker=[]):
17
+ out=dict(
18
+ index=[_ for _ in ticker if _.startswith("PP")],
19
+ factor=[_ for _ in ticker if not (_.startswith("PP"))]
20
+ )
21
+ return {_:__ for _,__ in out.items() if __}
22
+
23
+
24
+ """
25
+ main functions
26
+ """
27
+
28
+ def get_catalog(asset_class="index",filter_by=dict(),select="*")->pd.DataFrame:
29
+ if isinstance(asset_class,list):
30
+ df=pd.DataFrame()
31
+ for _ in asset_class:
32
+ df_i=get_catalog(asset_class=_,filter_by=filter_by,select="*")
33
+ df=pd.concat([df,df_i]).set_index("ticker")
34
+ return df
35
+ table_name=f"{asset_class}_catalog"
36
+ return GetFilteredDataFrame(table_name,filter_by=filter_by)
37
+
38
+
39
+ def get_info(ticker=None):
40
+
41
+ if not ticker:
42
+ return {}
43
+
44
+ if isinstance(ticker,str):
45
+ asset_class=_ticker2class(ticker)
46
+ table_name=f"{asset_class}_catalog"
47
+ return GetFilteredDataFrame(table_name,filter_by=dict(ticker=ticker)).iloc[0].to_dict()
48
+
49
+ if isinstance(ticker,list):
50
+ tickermap=_ticker2map(ticker)
51
+ out=pd.DataFrame()
52
+ for asset_i,ticker_i in tickermap.items():
53
+ table_name=f"{asset_i}_catalog"
54
+ df_i=GetFilteredDataFrame(table_name, filter_by=dict(ticker=ticker_i))
55
+ if isinstance(df_i,pd.DataFrame) and not (df_i.empty):
56
+ out=pd.concat([out,df_i],axis=0)
57
+ return out
58
+
59
+
60
+ def get_returns(
61
+ ticker=[],
62
+ month_list=[],
63
+ start=None,
64
+ end=None,
65
+ lookback=None,
66
+ column_label="ticker",
67
+ freq="M",
68
+ freq_aggregation="compounded",
69
+ annualized=False,
70
+ rounding_places=6,
71
+ pivot=True,
72
+
73
+ )->pd.DataFrame:
74
+
75
+ """
76
+ Get returns data for specified tickers.
77
+
78
+ Returns:
79
+ pd.DataFrame or None: DataFrame with returns data, or None if no ticker provided
80
+ """
81
+ if not ticker:
82
+ return pd.DataFrame()
83
+ if isinstance(ticker,str):
84
+ ticker=[ticker]
85
+ tickermap=_ticker2map(ticker=ticker)
86
+ if not month_list:
87
+ month_list=tsutil.build_month_list(start=start,end=end,lookback=lookback)
88
+ df=pd.DataFrame()
89
+ for asset_i, ticker_i in tickermap.items():
90
+ table_name=f"{asset_i}_returns"
91
+ rows=GetFilteredDataFrame(table_name, filter_by=dict(ticker=ticker_i))
92
+ if start:
93
+ rows=rows[rows['month']>=start]
94
+ if end:
95
+ rows=rows[rows['month']<=end]
96
+
97
+ if df.empty:
98
+ df=rows
99
+ else:
100
+ df=pd.concat([df,rows])
101
+
102
+ df=df[df['ticker'].isin(ticker)]
103
+ if freq!="M":
104
+ pivot=True
105
+
106
+ if pivot:
107
+ df=df.pivot_table(index='month', columns='ticker', values='return')
108
+ df=df[[_ for _ in ticker if _ in df.columns]]
109
+
110
+ if freq!="M":
111
+ df=tsutil.frequency_aggregation(df, freq=freq,freq_aggregation=freq_aggregation,annualized=annualized)
112
+
113
+ return round(df,rounding_places)
114
+
115
+
@@ -0,0 +1,35 @@
1
+
2
+ import pandas as pd
3
+ import warnings
4
+ import requests
5
+
6
+ class ApiCaller:
7
+
8
+ base_url = "http://apps.pivotalpath.com/p3ypi/resources/api/v1/"
9
+ api_key = "p3k_7H9mK2pL8qX3nR5vW1cY4jZ6sA0uE9"
10
+
11
+ @classmethod
12
+ def get_headers(cls):
13
+ return {
14
+ "Authorization": f"Bearer {cls.api_key}",
15
+ "Content-Type": "application/json"
16
+ }
17
+
18
+ @classmethod
19
+ def get_data(cls,endpoint=None,params=dict()):
20
+ url=cls.base_url+endpoint
21
+ headers=cls.get_headers()
22
+ response=requests.get(url,headers=headers,params=params)
23
+ if response.status_code==200:
24
+ try:
25
+ return pd.DataFrame(response.json())
26
+ except ValueError:
27
+ warnings.warn("Failed to parse JSON response")
28
+ return pd.DataFrame()
29
+ else:
30
+ warnings.warn(f"API call failed with status code {response.status_code}")
31
+ return pd.DataFrame()
32
+
33
+
34
+ def GetFilteredDataFrame(table_name=None, filter_by=dict())->pd.DataFrame:
35
+ return ApiCaller.get_data(endpoint=table_name,params=filter_by)
@@ -0,0 +1,40 @@
1
+ import pandas as pd
2
+
3
+ def _do_compound(df):
4
+ return (1 +df).prod(min_count=1) - 1
5
+ def _do_sum(df):
6
+ return df.sum()
7
+ def _compute_default_end():
8
+ return (pd.Timestamp.today()-pd.DateOffset(months=1)).strftime("%Y-%m")
9
+ def _compute_default_start():
10
+ return "1998-01"
11
+
12
+
13
+ def build_month_list(start=None,end=None,lookback=None, _default_start="1998-01",_default_rule="rule_of_7"):
14
+ if not end:
15
+ end=_compute_default_end()
16
+ if not start:
17
+ if lookback:
18
+ start=(pd.Timestamp(end)-pd.DateOffset(months=lookback-1)).strftime("%Y-%m")
19
+ else:
20
+ start=_compute_default_start()
21
+ return [_.strftime("%Y-%m") for _ in pd.period_range(start=start,end=end,freq="M")]
22
+
23
+ def timeseries_aggregation(df,freq="Q",freq_aggregation="compounded",annualized=False,rounding_places=6):
24
+ if not isinstance(df.index, pd.DatetimeIndex):
25
+ df.index = pd.to_datetime(df.index, format='%Y-%m')
26
+ df_agg=df.resample(freq)
27
+ if freq_aggregation=="compounded":
28
+ out=df_agg.apply(_do_compound)
29
+ if annualized:
30
+ out=(1+out)**4-1
31
+ elif freq_aggregation=="sum":
32
+ out= df_agg.apply(_do_sum)
33
+ if annualized:
34
+ out=4*out
35
+ if rounding_places:
36
+ out=round(out,rounding_places)
37
+ out.index=out.index.to_period(freq).astype(str)
38
+ if freq=="Y":
39
+ out.index=[f"Y{_}" for _ in out.index]
40
+ return out
@@ -0,0 +1,19 @@
1
+ """LLM-optimized interface for hedge fund index analysis."""
2
+
3
+ from .interface import (
4
+ quick_index_stats,
5
+ analyze_hedge_fund_index,
6
+ compare_hedge_fund_indices,
7
+ list_hedge_fund_indices,
8
+ list_available_metrics,
9
+ generate_index_report,
10
+ )
11
+
12
+ __all__ = [
13
+ "quick_index_stats",
14
+ "analyze_hedge_fund_index",
15
+ "compare_hedge_fund_indices",
16
+ "list_hedge_fund_indices",
17
+ "list_available_metrics",
18
+ "generate_index_report",
19
+ ]
@@ -0,0 +1,227 @@
1
+ """
2
+ LLM-optimized interface for PivotalPath.
3
+ Designed for hedge fund INDEX analysis only.
4
+
5
+ PivotalPath analyzes hedge fund indices - composite benchmarks that track
6
+ groups of hedge funds by strategy. It does NOT analyze individual hedge funds.
7
+ """
8
+
9
+ from typing import Dict, List, Optional, Union, Any
10
+ import pandas as pd
11
+ from datetime import datetime
12
+
13
+ # Import from your existing modules
14
+ from ..core.main_library import get_stats, get_returns, get_asset_list, get_catalog
15
+
16
+
17
+ def quick_index_stats(
18
+ index_ticker: str,
19
+ benchmark: str = "SP500",
20
+ period: str = "5Y"
21
+ ) -> Dict[str, Any]:
22
+ """
23
+ Get essential performance stats for a hedge fund index.
24
+
25
+ Args:
26
+ index_ticker: Hedge fund index ticker (e.g., "PP-HFC")
27
+ benchmark: Market benchmark (default: "SP500")
28
+ period: Time period - "1Y", "3Y", "5Y", "YTD"
29
+
30
+ Returns:
31
+ Dictionary with essential index performance metrics
32
+ """
33
+ import pandas as pd
34
+
35
+ end_date = (pd.Timestamp.now() - pd.DateOffset(months=1)).strftime("%Y-%m")
36
+
37
+ if period == "YTD":
38
+ start_date = f"{pd.Timestamp.now().year}-01"
39
+ elif period.endswith("Y"):
40
+ years = int(period[:-1])
41
+ start_date = (pd.Timestamp.now() - pd.DateOffset(years=years)).strftime("%Y-%m")
42
+ else:
43
+ start_date = "2020-01"
44
+
45
+ essential_stats = ['annret', 'annvol', 'sharpe', 'maxddc', 'beta']
46
+
47
+ try:
48
+ results = get_stats(
49
+ target=index_ticker,
50
+ base=benchmark,
51
+ start=start_date,
52
+ end=end_date,
53
+ stat=essential_stats
54
+ )
55
+
56
+ if isinstance(results, pd.DataFrame) and not results.empty:
57
+ data = results.iloc[:, 0].to_dict()
58
+ data.update({
59
+ 'index_ticker': index_ticker,
60
+ 'asset_type': 'hedge_fund_index',
61
+ 'benchmark': benchmark,
62
+ 'period': period,
63
+ 'analysis_date': pd.Timestamp.now().strftime("%Y-%m-%d")
64
+ })
65
+ return data
66
+ else:
67
+ return {
68
+ 'error': f"No data available for index {index_ticker}",
69
+ 'suggested_indices': list_hedge_fund_indices()[:3]
70
+ }
71
+ except Exception as e:
72
+ return {
73
+ 'error': f"Index analysis failed: {str(e)}",
74
+ 'index_ticker': index_ticker
75
+ }
76
+
77
+
78
+ def analyze_hedge_fund_index(
79
+ index_ticker: str,
80
+ benchmark: str = "SP500",
81
+ start_date: str = "2020-01",
82
+ end_date: str = "2024-12"
83
+ ) -> Dict[str, Any]:
84
+ """
85
+ Comprehensive analysis of a hedge fund index.
86
+
87
+ Args:
88
+ index_ticker: Hedge fund index ticker (e.g., "PP-HFC")
89
+ benchmark: Market benchmark (default: "SP500")
90
+ start_date: Start date in YYYY-MM format
91
+ end_date: End date in YYYY-MM format
92
+
93
+ Returns:
94
+ Dictionary with comprehensive index performance metrics
95
+ """
96
+ all_stats = ['annret', 'annvol', 'sharpe', 'beta', 'alpha', 'maxddc', 'sortino', 'calmar']
97
+
98
+ try:
99
+ results = get_stats(
100
+ target=index_ticker,
101
+ base=benchmark,
102
+ start=start_date,
103
+ end=end_date,
104
+ stat=all_stats
105
+ )
106
+
107
+ if isinstance(results, pd.DataFrame) and not results.empty:
108
+ analysis_dict = results.iloc[:, 0].to_dict()
109
+ analysis_dict.update({
110
+ 'index_ticker': index_ticker,
111
+ 'asset_type': 'hedge_fund_index',
112
+ 'benchmark': benchmark,
113
+ 'period': f"{start_date} to {end_date}",
114
+ 'analysis_date': datetime.now().strftime("%Y-%m-%d")
115
+ })
116
+ return analysis_dict
117
+ else:
118
+ return {
119
+ 'error': f"No data available for index {index_ticker}",
120
+ 'index_ticker': index_ticker
121
+ }
122
+ except Exception as e:
123
+ return {
124
+ 'error': f"Index analysis failed: {str(e)}",
125
+ 'index_ticker': index_ticker
126
+ }
127
+
128
+
129
+ def compare_hedge_fund_indices(
130
+ index_tickers: List[str],
131
+ benchmark: str = "SP500",
132
+ metrics: List[str] = ['annret', 'annvol', 'sharpe', 'maxddc'],
133
+ start_date: str = "2020-01",
134
+ end_date: str = "2024-12"
135
+ ) -> pd.DataFrame:
136
+ """
137
+ Compare multiple hedge fund indices side by side.
138
+
139
+ Args:
140
+ index_tickers: List of hedge fund index tickers
141
+ benchmark: Market benchmark (default: "SP500")
142
+ metrics: Metrics to compare
143
+ start_date: Start date in YYYY-MM format
144
+ end_date: End date in YYYY-MM format
145
+
146
+ Returns:
147
+ DataFrame comparing indices
148
+ """
149
+ try:
150
+ results = get_stats(
151
+ target=index_tickers,
152
+ base=benchmark,
153
+ start=start_date,
154
+ end=end_date,
155
+ stat=metrics,
156
+ transpose=True
157
+ )
158
+
159
+ if isinstance(results, pd.DataFrame) and not results.empty:
160
+ return results.round(4)
161
+ else:
162
+ return pd.DataFrame(index=metrics, columns=index_tickers)
163
+
164
+ except Exception as e:
165
+ print(f"Index comparison failed: {e}")
166
+ return pd.DataFrame(index=metrics, columns=index_tickers)
167
+
168
+
169
+ def list_hedge_fund_indices() -> List[str]:
170
+ """Get list of all available hedge fund index tickers."""
171
+ try:
172
+ return get_asset_list(asset_class="index", select="ticker")
173
+ except Exception:
174
+ return ["PP-HFC", "PP-L-EH", "PP-L-MA", "PP-L-ED", "PP-L-EM"]
175
+
176
+
177
+ def list_available_metrics() -> List[str]:
178
+ """Get list of all available performance metrics."""
179
+ try:
180
+ from ..core.main_library import get_stats_list
181
+ return get_stats_list(select="ticker")
182
+ except Exception:
183
+ return ['annret', 'annvol', 'sharpe', 'maxddc', 'beta', 'alpha', 'sortino', 'calmar']
184
+
185
+
186
+ def generate_index_report(
187
+ index_ticker: str,
188
+ benchmark: str = "SP500",
189
+ start_date: str = "2020-01",
190
+ end_date: str = "2024-12"
191
+ ) -> str:
192
+ """
193
+ Generate a formatted report for a hedge fund index.
194
+
195
+ Returns:
196
+ Formatted string report for the index
197
+ """
198
+ analysis = analyze_hedge_fund_index(index_ticker, benchmark, start_date, end_date)
199
+
200
+ if 'error' in analysis:
201
+ return f"Index Report Failed: {analysis['error']}"
202
+
203
+ return f"""
204
+ HEDGE FUND INDEX PERFORMANCE REPORT
205
+ {'='*50}
206
+
207
+ Index: {analysis.get('index_ticker', 'N/A')}
208
+ Period: {analysis.get('period', 'N/A')}
209
+ Benchmark: {analysis.get('benchmark', 'N/A')}
210
+
211
+ PERFORMANCE METRICS
212
+ {''*25}
213
+ Annual Return: {analysis.get('annret', 0):>8.1%}
214
+ Volatility: {analysis.get('annvol', 0):>8.1%}
215
+ Sharpe Ratio: {analysis.get('sharpe', 0):>8.2f}
216
+ Max Drawdown: {analysis.get('maxddc', 0):>8.1%}
217
+
218
+ MARKET EXPOSURE
219
+ {''*25}
220
+ Beta: {analysis.get('beta', 0):>8.2f}
221
+ Alpha: {analysis.get('alpha', 0):>8.1%}
222
+
223
+ Analysis Date: {analysis.get('analysis_date', 'N/A')}
224
+
225
+ NOTE: This report analyzes a hedge fund INDEX, which represents
226
+ aggregated performance of multiple hedge funds, not an individual fund.
227
+ """
@@ -0,0 +1,55 @@
1
+ 
2
+ import warnings
3
+ import pandas as pd
4
+ from .params import configureParams
5
+ from .wrapper import basicstats
6
+ from .catalog import stats_catalog
7
+
8
+
9
+ def stats_calculator(
10
+ target=None,
11
+ base="SP500",
12
+ riskfree="TBILL3M",
13
+ configured_params=[],
14
+ **kwargs
15
+ )->pd.DataFrame:
16
+
17
+ if not kwargs.get("_default_start"):
18
+ kwargs.update(_default_start=target.index[0])
19
+
20
+ if not kwargs.get("_default_end"):
21
+ kwargs.update(_default_end=target.index[-1])
22
+
23
+ if not configured_params:
24
+ # note: configured_params will most certainly be empty, unles it is repurposed from a different call
25
+ configured_params=configureParams(**kwargs)
26
+
27
+ df=target
28
+ out=None
29
+
30
+ for i,param_i in enumerate(configured_params):
31
+ stat_i=basicstats(df,base=base,riskfree=riskfree,**param_i)
32
+ if stat_i is None:
33
+ continue
34
+ stat_i=pd.DataFrame(stat_i)
35
+ if out is None:
36
+ out=stat_i.copy()
37
+ else:
38
+ try:
39
+ out=out.join(stat_i)
40
+ except:
41
+ warnings.warn("could not join the ith stat output")
42
+
43
+ return out
44
+
45
+
46
+ # Import everything from catalog
47
+ from .catalog import stats_catalog, stat_dict
48
+
49
+ __all__ = [
50
+ "stats_calculator",
51
+ "stats_catalog",
52
+ "stat_dict",
53
+ "basicstats",
54
+ "configureParams",
55
+ ]
@@ -0,0 +1,164 @@
1
+ stats_catalog=[
2
+ dict(
3
+ ticker='totret',
4
+ name='Total Return',
5
+ shortname='TotRet',
6
+ description="full sample return",
7
+ latex_expression='$\prod_{i=1}^N(1+r_i)-1$',
8
+ ),
9
+ dict(
10
+ ticker='annret',
11
+ name="Annualized Return",
12
+ shortname='AnnRet',
13
+ description="annualized mean return",
14
+ latex_expression='$(12/N)\sum_{i=1}^Nr_i$',
15
+ ),
16
+
17
+ dict(
18
+ ticker='annretc',
19
+ name="Compounded Annualized Return",
20
+ shortname='AnnRet(c)',
21
+ description="compounded annualized return",
22
+ latex_expression='$(\prod_{i=1}^N(1+r_i))^{12/N}-1$',
23
+ ),
24
+
25
+ dict(
26
+ ticker='annvol',
27
+ name='Annualized Volatility',
28
+ shortname='AnnVol',
29
+ description="annualized volatily",
30
+ latex_expression="$\sqrt{12}\\times\mathbb E[(r_i-\\bar{r_i})^2]^{1/2}$",
31
+ ),
32
+
33
+ dict(
34
+ ticker='maxddc',
35
+ name='Max Drawdown',
36
+ shortname='MaxDD',
37
+ description='max loss (peak to trough)',
38
+ latex_expression="$\\min_{1\leq s\leq t\leq N}\prod_{i=s}^t(1+r_i)-1$",
39
+ ),
40
+
41
+ dict(
42
+ ticker='skewness',
43
+ name='Skewness',
44
+ shortname='Skewness',
45
+ description="skewness",
46
+ latex_expression='$\mathbb E[(\\frac{r_i-\mu}{\sigma})^3]$',
47
+ ),
48
+
49
+ dict(
50
+ ticker='exckurt',
51
+ name='Excess Kurtosis',
52
+ shortname='Kurt(exc)',
53
+ description="excess kurtosis",
54
+ latex_expression='$\mathbb E[(\\frac{r_i-\mu}{\sigma})^4]-3$',
55
+ ),
56
+
57
+ dict(
58
+ ticker='sharpe',
59
+ name='Sharpe Ratio',
60
+ shortname='Sharpe',
61
+ latex_expression='AnnReturn/AnnVol'
62
+ ),
63
+
64
+ dict(
65
+ ticker='sharpec',
66
+ name='Sharpe Ratio (compounded)',
67
+ shortname='SR(c)',
68
+ latex_expression='AnnReturn(c)/AnnVol'
69
+ ),
70
+
71
+
72
+ dict(
73
+ ticker='sharperf',
74
+ name='Sharpe Ratio (riskfree)',
75
+ shortname='SR(rf)',
76
+ latex_expression='Sharpe(return-TBill3M)'
77
+ ),
78
+
79
+ dict(
80
+ ticker="sharpeadj",
81
+ name="Adjusted Sharpe Ratio",
82
+ shortname="SR(adj)",
83
+ latex_expression='$\operatorname{SR}(1 + (1/6) \cdot \operatorname{Skewness} \cdot \operatorname{SR} - (1/24)\operatorname{ExcKurt}\cdot \operatorname{SR}^2)$',
84
+ ),
85
+
86
+
87
+ dict(
88
+ ticker='calmar',
89
+ name='Calmar',
90
+ shortname='Calmar',
91
+ description="calmar ratio (36M)",
92
+ latex_expression='AnnReturn(c)/MDD',
93
+ ),
94
+
95
+ dict(
96
+ ticker='omega',
97
+ name='Omega',
98
+ shortname='Omega',
99
+ description="omega ratio",
100
+ latex_expression='$\mathbb E[r_i|r_i>0]/\mathbb E[|r_i||r_i<0]$ '
101
+ ),
102
+
103
+ dict(
104
+ ticker='sortino',
105
+ name='Sortino Ratio',
106
+ shortname='Sortino',
107
+ latex_expression='AnnRet/($\sqrt{12}\\times$DDEV)'
108
+ ),
109
+
110
+ dict(
111
+ ticker='ddev',
112
+ name='Downside Deviation',
113
+ shortname='DDEV',
114
+ latex_expression='$\mathbb E[\\min(0,r_i)^2]^{1/2}$',
115
+ ),
116
+
117
+ dict(
118
+ ticker='hitratio',
119
+ name='Hit Ratio',
120
+ shortname='HR',
121
+ description='proportion of positive returns',
122
+ latex_expression="Prob(r_i≥0)",
123
+ ),
124
+
125
+ dict(
126
+ ticker='alpha',
127
+ name='Annualized Alpha',
128
+ shortname='Alpha',
129
+ description="annualized alpha",
130
+ latex_expression='$12\\times(\mathbb E[r_i]-\\beta\mathbb E[f_i])$',
131
+ ),
132
+
133
+ dict(
134
+ ticker='beta',
135
+ name='Beta',
136
+ shortname='Beta',
137
+ description="beta",
138
+ latex_expression='$\operatorname{cov}(r_i,f_i)/\operatorname{var}(f_i)$',
139
+ ),
140
+
141
+ dict(
142
+ ticker='corr',
143
+ name='Correlation',
144
+ shortname='Corr',
145
+ description='Pearson correlation coefficient',
146
+ latex_expression='$\operatorname{cov}(r_i,f_i)/(\operatorname{std}(r_i)\operatorname{std}(f_i))$',
147
+ ),
148
+
149
+ dict(
150
+ ticker='r2',
151
+ name='RSquared',
152
+ shortname='R2',
153
+ description='r squared',
154
+ latex_expression='$\operatorname{var}[\epsilon_i]$',
155
+ ),
156
+
157
+ ]
158
+
159
+ stat_dict={
160
+ item['ticker']:item
161
+ for item in stats_catalog
162
+ }
163
+
164
+
@@ -0,0 +1,205 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import pdb
4
+
5
+
6
+ def _zeroInsert(df):
7
+ if "date" in df.columns.tolist():
8
+ df=df.set_index("date")
9
+ df.index.name="date"
10
+ un_indexify=True
11
+ else:
12
+ un_indexify=False
13
+ newrow=pd.DataFrame({k:[0] for k in df.columns},index=["0000-00"])
14
+ df=pd.concat([newrow,df],axis=0)
15
+ if un_indexify:
16
+ df=df.reset_index().rename(columns={'index':'date'})
17
+ return df
18
+
19
+ def _outputNull(df,title='stat'):
20
+ return pd.DataFrame(pd.Series(index=df.columns,name=title))
21
+
22
+
23
+
24
+ def _nanAlign(df,base):
25
+ try:
26
+ base=pd.concat([base] * (df.shape[1]), axis=1, ignore_index=True)
27
+ base.columns=df.columns
28
+ base[df.isnull()] = df.copy()
29
+ df[base.isnull()] = base.copy()
30
+ return df,base
31
+ except:
32
+ pass
33
+
34
+
35
+ def totret(df,**kwargs):
36
+ return (1+df).prod(axis=0)-1
37
+
38
+ def annret(df,**kwargs):
39
+ return 12*df.mean(axis=0)
40
+
41
+ def annretc(df,**kwargs):
42
+ A=(1+df).prod(axis=0)
43
+ B=df.notna().sum(axis=0)
44
+ B[B==0]=np.nan
45
+ return (A**(12/B))-1
46
+
47
+ def annvol(df,**kwargs):
48
+ return np.sqrt(12)*df.std(axis=0,ddof=1)
49
+
50
+ def maxdd(df,**kwargs):
51
+ nav=_zeroInsert(df).cumsum()
52
+ peak=nav.expanding(1).max()
53
+ return (nav-peak).iloc[1:].min(axis=0)
54
+
55
+ def maxddc(df,**kwargs):
56
+ nav=(1+_zeroInsert(df)).cumprod()
57
+ peak=nav.expanding(1).max()
58
+ return ((nav/peak)-1.0).iloc[1:].min(axis=0)
59
+
60
+
61
+ def skewness(df,**kwargs):
62
+ #scistats.skew(df,nan_policy='omit')
63
+ sigma=df.std(axis=0,ddof=0)
64
+ sigma[sigma==0]=np.nan
65
+ df0=(df-df.mean(axis=0))/sigma
66
+ return (df0**3).mean(axis=0)
67
+
68
+
69
+
70
+ def exckurt(df,**kwargs):
71
+ # scistats.kurtosis(df,nan_policy='omit')
72
+ sigma=df.std(axis=0,ddof=0)
73
+ sigma[sigma==0]=np.nan
74
+ df0=(df-df.mean(axis=0))/sigma
75
+ return (df0**4).mean(axis=0)-3
76
+
77
+ def sharpe(df,**kwargs):
78
+ R=12*df.mean(axis=0)
79
+ V=np.sqrt(12)*df.std(axis=0,ddof=1)
80
+ return R/V
81
+
82
+ def sharpec(df,**kwargs):
83
+ A=(1+df).prod(axis=0)
84
+ B=df.notna().sum(axis=0)
85
+ B[B==0]=np.nan
86
+ R=(A**(12/B))-1
87
+ V=np.sqrt(12)*df.std(axis=0,ddof=1)
88
+ return R/V
89
+
90
+ def sharperf(df,riskfree=None,**kwargs):
91
+ if riskfree is None:
92
+ riskfree=0
93
+ if isinstance(riskfree,pd.DataFrame) and not riskfree.empty:
94
+ riskfree=riskfree[riskfree.columns[0]]
95
+ df=df.sub(riskfree,axis=0)
96
+ R=12*df.mean(axis=0)
97
+ V=np.sqrt(12)*df.std(axis=0,ddof=1)
98
+ return R/V
99
+
100
+ def sharpeadj(df,base=None,**kwargs):
101
+ R=12*df.mean(axis=0)
102
+ V=np.sqrt(12)*df.std(axis=0,ddof=1)
103
+ SR=R/V
104
+ lambda_1=(1/6)*skewness(df)
105
+ lambda_2=(1/24)*exckurt(df)
106
+ adjustment_factor=1 + (lambda_1* SR) - (lambda_2* (SR**2))
107
+ return SR * adjustment_factor
108
+
109
+ def calmar(df,lookback=36,**kwargs):
110
+ # compute maxdd
111
+ if len(df)>36:
112
+ df=df.iloc[-lookback:]
113
+ nav=(1+_zeroInsert(df)).cumprod()
114
+ peak=nav.expanding(1).max()
115
+ currDD=(nav/peak)-1.0
116
+ maxDD=-currDD.iloc[1:].min(axis=0)
117
+ maxDD[maxDD==0]=np.nan
118
+ numPts=df.notna().sum()
119
+ annRet=(((1+df).prod(min_count=1))**(12/numPts))-1
120
+ out=annRet/maxDD
121
+ #out[:]="n/d"
122
+ #out[maxDD.isna()]="n/a (no drawdown)"
123
+ return out
124
+
125
+ def omega(df,**kwargs):
126
+ ypos=df.copy()
127
+ ypos[ypos<=0]=np.nan
128
+ yneg=df.copy()
129
+ yneg[yneg>=0]=np.nan
130
+ A=ypos.mean(axis=0)
131
+ B=-yneg.mean(axis=0)
132
+ return A/B
133
+
134
+ def sortino(df,**kwargs):
135
+ # as on pivotalbase
136
+ MAR=0
137
+ ye=df-MAR
138
+ yn=ye.clip(upper=0)
139
+ exrt=ye.mean(axis=0)
140
+ ddev=np.sqrt((yn**2).mean(axis=0))
141
+ ddev[ddev==0]=np.nan
142
+ return np.sqrt(12)*exrt/ddev
143
+
144
+
145
+ def ddev(df,**kwargs):
146
+ yn=df.clip(upper=0)
147
+ return np.sqrt((yn**2).mean(axis=0))
148
+
149
+ def hitratio(df,**kwargs):
150
+ A=(df>=0).sum(axis=0)
151
+ B=df.notna().sum(axis=0)
152
+ B[B==0]=np.nan
153
+ return A/B
154
+
155
+ #=========================================
156
+
157
+ def alpha(df,base=None,**kwargs):
158
+ if base is None:
159
+ return _outputNull(df,title='alpha')
160
+ df,base=_nanAlign(df,base)
161
+ ex=base.mean()
162
+ ey=df.mean()
163
+ base=base-ex
164
+ df=df-ey
165
+ exy=(base*df).mean()
166
+ exx=(base**2).mean()
167
+ exx[exx.abs()<1e-17]=np.nan
168
+ beta=exy/exx
169
+ alpha=ey-beta*ex
170
+ return 12*alpha
171
+
172
+ def beta(df,base=None,**kwargs):
173
+ if base is None:
174
+ return _outputNull(df,title='beta')
175
+ df,base=_nanAlign(df,base)
176
+ base=base-base.mean()
177
+ df=df-df.mean()
178
+ exy=(base*df).mean()
179
+ exx=(base**2).mean()
180
+ beta=exy/exx
181
+ return beta
182
+
183
+ def corr(df,base=None,**kwargs):
184
+ if base is None:
185
+ return _outputNull(df,title='corr')
186
+ if isinstance(base,pd.DataFrame):
187
+ base=base[base.columns[0]]
188
+ return df.apply(lambda x:x.corr(base))
189
+
190
+ def r2(df,base=None,**kwargs):
191
+ if base is None:
192
+ return _outputNull(df,title='r2')
193
+ if isinstance(base,pd.DataFrame):
194
+ base=base[base.columns[0]]
195
+ return df.apply(lambda x:x.corr(base))**2
196
+
197
+
198
+ def numpts(df,**kwargs):
199
+ return df.notna().sum(axis=0)
200
+
201
+ def currentvalue(df,**kwargs):
202
+ return df.iloc[-1]
203
+
204
+
205
+
@@ -0,0 +1,179 @@
1
+ import warnings
2
+ import pandas as pd
3
+ import pdb
4
+
5
+ from .catalog import stat_dict
6
+
7
+ def _islist(x):
8
+ return isinstance(x,list)
9
+
10
+ def _isscalar(x):
11
+ return not isinstance(x,list)
12
+
13
+ def _computestart(start=None,end=None,lookback=None):
14
+ if start:
15
+ return start
16
+ else:
17
+ return None if (not all([end,lookback])) else (pd.Timestamp(end)-pd.DateOffset(months=lookback-1)).strftime("%Y-%m")
18
+
19
+ def _computelookback(start,end):
20
+ start_dt,end_dt = [pd.to_datetime(_) for _ in [start,end]]
21
+ return (12*(end_dt.year - start_dt.year) + (end_dt.month - start_dt.month))+1
22
+
23
+ def _computeperiod(start,end):
24
+ if all([start,end]):
25
+ return f"{start}:{end}"
26
+ elif start:
27
+ return f"{start}:"
28
+ elif end:
29
+ return f":{end}"
30
+ else:
31
+ return ""
32
+
33
+ def _computeperiodformat(start,end):
34
+ def _safestr(_):
35
+ return pd.Timestamp(_).strftime("%b%y") if _ else ""
36
+ start,end=_safestr(start),_safestr(end)
37
+ if all([start,end]):
38
+ return f"{start}-{end}"
39
+ elif start:
40
+ return f"{start}-"
41
+ elif end:
42
+ return f"-{end}"
43
+ else:
44
+ return ""
45
+
46
+ def _completeparams(params={},ticker=None,label=None,label_as=None):
47
+ if not label:
48
+ label=_computelabel(params=params,ticker=ticker,label_as=label_as)
49
+ out={_:__ for _,__ in params.items()}
50
+ out.update(label=label,ticker=ticker)
51
+ return out
52
+
53
+ def _build_period_struct(
54
+ period=None, # has precedence over start, end
55
+ start=None,
56
+ end=None,
57
+ lookback=None,
58
+ _default_start=None,
59
+ _default_end=None,
60
+ ):
61
+ # period => start,end
62
+
63
+ if period:
64
+ if _isscalar(period):
65
+ period=[period]
66
+ period=[_ if isinstance(_,str) else "" for _ in period]
67
+ start=[]
68
+ end=[]
69
+ period_parse=[_.split(":") if ":" in _ else ["",""] for _ in period]
70
+ start=[_[0] for _ in period_parse]
71
+ end=[_[1] for _ in period_parse]
72
+
73
+ # align start,end
74
+ if _isscalar(start):
75
+ start=[start]
76
+ if _isscalar(end):
77
+ end=[end]
78
+ if _isscalar(lookback):
79
+ lookback=[lookback]
80
+
81
+
82
+ num_periods=max(len(start),len(end),len(lookback))
83
+ if len(start)==1:
84
+ start=start*num_periods
85
+ if len(end)==1:
86
+ end=end*num_periods
87
+ if len(lookback)==1:
88
+ lookback=lookback*num_periods
89
+ if len(start)!=num_periods:
90
+ warnings.warn('mismatch len(start)!=num_periods')
91
+ if len(end)!=num_periods:
92
+ warnings.warn('mismatch len(end)!=num_periods')
93
+ if len(lookback)!=num_periods:
94
+ warnings.warn('mismatch len(lookback)!=num_periods')
95
+
96
+ # end x lookback => start
97
+ end=[_ if _ else _default_end for _ in end]
98
+ start=[_computestart(start_i,end_i,lookback_i) for start_i,end_i,lookback_i in zip(start,end,lookback)]
99
+ start=[_ if _ else _default_start for _ in start]
100
+
101
+ return [
102
+ dict(
103
+ start=start_i,
104
+ end=end_i,
105
+ lookback=_computelookback(start_i,end_i),
106
+ period=_computeperiod(start_i,end_i),
107
+ periodformat=_computeperiodformat(start_i,end_i),
108
+ )
109
+ for start_i, end_i in zip(start,end)
110
+ ]
111
+
112
+ def _computelabel(params=None,ticker=None,label_as=None):
113
+ if not label_as:
114
+ label_as="ticker"
115
+ if "+" in label_as:
116
+ prefix_type,suffix_type=label_as.split("+")
117
+ else:
118
+ prefix_type,suffix_type=label_as,None
119
+ prefix=stat_dict[ticker][prefix_type]
120
+ if suffix_type=="lookback":
121
+ suffix=f"{params['lookback']}M"
122
+ elif suffix_type=="period":
123
+ suffix=params['periodformat']
124
+ else:
125
+ suffix=None
126
+ return f"{prefix} ({suffix})" if suffix else prefix
127
+
128
+ def configureParams(
129
+ _usecase=None,
130
+ stat=None,
131
+ label=None,
132
+ label_as=None,
133
+ **providedPeriodInputs
134
+ ):
135
+ usecasemap={
136
+ 1:dict(start=None,end=None,stat="sharpe"),
137
+ 2:dict(lookback=[12,18],label_as="ticker+lookback",stat=['sharpe','annvol']),
138
+ 3:dict(start="2020-01",label_as="name+period",stat=['sharpe','annvol']),
139
+ }
140
+ if _usecase:
141
+ return configureParams(_default_start="2020-01",_default_end="2025-07",**usecasemap[_usecase])
142
+
143
+
144
+ params=_build_period_struct(**providedPeriodInputs)
145
+
146
+
147
+ if _isscalar(stat):
148
+ stats=[stat]
149
+ else:
150
+ stats=stat
151
+
152
+ if len(params)==1 and len(stats)>1:
153
+ params=params*len(stats)
154
+
155
+ elif len(params)>1 and len(stats)==1:
156
+ stats=stats*len(params)
157
+
158
+ if len(params)!=len(stats):
159
+ warnings.warn("mismatch len(params)!=len(stat_tickers)")
160
+
161
+ if _isscalar(label):
162
+ label=[label]
163
+ if len(label)==1:
164
+ label=label*len(params)
165
+ if len(params)!=len(label):
166
+ warnings.warn("mismatch len(params)!=len(label)")
167
+
168
+
169
+ out=[
170
+ _completeparams(params=param_i,ticker=ticker_i,label=label_i,label_as=label_as)
171
+ for param_i, ticker_i,label_i in zip(params,stats,label)
172
+ ]
173
+
174
+ return out
175
+
176
+
177
+
178
+
179
+
@@ -0,0 +1,51 @@
1
+ import pandas as pd
2
+ import pdb
3
+
4
+ from . import kernel as statsKernel
5
+
6
+ def _align2target(df,target=None):
7
+ out=pd.DataFrame(index=target.index,columns=df.columns)
8
+ common_idx=[_ for _ in target.index.tolist() if _ in df.index.tolist()]
9
+ out.loc[common_idx,:]=df.loc[common_idx,:].copy()
10
+ return out
11
+
12
+ def basicstats(
13
+ df,
14
+ base=None,
15
+ riskfree=None,
16
+ ticker=None,
17
+ start=None,
18
+ end=None,
19
+ label=None,
20
+ _roundoff=6,
21
+ **overflowParams,
22
+ ):
23
+
24
+ if not ticker:
25
+ return pd.DataFrame()
26
+
27
+ if any([start,end]):
28
+ df=df.loc[start:end]
29
+ if base is not None:
30
+ base=base.loc[start:end]
31
+ if riskfree is not None:
32
+ riskfree=riskfree.loc[start:end]
33
+
34
+ if base is not None:
35
+ base=_align2target(base,target=df)
36
+ if riskfree is not None:
37
+ riskfree=_align2target(riskfree,target=df)
38
+ if not label:
39
+ label=ticker
40
+
41
+ out=getattr(statsKernel,ticker)(df,base=base,riskfree=riskfree,start=None,end=None,label=None)
42
+
43
+ if isinstance(out,pd.DataFrame):
44
+ out=out[out.columns[0]]
45
+ if isinstance(out,pd.Series):
46
+ out.name=label
47
+ out=round(out,_roundoff)
48
+
49
+ return out
50
+
51
+
pivotalpath/version.py ADDED
@@ -0,0 +1,2 @@
1
+ """Version information for pivotalpath."""
2
+ __version__ = "1.0.0"
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.1
2
+ Name: pivotalpath
3
+ Version: 1.0.0
4
+ Summary: The industry standard for hedge fund indices data and analytics
5
+ Author-email: Your Name <your.email@example.com>
6
+ License: MIT
7
+ Requires-Python: >=3.7
8
+ Requires-Dist: pandas >=1.3.0
9
+ Requires-Dist: numpy >=1.20.0
10
+ Requires-Dist: requests >=2.25.0
11
+ Requires-Dist: python-dateutil >=2.8.0
12
+
@@ -0,0 +1,21 @@
1
+ pivotalpath/__init__.py,sha256=JXuQsd1ojdAlHk2WPGh7L_votDt-BSWX3OH-Gqwju20,1463
2
+ pivotalpath/version.py,sha256=i_J42cJKAhNEnqxy7xhrN3kJTwi8BRU5dVTdVERm0gc,69
3
+ pivotalpath/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ pivotalpath/core/__init__.py,sha256=EzD2XiPCsjHJCSrgVcZAcHgkFKPr6dEu7tMJriDQj_k,417
5
+ pivotalpath/core/examples.py,sha256=CtCqNMCC2-P2mrInQeMvnpbGn-rtvkhzZ_a42mbz0eo,1970
6
+ pivotalpath/core/important_tickers.py,sha256=xHOAR8wMTZXS5_0PH8_V6WFQpayxpqmcIPsi_yfnIp0,92
7
+ pivotalpath/core/main_library.py,sha256=oTbxiYaVDXjmO3Jr18hjj3Y15PrI-R9KSBFBTM_yn-w,1486
8
+ pivotalpath/data/__init__.py,sha256=tNyy_5DF0XGYxqO_SG0vWJfZmBuC437HY1dAwnH31Fw,3288
9
+ pivotalpath/data/api.py,sha256=my_APfA0Mv_Fy97x2XHCswWfaotSJW_aMu2aPZXlcKQ,1132
10
+ pivotalpath/data/utils.py,sha256=OYmRB2HSlX-7ESU2VokbnanaY97ACvWdFe72ZRH1rf0,1435
11
+ pivotalpath/llm/__init__.py,sha256=q786GSBYq76CmF9xxAuwN-y1d04c25v0eI5hhPUjddY,459
12
+ pivotalpath/llm/interface.py,sha256=P8V9GxEYJ-WLP8K_c9TXzOx3GhptHVNy-rt455ybMUc,7053
13
+ pivotalpath/stats/__init__.py,sha256=z9XQKid32JI3G8KZBEfukmqhOCjMooD_9x-yZJ9bkJg,1378
14
+ pivotalpath/stats/catalog.py,sha256=LJ_5JpV04b0i6DPa_LwWzXlXhzn2wsxkSS74jeV0uho,4306
15
+ pivotalpath/stats/kernel.py,sha256=1X2oFYrjJBhEJ630PMIJWmgZimKgyjgVrmwbxRhDXcE,4940
16
+ pivotalpath/stats/params.py,sha256=UuKMqFHB6nBslzTbCwzJKoEzSnEybdPdYnbJ7p3ECpY,5115
17
+ pivotalpath/stats/wrapper.py,sha256=CW8R892dHufRh1fwm4aKtKPeZefMPqcgM92pwdF_P0g,1297
18
+ pivotalpath-1.0.0.dist-info/METADATA,sha256=JlbGppWdC-pB6ZSLb8Qc4COIREeqKAgfeMQSpPKH8-M,358
19
+ pivotalpath-1.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
20
+ pivotalpath-1.0.0.dist-info/top_level.txt,sha256=qFWuReiHrH_6fShiOREDcVd0PEbCvMhilLJDpOLQBY8,12
21
+ pivotalpath-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.42.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ pivotalpath