pypsx-toolkit 3.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.
- pypsx_toolkit/__init__.py +304 -0
- pypsx_toolkit/__init__.pyi +99 -0
- pypsx_toolkit/analysis/__init__.py +222 -0
- pypsx_toolkit/analysis/__init__.pyi +348 -0
- pypsx_toolkit/api.pyi +294 -0
- pypsx_toolkit/core/__init__.py +6 -0
- pypsx_toolkit/core/errors.pyi +38 -0
- pypsx_toolkit/core/stream.pyi +66 -0
- pypsx_toolkit/endpoints/__init__.py +1 -0
- pypsx_toolkit/endpoints/constants.py +121 -0
- pypsx_toolkit/format/__init__.py +1 -0
- pypsx_toolkit/market.pyi +158 -0
- pypsx_toolkit/models.py +224 -0
- pypsx_toolkit/models.pyi +199 -0
- pypsx_toolkit/py.typed +0 -0
- pypsx_toolkit/ticker.pyi +237 -0
- pypsx_toolkit-3.0.0.dist-info/METADATA +744 -0
- pypsx_toolkit-3.0.0.dist-info/RECORD +22 -0
- pypsx_toolkit-3.0.0.dist-info/WHEEL +5 -0
- pypsx_toolkit-3.0.0.dist-info/entry_points.txt +2 -0
- pypsx_toolkit-3.0.0.dist-info/licenses/LICENSE +17 -0
- pypsx_toolkit-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PSX yfinance-style API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "3.0.0"
|
|
6
|
+
__author__ = "PyPSX Team"
|
|
7
|
+
__email__ = "pypsx@example.com"
|
|
8
|
+
|
|
9
|
+
from pypsx_toolkit.ticker import PSXTicker, Ticker
|
|
10
|
+
|
|
11
|
+
# Backward compatibility alias
|
|
12
|
+
class PSXSymbol(Ticker):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
from pypsx_toolkit.api import (
|
|
16
|
+
download,
|
|
17
|
+
sectors,
|
|
18
|
+
performers,
|
|
19
|
+
market_watch,
|
|
20
|
+
listings_nc,
|
|
21
|
+
listings_dc,
|
|
22
|
+
trading_board,
|
|
23
|
+
index_constituents,
|
|
24
|
+
get_indices,
|
|
25
|
+
get_intraday_multiple,
|
|
26
|
+
get_historical,
|
|
27
|
+
symbols_nc,
|
|
28
|
+
symbols_dc,
|
|
29
|
+
get_symbols,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from pypsx_toolkit.endpoints.company import get_quote, get_quote_batch
|
|
33
|
+
from pypsx_toolkit.endpoints.company_fundamentals import get_company_fundamentals
|
|
34
|
+
from pypsx_toolkit.endpoints.announcements import get_announcements as _get_announcements
|
|
35
|
+
from pypsx_toolkit.endpoints.dividends import get_dividend_info as _get_dividend_info, get_dividend_history as _get_dividend_history
|
|
36
|
+
from pypsx_toolkit.endpoints.sectors import get_sector_constituents as _get_sector_constituents
|
|
37
|
+
from pypsx_toolkit.endpoints.compliant_listings import get_symbols_by_sector as _get_symbols_by_sector
|
|
38
|
+
from pypsx_toolkit.endpoints.snapshot import get_snapshot
|
|
39
|
+
|
|
40
|
+
from pypsx_toolkit.market import (
|
|
41
|
+
top_performers,
|
|
42
|
+
sector_summary,
|
|
43
|
+
market_watch as market_watch_func,
|
|
44
|
+
get_indices as get_indices_func,
|
|
45
|
+
get_indices_breakdown,
|
|
46
|
+
get_sector_breakdown,
|
|
47
|
+
get_homepage_indices,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
from pypsx_toolkit.core.stream import PSXStream
|
|
51
|
+
|
|
52
|
+
# Models
|
|
53
|
+
from pypsx_toolkit.models import (
|
|
54
|
+
SymbolInfo,
|
|
55
|
+
SectorSummary,
|
|
56
|
+
SectorCompany,
|
|
57
|
+
CompanyMarketWatch,
|
|
58
|
+
IndexConstituent,
|
|
59
|
+
IndexMeta,
|
|
60
|
+
TradingBoardRow,
|
|
61
|
+
TopActiveStock,
|
|
62
|
+
TopAdvancer,
|
|
63
|
+
TopDecliner,
|
|
64
|
+
IntradayBar,
|
|
65
|
+
EODBar,
|
|
66
|
+
ListingEntry,
|
|
67
|
+
CompanyFundamentals,
|
|
68
|
+
Announcement,
|
|
69
|
+
DividendInfo,
|
|
70
|
+
DividendHistory,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Analysis module (optional - requires analysis package)
|
|
74
|
+
try:
|
|
75
|
+
from pypsx_toolkit.analysis import (
|
|
76
|
+
interpret_stock,
|
|
77
|
+
sharpe_ratio,
|
|
78
|
+
rsi,
|
|
79
|
+
macd,
|
|
80
|
+
bollinger_bands,
|
|
81
|
+
)
|
|
82
|
+
except ImportError:
|
|
83
|
+
# Analysis module not available
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
__all__ = [
|
|
87
|
+
"__version__",
|
|
88
|
+
"PSXTicker",
|
|
89
|
+
"Ticker",
|
|
90
|
+
"PSXSymbol",
|
|
91
|
+
"PSXStream",
|
|
92
|
+
"download",
|
|
93
|
+
"sectors",
|
|
94
|
+
"performers",
|
|
95
|
+
"market_watch",
|
|
96
|
+
"top_performers",
|
|
97
|
+
"sector_summary",
|
|
98
|
+
"get_indices",
|
|
99
|
+
"get_indices_breakdown",
|
|
100
|
+
"get_sector_breakdown",
|
|
101
|
+
"get_homepage_indices",
|
|
102
|
+
"listings_nc",
|
|
103
|
+
"listings_dc",
|
|
104
|
+
"trading_board",
|
|
105
|
+
"index_constituents",
|
|
106
|
+
"get_intraday_multiple",
|
|
107
|
+
"get_historical",
|
|
108
|
+
"symbols_nc",
|
|
109
|
+
"symbols_dc",
|
|
110
|
+
"get_symbols",
|
|
111
|
+
# Convenience wrappers
|
|
112
|
+
"get_market_watch",
|
|
113
|
+
"get_most_active",
|
|
114
|
+
"get_top_gainers",
|
|
115
|
+
"get_top_losers",
|
|
116
|
+
"get_orderbook",
|
|
117
|
+
"get_intraday",
|
|
118
|
+
"get_history",
|
|
119
|
+
"get_index",
|
|
120
|
+
"get_symbols_nc",
|
|
121
|
+
"get_symbols_dc",
|
|
122
|
+
"get_quote",
|
|
123
|
+
"get_quote_batch",
|
|
124
|
+
"get_company_fundamentals",
|
|
125
|
+
"get_announcements",
|
|
126
|
+
"get_dividend_info",
|
|
127
|
+
"get_dividend_history",
|
|
128
|
+
"get_business_description",
|
|
129
|
+
"get_sector_constituents",
|
|
130
|
+
"get_symbols_by_sector",
|
|
131
|
+
"get_snapshot",
|
|
132
|
+
# Models
|
|
133
|
+
"SymbolInfo",
|
|
134
|
+
"SectorSummary",
|
|
135
|
+
"SectorCompany",
|
|
136
|
+
"CompanyMarketWatch",
|
|
137
|
+
"IndexConstituent",
|
|
138
|
+
"IndexMeta",
|
|
139
|
+
"TradingBoardRow",
|
|
140
|
+
"TopActiveStock",
|
|
141
|
+
"TopAdvancer",
|
|
142
|
+
"TopDecliner",
|
|
143
|
+
"IntradayBar",
|
|
144
|
+
"EODBar",
|
|
145
|
+
"ListingEntry",
|
|
146
|
+
"CompanyFundamentals",
|
|
147
|
+
"Announcement",
|
|
148
|
+
"DividendInfo",
|
|
149
|
+
"DividendHistory",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# Legacy compatibility wrappers used by audit script (clean outputs only)
|
|
154
|
+
|
|
155
|
+
def get_market_watch():
|
|
156
|
+
return market_watch()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_most_active():
|
|
160
|
+
return performers().get("top_actives")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_top_gainers():
|
|
164
|
+
return performers().get("top_gainers")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def get_top_losers():
|
|
168
|
+
return performers().get("top_decliners")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_orderbook(symbol: str | None = None):
|
|
172
|
+
if symbol:
|
|
173
|
+
return Ticker(symbol).orderbook()
|
|
174
|
+
return trading_board()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_intraday(symbol: str):
|
|
178
|
+
return Ticker(symbol).history(period="1d", interval="1m")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_history(symbol: str, period: str = "1y"):
|
|
182
|
+
"""Get historical data for a symbol (convenience function)."""
|
|
183
|
+
return PSXTicker(symbol).history(period=period, interval="1d")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_index(index_name: str, format: str = 'dataframe'):
|
|
187
|
+
"""Get index constituents data (convenience function)."""
|
|
188
|
+
return index_constituents(index_name)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_symbols_nc():
|
|
194
|
+
import pandas as _pd
|
|
195
|
+
return _pd.DataFrame({"Symbol": symbols_nc()})
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_symbols_dc():
|
|
199
|
+
import pandas as _pd
|
|
200
|
+
return _pd.DataFrame({"Symbol": symbols_dc()})
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def get_announcements(symbol: str, format: str = 'dataframe'):
|
|
204
|
+
"""Get company announcements (convenience function)."""
|
|
205
|
+
return _get_announcements(symbol, format)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_dividend_info(symbol: str, format: str = 'dataframe'):
|
|
209
|
+
"""Get dividend information (convenience function)."""
|
|
210
|
+
return _get_dividend_info(symbol, format)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_dividend_history(symbol: str, format: str = 'dataframe'):
|
|
214
|
+
"""Get dividend history (convenience function)."""
|
|
215
|
+
return _get_dividend_history(symbol, format)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_sector_constituents(sector_code: str, format: str = 'dataframe'):
|
|
219
|
+
"""
|
|
220
|
+
Get all companies in a specific sector.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
sector_code: Sector code (e.g., '0801' for AUTOMOBILE ASSEMBLER) or sector name
|
|
224
|
+
format: Output format - 'dataframe' or 'json'
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
DataFrame with sector constituents or JSON dict
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
>>> import pypsx_toolkit
|
|
231
|
+
>>> df = pypsx_toolkit.get_sector_constituents('0801') # Automobile Assembler
|
|
232
|
+
>>> print(df.head())
|
|
233
|
+
"""
|
|
234
|
+
return _get_sector_constituents(sector_code, format)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def get_symbols_by_sector(sector_name: str, format: str = 'dataframe'):
|
|
238
|
+
"""
|
|
239
|
+
Get all symbols in a specific sector by sector name (supports partial matching).
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
sector_name: Sector name (e.g., 'automobile', 'AUTOMOBILE ASSEMBLER', 'Automobile Assembler')
|
|
243
|
+
format: Output format - 'dataframe' or 'json'
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
DataFrame with symbols in the sector or JSON dict
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
>>> import pypsx_toolkit
|
|
250
|
+
>>> df = pypsx_toolkit.get_symbols_by_sector('automobile')
|
|
251
|
+
>>> print(df.head())
|
|
252
|
+
"""
|
|
253
|
+
return _get_symbols_by_sector(sector_name, format)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_business_description(symbol: str) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Get business description for a company.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
symbol: Stock symbol
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Business description string, or empty string if not found
|
|
265
|
+
"""
|
|
266
|
+
import pandas as pd
|
|
267
|
+
try:
|
|
268
|
+
df = get_company_fundamentals(symbol, format='dataframe')
|
|
269
|
+
if df is None or df.empty: # type: ignore[union-attr]
|
|
270
|
+
return ""
|
|
271
|
+
|
|
272
|
+
# Look for Business Description in the fundamentals DataFrame
|
|
273
|
+
if isinstance(df, pd.DataFrame):
|
|
274
|
+
# Try resetting index first (handles both regular and MultiIndex)
|
|
275
|
+
df_reset = df.reset_index()
|
|
276
|
+
|
|
277
|
+
# Check if we have METRIC and VALUE columns
|
|
278
|
+
if 'METRIC' in df_reset.columns and 'VALUE' in df_reset.columns:
|
|
279
|
+
desc_row = df_reset[df_reset['METRIC'] == 'Business Description']
|
|
280
|
+
if not desc_row.empty:
|
|
281
|
+
value = desc_row['VALUE'].iloc[0]
|
|
282
|
+
# Return clean string, not DataFrame representation
|
|
283
|
+
if pd.notna(value):
|
|
284
|
+
return str(value).strip()
|
|
285
|
+
|
|
286
|
+
# Alternative: check if it's indexed by METRIC (MultiIndex) - try direct access
|
|
287
|
+
if hasattr(df.index, 'get_level_values') and isinstance(df.index, pd.MultiIndex):
|
|
288
|
+
if 'Business Description' in df.index.get_level_values('METRIC').tolist():
|
|
289
|
+
try:
|
|
290
|
+
# Sort index first to avoid PerformanceWarning
|
|
291
|
+
df_sorted = df.sort_index()
|
|
292
|
+
value = df_sorted.loc[(symbol.upper(), 'Profile', 'Business Description'), 'VALUE']
|
|
293
|
+
if pd.notna(value):
|
|
294
|
+
return str(value).strip()
|
|
295
|
+
except (KeyError, IndexError):
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
return ""
|
|
299
|
+
except Exception:
|
|
300
|
+
return ""
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pypsx-toolkit — Pakistan Stock Exchange data library (yfinance-style API).
|
|
3
|
+
|
|
4
|
+
Optional, supplementary to the pypsx trading SDK - not required for trading,
|
|
5
|
+
backtesting, or standard data fetching (see the pypsx package for that).
|
|
6
|
+
|
|
7
|
+
Install:
|
|
8
|
+
pip install pypsx-toolkit
|
|
9
|
+
|
|
10
|
+
Quick start:
|
|
11
|
+
import pypsx_toolkit
|
|
12
|
+
|
|
13
|
+
# Single ticker
|
|
14
|
+
t = pypsx_toolkit.Ticker("ENGRO")
|
|
15
|
+
df = t.history(period="1y")
|
|
16
|
+
|
|
17
|
+
# Batch download
|
|
18
|
+
df = pypsx_toolkit.download(["ENGRO", "HBL", "OGDC"], period="6mo")
|
|
19
|
+
|
|
20
|
+
# Market overview
|
|
21
|
+
mw = pypsx_toolkit.market_watch()
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import List, Optional, Union
|
|
25
|
+
import pandas as pd
|
|
26
|
+
|
|
27
|
+
from .ticker import PSXTicker as PSXTicker, PSXTicker as Ticker
|
|
28
|
+
from .models import (
|
|
29
|
+
SymbolInfo as SymbolInfo,
|
|
30
|
+
SectorSummary as SectorSummary,
|
|
31
|
+
SectorCompany as SectorCompany,
|
|
32
|
+
CompanyMarketWatch as CompanyMarketWatch,
|
|
33
|
+
IndexConstituent as IndexConstituent,
|
|
34
|
+
IndexMeta as IndexMeta,
|
|
35
|
+
TradingBoardRow as TradingBoardRow,
|
|
36
|
+
TopActiveStock as TopActiveStock,
|
|
37
|
+
TopAdvancer as TopAdvancer,
|
|
38
|
+
TopDecliner as TopDecliner,
|
|
39
|
+
IntradayBar as IntradayBar,
|
|
40
|
+
EODBar as EODBar,
|
|
41
|
+
ListingEntry as ListingEntry,
|
|
42
|
+
CompanyFundamentals as CompanyFundamentals,
|
|
43
|
+
Announcement as Announcement,
|
|
44
|
+
DividendInfo as DividendInfo,
|
|
45
|
+
DividendHistory as DividendHistory,
|
|
46
|
+
)
|
|
47
|
+
from .core.errors import (
|
|
48
|
+
PSXError as PSXError,
|
|
49
|
+
PSXHTTPError as PSXHTTPError,
|
|
50
|
+
PSXTimeoutError as PSXTimeoutError,
|
|
51
|
+
PSXNotFoundError as PSXNotFoundError,
|
|
52
|
+
PSXScopeError as PSXScopeError,
|
|
53
|
+
)
|
|
54
|
+
from .core.stream import PSXStream as PSXStream
|
|
55
|
+
from .api import (
|
|
56
|
+
download as download,
|
|
57
|
+
get_historical as get_historical,
|
|
58
|
+
get_intraday as get_intraday,
|
|
59
|
+
get_intraday_multiple as get_intraday_multiple,
|
|
60
|
+
get_quote as get_quote,
|
|
61
|
+
get_quote_batch as get_quote_batch,
|
|
62
|
+
get_company_fundamentals as get_company_fundamentals,
|
|
63
|
+
get_announcements as get_announcements,
|
|
64
|
+
get_dividend_info as get_dividend_info,
|
|
65
|
+
get_dividend_history as get_dividend_history,
|
|
66
|
+
get_snapshot as get_snapshot,
|
|
67
|
+
get_sector_constituents as get_sector_constituents,
|
|
68
|
+
get_symbols_by_sector as get_symbols_by_sector,
|
|
69
|
+
get_business_description as get_business_description,
|
|
70
|
+
)
|
|
71
|
+
from .market import (
|
|
72
|
+
market_watch as market_watch,
|
|
73
|
+
performers as performers,
|
|
74
|
+
sectors as sectors,
|
|
75
|
+
trading_board as trading_board,
|
|
76
|
+
get_symbols as get_symbols,
|
|
77
|
+
listings_nc as listings_nc,
|
|
78
|
+
listings_dc as listings_dc,
|
|
79
|
+
get_indices as get_indices,
|
|
80
|
+
get_indices_breakdown as get_indices_breakdown,
|
|
81
|
+
get_sector_breakdown as get_sector_breakdown,
|
|
82
|
+
get_homepage_indices as get_homepage_indices,
|
|
83
|
+
index_constituents as index_constituents,
|
|
84
|
+
)
|
|
85
|
+
from .analysis import (
|
|
86
|
+
moving_average as moving_average,
|
|
87
|
+
exponential_moving_average as exponential_moving_average,
|
|
88
|
+
bollinger_bands as bollinger_bands,
|
|
89
|
+
rsi as rsi,
|
|
90
|
+
macd as macd,
|
|
91
|
+
sharpe_ratio as sharpe_ratio,
|
|
92
|
+
sortino_ratio as sortino_ratio,
|
|
93
|
+
performance_summary as performance_summary,
|
|
94
|
+
interpret_stock as interpret_stock,
|
|
95
|
+
portfolio_analysis as portfolio_analysis,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
__version__: str
|
|
99
|
+
__all__: List[str]
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyPSX Analysis Module
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive financial analysis tools for Pakistan Stock Exchange data,
|
|
5
|
+
including statistical functions, technical indicators, performance metrics, and automated insights.
|
|
6
|
+
|
|
7
|
+
Main Components:
|
|
8
|
+
- stats: Core statistical functions (returns, volatility, correlation, etc.)
|
|
9
|
+
- indicators: Technical analysis indicators (RSI, MACD, Bollinger Bands, etc.)
|
|
10
|
+
- performance: Financial performance metrics (Sharpe ratio, drawdown, etc.)
|
|
11
|
+
- insights: Automated insight generation and pattern detection
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
import pypsx_toolkit
|
|
15
|
+
from pypsx_toolkit.analysis import interpret_stock, sharpe_ratio, bollinger_bands
|
|
16
|
+
|
|
17
|
+
# Get stock data
|
|
18
|
+
ticker = pypsx_toolkit.PSXTicker("OGDC")
|
|
19
|
+
df = ticker.history(period="1y")
|
|
20
|
+
|
|
21
|
+
# Generate insights
|
|
22
|
+
insights = interpret_stock(df, "OGDC")
|
|
23
|
+
print(insights['insights'])
|
|
24
|
+
|
|
25
|
+
# Calculate metrics
|
|
26
|
+
sharpe = sharpe_ratio(df)
|
|
27
|
+
ma, upper, lower = bollinger_bands(df)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# Core statistical functions
|
|
31
|
+
from .stats import (
|
|
32
|
+
returns, volatility, correlation, correlation_matrix, beta,
|
|
33
|
+
skewness, kurtosis, var, cvar, autocorrelation
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Technical indicators
|
|
37
|
+
from .indicators import (
|
|
38
|
+
moving_average, exponential_moving_average, bollinger_bands,
|
|
39
|
+
rsi, macd, stochastic, williams_r, atr, adx, cci, obv, vwap
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Performance metrics
|
|
43
|
+
from .performance import (
|
|
44
|
+
sharpe_ratio, sortino_ratio, calmar_ratio, cumulative_returns,
|
|
45
|
+
annualized_return, annualized_volatility, drawdown, max_drawdown,
|
|
46
|
+
drawdown_duration, information_ratio, treynor_ratio, jensen_alpha,
|
|
47
|
+
win_rate, profit_loss_ratio, recovery_factor, performance_summary
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Insight engine
|
|
51
|
+
from .insights import (
|
|
52
|
+
interpret_stock, interpret_portfolio, detect_patterns,
|
|
53
|
+
generate_trading_signals, market_sentiment_analysis
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
# Statistical functions
|
|
58
|
+
'returns', 'volatility', 'correlation', 'correlation_matrix', 'beta',
|
|
59
|
+
'skewness', 'kurtosis', 'var', 'cvar', 'autocorrelation',
|
|
60
|
+
|
|
61
|
+
# Technical indicators
|
|
62
|
+
'moving_average', 'exponential_moving_average', 'bollinger_bands',
|
|
63
|
+
'rsi', 'macd', 'stochastic', 'williams_r', 'atr', 'adx', 'cci', 'obv', 'vwap',
|
|
64
|
+
|
|
65
|
+
# Performance metrics
|
|
66
|
+
'sharpe_ratio', 'sortino_ratio', 'calmar_ratio', 'cumulative_returns',
|
|
67
|
+
'annualized_return', 'annualized_volatility', 'drawdown', 'max_drawdown',
|
|
68
|
+
'drawdown_duration', 'information_ratio', 'treynor_ratio', 'jensen_alpha',
|
|
69
|
+
'win_rate', 'profit_loss_ratio', 'recovery_factor', 'performance_summary',
|
|
70
|
+
|
|
71
|
+
# Insight engine
|
|
72
|
+
'interpret_stock', 'interpret_portfolio', 'detect_patterns',
|
|
73
|
+
'generate_trading_signals', 'market_sentiment_analysis'
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def quick_analysis(df, symbol=None):
|
|
78
|
+
"""
|
|
79
|
+
Quick analysis function that provides a comprehensive overview of stock performance.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
df: Stock DataFrame with OHLCV data
|
|
83
|
+
symbol: Stock symbol (optional)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dictionary containing key metrics and insights
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
>>> import pypsx_toolkit
|
|
90
|
+
>>> ticker = pypsx_toolkit.PSXTicker("OGDC")
|
|
91
|
+
>>> df = ticker.history(period="1y")
|
|
92
|
+
>>> analysis = quick_analysis(df, "OGDC")
|
|
93
|
+
>>> print(f"Sharpe Ratio: {analysis['sharpe_ratio']:.3f}")
|
|
94
|
+
>>> print(f"Total Return: {analysis['total_return']:.2%}")
|
|
95
|
+
"""
|
|
96
|
+
if df is None or df.empty:
|
|
97
|
+
return {"error": "No data available for analysis"}
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
# Get comprehensive insights
|
|
101
|
+
insights = interpret_stock(df, symbol)
|
|
102
|
+
|
|
103
|
+
# Get performance summary
|
|
104
|
+
perf_summary = performance_summary(df)
|
|
105
|
+
|
|
106
|
+
# Get technical patterns
|
|
107
|
+
patterns = detect_patterns(df, symbol)
|
|
108
|
+
|
|
109
|
+
# Get trading signals
|
|
110
|
+
signals = generate_trading_signals(df, symbol)
|
|
111
|
+
|
|
112
|
+
# Merge performance summary metrics at top level for easy access
|
|
113
|
+
result = {
|
|
114
|
+
"symbol": symbol,
|
|
115
|
+
"performance": perf_summary,
|
|
116
|
+
"insights": insights.get('insights', []),
|
|
117
|
+
"patterns": patterns.get('patterns', []),
|
|
118
|
+
"trading_signal": signals.get('primary_signal', 'HOLD'),
|
|
119
|
+
"signal_confidence": signals.get('confidence', 0),
|
|
120
|
+
"key_metrics": {
|
|
121
|
+
"total_return": insights.get('total_return', 0),
|
|
122
|
+
"volatility": insights.get('volatility', 0),
|
|
123
|
+
"sharpe_ratio": insights.get('sharpe_ratio', 0),
|
|
124
|
+
"max_drawdown": insights.get('max_drawdown', 0),
|
|
125
|
+
"rsi": insights.get('rsi', 50)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Also add top-level access for backward compatibility
|
|
130
|
+
# These match the keys that users are accessing directly
|
|
131
|
+
if perf_summary:
|
|
132
|
+
result["sharpe_ratio"] = perf_summary.get('sharpe_ratio', insights.get('sharpe_ratio', 0))
|
|
133
|
+
result["max_drawdown"] = perf_summary.get('max_drawdown', insights.get('max_drawdown', 0))
|
|
134
|
+
result["total_return"] = perf_summary.get('total_return', insights.get('total_return', 0))
|
|
135
|
+
result["annualized_return"] = perf_summary.get('annualized_return', 0)
|
|
136
|
+
result["annualized_volatility"] = perf_summary.get('annualized_volatility', 0)
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
return {
|
|
142
|
+
"symbol": symbol,
|
|
143
|
+
"error": str(e),
|
|
144
|
+
"insights": ["Error in analysis"]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def portfolio_analysis(portfolio_data, risk_free_rate=0.08):
|
|
149
|
+
"""
|
|
150
|
+
Comprehensive portfolio analysis function.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
portfolio_data: Dictionary of symbol -> DataFrame
|
|
154
|
+
risk_free_rate: Risk-free rate for calculations (default: 0.08)
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dictionary containing portfolio analysis results
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
>>> portfolio = {
|
|
161
|
+
... "OGDC": pypsx_toolkit.PSXTicker("OGDC").history(period="1y"),
|
|
162
|
+
... "PPL": pypsx_toolkit.PSXTicker("PPL").history(period="1y"),
|
|
163
|
+
... "KEL": pypsx_toolkit.PSXTicker("KEL").history(period="1y")
|
|
164
|
+
... }
|
|
165
|
+
>>> analysis = portfolio_analysis(portfolio)
|
|
166
|
+
>>> print(f"Portfolio insights: {analysis['portfolio_insights']}")
|
|
167
|
+
"""
|
|
168
|
+
if not portfolio_data:
|
|
169
|
+
return {"error": "No portfolio data provided"}
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Get portfolio insights
|
|
173
|
+
portfolio_insights = interpret_portfolio(portfolio_data, risk_free_rate)
|
|
174
|
+
|
|
175
|
+
# Get market sentiment
|
|
176
|
+
sentiment = market_sentiment_analysis(portfolio_data)
|
|
177
|
+
|
|
178
|
+
# Individual stock analysis
|
|
179
|
+
individual_analyses = {}
|
|
180
|
+
for symbol, df in portfolio_data.items():
|
|
181
|
+
if df is not None and not df.empty:
|
|
182
|
+
individual_analyses[symbol] = quick_analysis(df, symbol)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
"portfolio_insights": portfolio_insights.get('portfolio_insights', []),
|
|
186
|
+
"portfolio_metrics": portfolio_insights.get('portfolio_metrics', {}),
|
|
187
|
+
"market_sentiment": sentiment.get('overall_sentiment', 'NEUTRAL'),
|
|
188
|
+
"sentiment_strength": sentiment.get('sentiment_strength', 'Mixed'),
|
|
189
|
+
"individual_analyses": individual_analyses,
|
|
190
|
+
"summary": {
|
|
191
|
+
"total_stocks": len(portfolio_data),
|
|
192
|
+
"analyzed_stocks": len(individual_analyses),
|
|
193
|
+
"bullish_stocks": sentiment.get('bullish_stocks', 0),
|
|
194
|
+
"bearish_stocks": sentiment.get('bearish_stocks', 0),
|
|
195
|
+
"neutral_stocks": sentiment.get('neutral_stocks', 0)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
return {
|
|
201
|
+
"error": str(e),
|
|
202
|
+
"portfolio_insights": ["Error in portfolio analysis"]
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# Add convenience aliases
|
|
207
|
+
ma = moving_average
|
|
208
|
+
ema = exponential_moving_average
|
|
209
|
+
bb = bollinger_bands
|
|
210
|
+
sharpe = sharpe_ratio
|
|
211
|
+
sortino = sortino_ratio
|
|
212
|
+
calmar = calmar_ratio
|
|
213
|
+
cum_returns = cumulative_returns
|
|
214
|
+
max_dd = max_drawdown
|
|
215
|
+
win_rate_pct = win_rate
|
|
216
|
+
pl_ratio = profit_loss_ratio
|
|
217
|
+
recovery = recovery_factor
|
|
218
|
+
perf_summary = performance_summary
|
|
219
|
+
interpret = interpret_stock
|
|
220
|
+
patterns = detect_patterns
|
|
221
|
+
signals = generate_trading_signals
|
|
222
|
+
sentiment = market_sentiment_analysis
|