pivotalpath 1.0.0__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.
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,35 @@
1
+ # PivotalPath
2
+
3
+ The industry standard for hedge fund indices data and analytics.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install pivotalpath
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ import pivotalpath as pp
15
+
16
+ # See available tickers
17
+ print(pp.examples.example1())
18
+
19
+ # Get fund returns
20
+ returns = pp.get_returns("PP-HFC")
21
+
22
+ # Calculate performance stats
23
+ stats = pp.get_stats(target="PP-HFC", base="SP500", stat="sharpe")
24
+ ```
25
+
26
+ ## Features
27
+
28
+ - Comprehensive hedge fund performance analytics
29
+ - 20+ institutional-quality metrics
30
+ - Real-time data API integration
31
+ - LLM-optimized for AI integration
32
+
33
+ ## License
34
+
35
+ MIT
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pivotalpath"
7
+ version = "1.0.0"
8
+ description = "The industry standard for hedge fund indices data and analytics"
9
+ authors = [{name = "Your Name", email = "your.email@example.com"}]
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.7"
12
+ dependencies = [
13
+ "pandas>=1.3.0",
14
+ "numpy>=1.20.0",
15
+ "requests>=2.25.0",
16
+ "python-dateutil>=2.8.0"
17
+ ]
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ ]