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,348 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type stubs for pypsx.analysis — statistical analysis, technical indicators,
|
|
3
|
+
performance metrics, and AI-powered insights.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
>>> import pypsx
|
|
7
|
+
>>> t = pypsx.Ticker("ENGRO")
|
|
8
|
+
>>> df = t.history(period="1y")
|
|
9
|
+
>>>
|
|
10
|
+
>>> # Technical indicators
|
|
11
|
+
>>> sma_20 = pypsx.analysis.moving_average(df, window=20)
|
|
12
|
+
>>> upper, mid, lower = pypsx.analysis.bollinger_bands(df)
|
|
13
|
+
>>> rsi = pypsx.analysis.rsi(df)
|
|
14
|
+
>>>
|
|
15
|
+
>>> # Performance metrics
|
|
16
|
+
>>> print(pypsx.analysis.sharpe_ratio(df))
|
|
17
|
+
>>> print(pypsx.analysis.max_drawdown(df))
|
|
18
|
+
>>>
|
|
19
|
+
>>> # Full summary
|
|
20
|
+
>>> summary = pypsx.analysis.performance_summary(df)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
24
|
+
import pandas as pd
|
|
25
|
+
|
|
26
|
+
# =============================================================================
|
|
27
|
+
# Statistical Functions
|
|
28
|
+
# =============================================================================
|
|
29
|
+
|
|
30
|
+
def returns(df: pd.DataFrame) -> float:
|
|
31
|
+
"""Total return for the period."""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def volatility(df: pd.DataFrame, annualize: bool = True) -> float:
|
|
35
|
+
"""Annualised return volatility (std dev of log returns)."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
def correlation(df1: pd.DataFrame, df2: pd.DataFrame) -> float:
|
|
39
|
+
"""Pearson correlation coefficient between two price series."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
def correlation_matrix(portfolio_data: Dict[str, pd.DataFrame]) -> pd.DataFrame:
|
|
43
|
+
"""Correlation matrix for a portfolio of symbols."""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
def beta(df: pd.DataFrame, market_returns: pd.Series) -> float:
|
|
47
|
+
"""Beta coefficient relative to a market index."""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def skewness(df: pd.DataFrame) -> float:
|
|
51
|
+
"""Skewness of the return distribution."""
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
def kurtosis(df: pd.DataFrame) -> float:
|
|
55
|
+
"""Excess kurtosis of the return distribution."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def var(df: pd.DataFrame, confidence: float = 0.95) -> float:
|
|
59
|
+
"""Historical Value at Risk at the given confidence level."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
def cvar(df: pd.DataFrame, confidence: float = 0.95) -> float:
|
|
63
|
+
"""Conditional Value at Risk (Expected Shortfall)."""
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
def autocorrelation(df: pd.DataFrame, lag: int = 1) -> float:
|
|
67
|
+
"""Autocorrelation of returns at the given lag."""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
# =============================================================================
|
|
71
|
+
# Technical Indicators
|
|
72
|
+
# =============================================================================
|
|
73
|
+
|
|
74
|
+
def moving_average(df: pd.DataFrame, window: int = 20) -> pd.Series:
|
|
75
|
+
"""Simple Moving Average (SMA).
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
df: OHLCV DataFrame (uses Close column).
|
|
79
|
+
window: Rolling window in bars (default 20).
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
pd.Series of SMA values.
|
|
83
|
+
"""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
def exponential_moving_average(df: pd.DataFrame, window: int = 20) -> pd.Series:
|
|
87
|
+
"""Exponential Moving Average (EMA).
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
df: OHLCV DataFrame.
|
|
91
|
+
window: Span for EMA calculation.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
pd.Series of EMA values.
|
|
95
|
+
"""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
def bollinger_bands(
|
|
99
|
+
df: pd.DataFrame,
|
|
100
|
+
window: int = 20,
|
|
101
|
+
num_std: float = 2.0,
|
|
102
|
+
) -> Tuple[pd.Series, pd.Series, pd.Series]:
|
|
103
|
+
"""
|
|
104
|
+
Bollinger Bands.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Tuple of (upper_band, middle_band, lower_band) as pd.Series.
|
|
108
|
+
"""
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
def rsi(df: pd.DataFrame, window: int = 14) -> pd.Series:
|
|
112
|
+
"""Relative Strength Index (0–100)."""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
def macd(
|
|
116
|
+
df: pd.DataFrame,
|
|
117
|
+
fast: int = 12,
|
|
118
|
+
slow: int = 26,
|
|
119
|
+
signal: int = 9,
|
|
120
|
+
) -> Tuple[pd.Series, pd.Series, pd.Series]:
|
|
121
|
+
"""
|
|
122
|
+
MACD indicator.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Tuple of (macd_line, signal_line, histogram).
|
|
126
|
+
"""
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
def stochastic(
|
|
130
|
+
df: pd.DataFrame,
|
|
131
|
+
window: int = 14,
|
|
132
|
+
) -> Tuple[pd.Series, pd.Series]:
|
|
133
|
+
"""
|
|
134
|
+
Stochastic Oscillator (%K and %D).
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Tuple of (%K, %D) as pd.Series.
|
|
138
|
+
"""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
def williams_r(df: pd.DataFrame, window: int = 14) -> pd.Series:
|
|
142
|
+
"""Williams %R oscillator (-100 to 0)."""
|
|
143
|
+
...
|
|
144
|
+
|
|
145
|
+
def atr(df: pd.DataFrame, window: int = 14) -> pd.Series:
|
|
146
|
+
"""Average True Range."""
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
def adx(df: pd.DataFrame, window: int = 14) -> pd.Series:
|
|
150
|
+
"""Average Directional Index (trend strength 0–100)."""
|
|
151
|
+
...
|
|
152
|
+
|
|
153
|
+
def cci(df: pd.DataFrame, window: int = 20) -> pd.Series:
|
|
154
|
+
"""Commodity Channel Index."""
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
def obv(df: pd.DataFrame) -> pd.Series:
|
|
158
|
+
"""On-Balance Volume."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
def vwap(df: pd.DataFrame) -> pd.Series:
|
|
162
|
+
"""Volume Weighted Average Price."""
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
# =============================================================================
|
|
166
|
+
# Performance Metrics
|
|
167
|
+
# =============================================================================
|
|
168
|
+
|
|
169
|
+
def sharpe_ratio(df: pd.DataFrame, risk_free_rate: float = 0.10) -> float:
|
|
170
|
+
"""
|
|
171
|
+
Annualised Sharpe Ratio.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
df: OHLCV DataFrame with daily close prices.
|
|
175
|
+
risk_free_rate: Annual risk-free rate (default 10% for Pakistan T-bills).
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Sharpe ratio (higher is better; >1 is acceptable, >2 is excellent).
|
|
179
|
+
"""
|
|
180
|
+
...
|
|
181
|
+
|
|
182
|
+
def sortino_ratio(df: pd.DataFrame, risk_free_rate: float = 0.10) -> float:
|
|
183
|
+
"""Annualised Sortino Ratio (penalises only downside volatility)."""
|
|
184
|
+
...
|
|
185
|
+
|
|
186
|
+
def calmar_ratio(df: pd.DataFrame) -> float:
|
|
187
|
+
"""Calmar Ratio = annualised return / max drawdown."""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
def cumulative_returns(df: pd.DataFrame) -> float:
|
|
191
|
+
"""Total cumulative return for the period as a decimal (e.g. 0.35 = 35%)."""
|
|
192
|
+
...
|
|
193
|
+
|
|
194
|
+
def annualized_return(df: pd.DataFrame) -> float:
|
|
195
|
+
"""Compound Annual Growth Rate (CAGR)."""
|
|
196
|
+
...
|
|
197
|
+
|
|
198
|
+
def annualized_volatility(df: pd.DataFrame) -> float:
|
|
199
|
+
"""Annualised standard deviation of daily returns."""
|
|
200
|
+
...
|
|
201
|
+
|
|
202
|
+
def drawdown(df: pd.DataFrame) -> pd.Series:
|
|
203
|
+
"""Rolling drawdown series (0 to -1 scale)."""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
def max_drawdown(df: pd.DataFrame) -> float:
|
|
207
|
+
"""Maximum peak-to-trough drawdown as a negative decimal (e.g. -0.35 = -35%)."""
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
def drawdown_duration(df: pd.DataFrame) -> int:
|
|
211
|
+
"""Length of the longest drawdown period in trading days."""
|
|
212
|
+
...
|
|
213
|
+
|
|
214
|
+
def information_ratio(df: pd.DataFrame, benchmark: pd.DataFrame) -> float:
|
|
215
|
+
"""Information Ratio = active return / tracking error."""
|
|
216
|
+
...
|
|
217
|
+
|
|
218
|
+
def treynor_ratio(df: pd.DataFrame, beta_value: float) -> float:
|
|
219
|
+
"""Treynor Ratio = excess return / beta."""
|
|
220
|
+
...
|
|
221
|
+
|
|
222
|
+
def jensen_alpha(df: pd.DataFrame, expected_return: float) -> float:
|
|
223
|
+
"""Jensen's Alpha = actual return - CAPM-expected return."""
|
|
224
|
+
...
|
|
225
|
+
|
|
226
|
+
def win_rate(trades: pd.DataFrame) -> float:
|
|
227
|
+
"""Percentage of profitable trades (0.0–1.0)."""
|
|
228
|
+
...
|
|
229
|
+
|
|
230
|
+
def profit_loss_ratio(trades: pd.DataFrame) -> float:
|
|
231
|
+
"""Average profit of winning trades / average loss of losing trades."""
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
def recovery_factor(trades: pd.DataFrame) -> float:
|
|
235
|
+
"""Net profit / max drawdown."""
|
|
236
|
+
...
|
|
237
|
+
|
|
238
|
+
def performance_summary(df: pd.DataFrame) -> Dict[str, Any]:
|
|
239
|
+
"""
|
|
240
|
+
Compute a comprehensive performance report.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Dict with keys: total_return, annualized_return, annualized_volatility,
|
|
244
|
+
sharpe_ratio, sortino_ratio, calmar_ratio, max_drawdown, win_rate,
|
|
245
|
+
best_day, worst_day, positive_days_pct.
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
>>> summary = pypsx.analysis.performance_summary(df)
|
|
249
|
+
>>> print(summary["sharpe_ratio"])
|
|
250
|
+
"""
|
|
251
|
+
...
|
|
252
|
+
|
|
253
|
+
# =============================================================================
|
|
254
|
+
# Insights & Pattern Recognition
|
|
255
|
+
# =============================================================================
|
|
256
|
+
|
|
257
|
+
def interpret_stock(df: pd.DataFrame, symbol: str) -> Dict[str, Any]:
|
|
258
|
+
"""
|
|
259
|
+
AI-powered interpretation of a stock's technical and statistical profile.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Dict with keys: trend, momentum, volatility_level, support, resistance,
|
|
263
|
+
signals, outlook.
|
|
264
|
+
"""
|
|
265
|
+
...
|
|
266
|
+
|
|
267
|
+
def interpret_portfolio(
|
|
268
|
+
portfolio_data: Dict[str, pd.DataFrame],
|
|
269
|
+
risk_free_rate: float = 0.10,
|
|
270
|
+
) -> Dict[str, Any]:
|
|
271
|
+
"""
|
|
272
|
+
Holistic portfolio analysis including diversification and risk metrics.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
portfolio_data: Dict mapping symbol → OHLCV DataFrame.
|
|
276
|
+
risk_free_rate: Annual risk-free rate.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dict with per-symbol and aggregate metrics.
|
|
280
|
+
"""
|
|
281
|
+
...
|
|
282
|
+
|
|
283
|
+
def detect_patterns(df: pd.DataFrame, symbol: str) -> Dict[str, Any]:
|
|
284
|
+
"""
|
|
285
|
+
Detect common chart patterns (head-and-shoulders, double top/bottom, etc.).
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Dict with pattern names as keys and confidence scores as values.
|
|
289
|
+
"""
|
|
290
|
+
...
|
|
291
|
+
|
|
292
|
+
def generate_trading_signals(df: pd.DataFrame, symbol: str) -> Dict[str, Any]:
|
|
293
|
+
"""
|
|
294
|
+
Generate buy/sell/hold signals based on technical indicator confluence.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Dict with keys: signal ("BUY"/"SELL"/"HOLD"), confidence (0–1),
|
|
298
|
+
indicators_agreed, indicators_disagreed.
|
|
299
|
+
"""
|
|
300
|
+
...
|
|
301
|
+
|
|
302
|
+
def market_sentiment_analysis(
|
|
303
|
+
portfolio_data: Dict[str, pd.DataFrame],
|
|
304
|
+
) -> Dict[str, Any]:
|
|
305
|
+
"""
|
|
306
|
+
Aggregate sentiment analysis across a portfolio.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Dict with overall_sentiment, bullish_count, bearish_count, neutral_count.
|
|
310
|
+
"""
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
def quick_analysis(df: pd.DataFrame, symbol: str) -> Dict[str, Any]:
|
|
314
|
+
"""
|
|
315
|
+
Fast single-call analysis combining key indicators and a brief narrative.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Dict with price_action, trend, momentum, key_levels, one_line_summary.
|
|
319
|
+
"""
|
|
320
|
+
...
|
|
321
|
+
|
|
322
|
+
def portfolio_analysis(
|
|
323
|
+
portfolio_data: Dict[str, pd.DataFrame],
|
|
324
|
+
risk_free_rate: float = 0.10,
|
|
325
|
+
) -> Dict[str, Any]:
|
|
326
|
+
"""Full portfolio-level analysis including correlation, risk, and attribution."""
|
|
327
|
+
...
|
|
328
|
+
|
|
329
|
+
# =============================================================================
|
|
330
|
+
# Short-form aliases
|
|
331
|
+
# =============================================================================
|
|
332
|
+
|
|
333
|
+
ma = moving_average
|
|
334
|
+
ema = exponential_moving_average
|
|
335
|
+
bb = bollinger_bands
|
|
336
|
+
sharpe = sharpe_ratio
|
|
337
|
+
sortino = sortino_ratio
|
|
338
|
+
calmar = calmar_ratio
|
|
339
|
+
cum_returns = cumulative_returns
|
|
340
|
+
max_dd = max_drawdown
|
|
341
|
+
win_rate_pct = win_rate
|
|
342
|
+
pl_ratio = profit_loss_ratio
|
|
343
|
+
recovery = recovery_factor
|
|
344
|
+
perf_summary = performance_summary
|
|
345
|
+
interpret = interpret_stock
|
|
346
|
+
patterns = detect_patterns
|
|
347
|
+
signals = generate_trading_signals
|
|
348
|
+
sentiment = market_sentiment_analysis
|
pypsx_toolkit/api.pyi
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Type stubs for pypsx.api — high-level data retrieval functions."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def download(
|
|
8
|
+
symbols: Union[str, List[str]],
|
|
9
|
+
period: str = "1mo",
|
|
10
|
+
interval: str = "1d",
|
|
11
|
+
to_csv: Optional[str] = None,
|
|
12
|
+
) -> pd.DataFrame:
|
|
13
|
+
"""
|
|
14
|
+
Download historical OHLCV data for one or more PSX symbols (yfinance-style).
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
symbols: Single symbol string or list of symbols, e.g. ["ENGRO", "HBL", "OGDC"].
|
|
18
|
+
period: Time period — "1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max".
|
|
19
|
+
interval: Bar interval. Only "1d" is currently supported.
|
|
20
|
+
to_csv: Optional path to save the result as CSV.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
For a single symbol: DataFrame with [Open, High, Low, Close, Volume, Change%].
|
|
24
|
+
For multiple symbols: Multi-level column DataFrame keyed by (field, symbol).
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> import pypsx
|
|
28
|
+
>>> df = pypsx.download("ENGRO", period="1y")
|
|
29
|
+
>>> df = pypsx.download(["ENGRO", "HBL", "OGDC"], period="6mo")
|
|
30
|
+
"""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_historical(
|
|
35
|
+
symbol: str,
|
|
36
|
+
start_date: str,
|
|
37
|
+
end_date: str,
|
|
38
|
+
max_workers: int = 4,
|
|
39
|
+
show_progress: bool = False,
|
|
40
|
+
) -> pd.DataFrame:
|
|
41
|
+
"""
|
|
42
|
+
Fetch historical daily OHLCV data for a specific date range.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
symbol: PSX ticker symbol, e.g. "ENGRO".
|
|
46
|
+
start_date: Start date in "YYYY-MM-DD" format.
|
|
47
|
+
end_date: End date in "YYYY-MM-DD" format.
|
|
48
|
+
max_workers: Number of parallel HTTP workers for large date ranges.
|
|
49
|
+
show_progress: Show tqdm progress bar if True.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame with columns [Date, Open, High, Low, Close, Volume, Change%].
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> df = pypsx.get_historical("OGDC", "2019-01-01", "2024-12-31")
|
|
56
|
+
"""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_intraday(symbol: str) -> pd.DataFrame:
|
|
61
|
+
"""
|
|
62
|
+
Fetch intraday tick data for the last 2 trading days.
|
|
63
|
+
|
|
64
|
+
Runs on the user's local machine to distribute scraping traffic.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
symbol: PSX ticker symbol.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
DataFrame with columns [Time, Price, Volume, Change, Change%].
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> ticks = pypsx.get_intraday("SYS")
|
|
74
|
+
"""
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_intraday_multiple(symbols: List[str]) -> pd.DataFrame:
|
|
79
|
+
"""
|
|
80
|
+
Fetch intraday tick data for multiple symbols in parallel.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
symbols: List of PSX ticker symbols.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Concatenated DataFrame with an additional "Symbol" column.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
>>> df = pypsx.get_intraday_multiple(["ENGRO", "HBL", "SYS"])
|
|
90
|
+
"""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_quote(
|
|
95
|
+
symbol: str,
|
|
96
|
+
as_dict: bool = False,
|
|
97
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
98
|
+
"""
|
|
99
|
+
Get the latest price quote for a single symbol.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
symbol: PSX ticker symbol.
|
|
103
|
+
as_dict: If True, return a dict instead of a DataFrame.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
DataFrame row or dict with fields: symbol, price, change, change_pct,
|
|
107
|
+
volume, high, low, open, prev_close, market_cap.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> quote = pypsx.get_quote("ENGRO")
|
|
111
|
+
>>> quote = pypsx.get_quote("ENGRO", as_dict=True)
|
|
112
|
+
"""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_quote_batch(
|
|
117
|
+
symbols: List[str],
|
|
118
|
+
as_dict: bool = False,
|
|
119
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
120
|
+
"""
|
|
121
|
+
Get latest price quotes for multiple symbols at once.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
symbols: List of PSX ticker symbols.
|
|
125
|
+
as_dict: If True, return a dict keyed by symbol.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
DataFrame with one row per symbol, or nested dict if as_dict=True.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> df = pypsx.get_quote_batch(["ENGRO", "HBL", "OGDC"])
|
|
132
|
+
"""
|
|
133
|
+
...
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_company_fundamentals(
|
|
137
|
+
symbol: str,
|
|
138
|
+
as_dict: bool = False,
|
|
139
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
140
|
+
"""
|
|
141
|
+
Fetch company fundamental data (P/E, EPS, book value, ROE, etc.).
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
symbol: PSX ticker symbol.
|
|
145
|
+
as_dict: Return dict instead of DataFrame if True.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
DataFrame or dict with keys: pe_ratio, eps, book_value, dividend_yield,
|
|
149
|
+
roe, roa, debt_to_equity, current_ratio, market_cap, shares_outstanding.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
>>> fundamentals = pypsx.get_company_fundamentals("HUBC")
|
|
153
|
+
"""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_announcements(
|
|
158
|
+
symbol: str,
|
|
159
|
+
as_dict: bool = False,
|
|
160
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
161
|
+
"""
|
|
162
|
+
Fetch PSX corporate announcements for a symbol.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
symbol: PSX ticker symbol.
|
|
166
|
+
as_dict: Return list of dicts if True.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
DataFrame with columns [Date, Category, Subject, Description].
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
>>> df = pypsx.get_announcements("ENGRO")
|
|
173
|
+
"""
|
|
174
|
+
...
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_dividend_info(
|
|
178
|
+
symbol: str,
|
|
179
|
+
as_dict: bool = False,
|
|
180
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
181
|
+
"""
|
|
182
|
+
Get current/latest dividend information for a symbol.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
symbol: PSX ticker symbol.
|
|
186
|
+
as_dict: Return dict if True.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
DataFrame or dict with last dividend rate, type, ex-date, payment date.
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
>>> info = pypsx.get_dividend_info("OGDC")
|
|
193
|
+
"""
|
|
194
|
+
...
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def get_dividend_history(
|
|
198
|
+
symbol: str,
|
|
199
|
+
as_dict: bool = False,
|
|
200
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
201
|
+
"""
|
|
202
|
+
Full dividend payment history for a symbol (cash, stock, bonus, rights).
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
symbol: PSX ticker symbol.
|
|
206
|
+
as_dict: Return list of dicts if True.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
DataFrame with columns [Date, Type, Rate, Face_Value, Announcement_Date].
|
|
210
|
+
|
|
211
|
+
Example:
|
|
212
|
+
>>> history = pypsx.get_dividend_history("ENGRO")
|
|
213
|
+
"""
|
|
214
|
+
...
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_snapshot(
|
|
218
|
+
symbol: str,
|
|
219
|
+
as_dict: bool = False,
|
|
220
|
+
) -> Union[Dict[str, Any], pd.DataFrame]:
|
|
221
|
+
"""
|
|
222
|
+
Multi-tab market snapshot combining price, fundamentals, and trading stats.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
symbol: PSX ticker symbol.
|
|
226
|
+
as_dict: Return nested dict if True (default).
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Nested dict / DataFrame with tabs: price, fundamentals, trading_stats, company.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
>>> snap = pypsx.get_snapshot("SYS")
|
|
233
|
+
>>> print(snap["price"]["last_price"])
|
|
234
|
+
"""
|
|
235
|
+
...
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def get_sector_constituents(
|
|
239
|
+
sector_code: str,
|
|
240
|
+
as_dict: bool = False,
|
|
241
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
242
|
+
"""
|
|
243
|
+
Get all companies belonging to a PSX sector by sector code.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
sector_code: PSX sector code (e.g. "BAN" for Banking, "OGX" for Oil & Gas).
|
|
247
|
+
as_dict: Return list of dicts if True.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
DataFrame with columns [Symbol, Company, Market_Cap, Change%, Volume].
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
>>> df = pypsx.get_sector_constituents("BAN")
|
|
254
|
+
"""
|
|
255
|
+
...
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def get_symbols_by_sector(
|
|
259
|
+
sector_name: str,
|
|
260
|
+
as_dict: bool = False,
|
|
261
|
+
) -> Union[pd.DataFrame, Dict[str, Any]]:
|
|
262
|
+
"""
|
|
263
|
+
Get symbols belonging to a PSX sector by human-readable sector name.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
sector_name: Full sector name, e.g. "Banking", "Automobile Assembler",
|
|
267
|
+
"Oil & Gas Exploration Companies".
|
|
268
|
+
as_dict: Return list of dicts if True.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
DataFrame with constituent symbols and company names.
|
|
272
|
+
|
|
273
|
+
Example:
|
|
274
|
+
>>> df = pypsx.get_symbols_by_sector("Banking")
|
|
275
|
+
>>> df = pypsx.get_symbols_by_sector("Cement")
|
|
276
|
+
"""
|
|
277
|
+
...
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def get_business_description(symbol: str) -> str:
|
|
281
|
+
"""
|
|
282
|
+
Return a plain-text business description for a PSX-listed company.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
symbol: PSX ticker symbol.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
String with the company's business overview.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> desc = pypsx.get_business_description("ENGRO")
|
|
292
|
+
>>> print(desc[:200])
|
|
293
|
+
"""
|
|
294
|
+
...
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Type stubs for pypsx.core.errors — custom exception hierarchy."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PSXError(Exception):
|
|
5
|
+
"""Base exception for all pypsx errors."""
|
|
6
|
+
...
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PSXHTTPError(PSXError):
|
|
10
|
+
"""
|
|
11
|
+
Raised when the PSX server returns an unexpected HTTP status code.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
status_code: The HTTP status code received.
|
|
15
|
+
url: The URL that triggered the error.
|
|
16
|
+
"""
|
|
17
|
+
status_code: int
|
|
18
|
+
url: str
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PSXTimeoutError(PSXError):
|
|
23
|
+
"""Raised when an HTTP request to PSX exceeds the configured timeout."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PSXNotFoundError(PSXError):
|
|
28
|
+
"""Raised when a requested symbol, sector, or index does not exist on PSX."""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PSXScopeError(PSXError):
|
|
33
|
+
"""
|
|
34
|
+
Raised when a method is called on the wrong ticker type.
|
|
35
|
+
|
|
36
|
+
Example: calling .constituents() on an equity ticker instead of an index ticker.
|
|
37
|
+
"""
|
|
38
|
+
...
|