pypsx-toolkit 3.0.2__tar.gz → 3.0.4__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.
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/PKG-INFO +10 -20
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/README.md +3 -12
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pyproject.toml +24 -14
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/__init__.py +86 -14
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/analysis/__init__.py +10 -1
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/api.pyi +0 -2
- pypsx_toolkit-3.0.4/setup.py +103 -0
- pypsx_toolkit-3.0.2/setup.py +0 -70
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/LICENSE +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/MANIFEST.in +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/__init__.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/analysis/__init__.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/__init__.py +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/errors.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/stream.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/endpoints/__init__.py +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/endpoints/constants.py +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/format/__init__.py +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/market.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/models.py +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/models.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/py.typed +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/ticker.pyi +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit.egg-info/SOURCES.txt +0 -0
- {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/setup.cfg +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypsx-toolkit
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.4
|
|
4
4
|
Summary: Pakistan Stock Exchange (PSX) data library with yfinance-like API
|
|
5
|
-
Author-email: PyPSX Team <
|
|
6
|
-
Maintainer-email: PyPSX Team <
|
|
5
|
+
Author-email: PyPSX Team <pypsxofficial@gmail.com>
|
|
6
|
+
Maintainer-email: PyPSX Team <pypsxofficial@gmail.com>
|
|
7
7
|
License: Copyright (c) 2026 PyPSX Team. All rights reserved.
|
|
8
8
|
|
|
9
9
|
This software and associated documentation files (the "Software") are the
|
|
@@ -32,8 +32,6 @@ Classifier: Intended Audience :: Financial and Insurance Industry
|
|
|
32
32
|
Classifier: License :: Other/Proprietary License
|
|
33
33
|
Classifier: Operating System :: OS Independent
|
|
34
34
|
Classifier: Programming Language :: Python :: 3
|
|
35
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
36
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
37
35
|
Classifier: Programming Language :: Python :: 3.10
|
|
38
36
|
Classifier: Programming Language :: Python :: 3.11
|
|
39
37
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -41,15 +39,16 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
41
39
|
Classifier: Topic :: Office/Business :: Financial
|
|
42
40
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
43
41
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
44
|
-
Requires-Python: >=3.
|
|
42
|
+
Requires-Python: >=3.10
|
|
45
43
|
Description-Content-Type: text/markdown
|
|
46
44
|
License-File: LICENSE
|
|
47
45
|
Requires-Dist: requests>=2.31.0
|
|
48
46
|
Requires-Dist: beautifulsoup4>=4.12.0
|
|
49
47
|
Requires-Dist: lxml>=4.9.0
|
|
50
|
-
Requires-Dist: pandas
|
|
48
|
+
Requires-Dist: pandas<2.3.3,>=2.0.0
|
|
51
49
|
Requires-Dist: loguru>=0.7.0
|
|
52
|
-
Requires-Dist: numpy
|
|
50
|
+
Requires-Dist: numpy<2.3.0,>=1.24.0
|
|
51
|
+
Requires-Dist: tqdm>=4.66.0
|
|
53
52
|
Provides-Extra: plot
|
|
54
53
|
Requires-Dist: matplotlib>=3.5.0; extra == "plot"
|
|
55
54
|
Requires-Dist: seaborn>=0.11.0; extra == "plot"
|
|
@@ -67,7 +66,7 @@ Dynamic: license-file
|
|
|
67
66
|
|
|
68
67
|
# pypsx-toolkit — Pakistan Stock Exchange Data Library
|
|
69
68
|
|
|
70
|
-
A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) data
|
|
69
|
+
A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) market data. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
|
|
71
70
|
|
|
72
71
|
## Installation
|
|
73
72
|
|
|
@@ -505,17 +504,6 @@ historical = ticker.get_historical(
|
|
|
505
504
|
print(historical.head())
|
|
506
505
|
```
|
|
507
506
|
|
|
508
|
-
## Data Sources
|
|
509
|
-
|
|
510
|
-
PyPSX Toolkit uses official Pakistan Stock Exchange DPS endpoints:
|
|
511
|
-
- Market Watch: `https://dps.psx.com.pk/market-watch`
|
|
512
|
-
- Sector Summary: `https://dps.psx.com.pk/sector-summary/sectorwise`
|
|
513
|
-
- Indices: `https://dps.psx.com.pk/indices/{INDEX}`
|
|
514
|
-
- Trading Board: `https://dps.psx.com.pk/trading-board/REG/main`
|
|
515
|
-
- Performers: `https://dps.psx.com.pk/performers`
|
|
516
|
-
- Timeseries: `https://dps.psx.com.pk/timeseries/int/{SYMBOL}`, `.../eod/{SYMBOL}`
|
|
517
|
-
- Historical OHLCV: `https://dps.psx.com.pk/historical`
|
|
518
|
-
|
|
519
507
|
## Examples
|
|
520
508
|
|
|
521
509
|
### Example 1: Find Top Volume Stocks
|
|
@@ -604,6 +592,7 @@ for insight in analysis["portfolio_insights"]:
|
|
|
604
592
|
|
|
605
593
|
### PSXTicker Class
|
|
606
594
|
|
|
595
|
+
<!-- pypsx-test: skip: API signature reference, not executable code (shows type annotations in call position) -->
|
|
607
596
|
```python
|
|
608
597
|
ticker = pypsx_toolkit.PSXTicker(symbol: str)
|
|
609
598
|
|
|
@@ -649,6 +638,7 @@ pypsx_toolkit.get_snapshot(symbol) # Get snapshot data from all tab
|
|
|
649
638
|
|
|
650
639
|
### Download Functions
|
|
651
640
|
|
|
641
|
+
<!-- pypsx-test: skip: API signature reference; parameter names are placeholders, not bound values -->
|
|
652
642
|
```python
|
|
653
643
|
pypsx_toolkit.download(symbols, period="1y", interval="1d") # Batch download
|
|
654
644
|
pypsx_toolkit.get_intraday_multiple(symbols) # Multiple intraday
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pypsx-toolkit — Pakistan Stock Exchange Data Library
|
|
2
2
|
|
|
3
|
-
A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) data
|
|
3
|
+
A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) market data. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -438,17 +438,6 @@ historical = ticker.get_historical(
|
|
|
438
438
|
print(historical.head())
|
|
439
439
|
```
|
|
440
440
|
|
|
441
|
-
## Data Sources
|
|
442
|
-
|
|
443
|
-
PyPSX Toolkit uses official Pakistan Stock Exchange DPS endpoints:
|
|
444
|
-
- Market Watch: `https://dps.psx.com.pk/market-watch`
|
|
445
|
-
- Sector Summary: `https://dps.psx.com.pk/sector-summary/sectorwise`
|
|
446
|
-
- Indices: `https://dps.psx.com.pk/indices/{INDEX}`
|
|
447
|
-
- Trading Board: `https://dps.psx.com.pk/trading-board/REG/main`
|
|
448
|
-
- Performers: `https://dps.psx.com.pk/performers`
|
|
449
|
-
- Timeseries: `https://dps.psx.com.pk/timeseries/int/{SYMBOL}`, `.../eod/{SYMBOL}`
|
|
450
|
-
- Historical OHLCV: `https://dps.psx.com.pk/historical`
|
|
451
|
-
|
|
452
441
|
## Examples
|
|
453
442
|
|
|
454
443
|
### Example 1: Find Top Volume Stocks
|
|
@@ -537,6 +526,7 @@ for insight in analysis["portfolio_insights"]:
|
|
|
537
526
|
|
|
538
527
|
### PSXTicker Class
|
|
539
528
|
|
|
529
|
+
<!-- pypsx-test: skip: API signature reference, not executable code (shows type annotations in call position) -->
|
|
540
530
|
```python
|
|
541
531
|
ticker = pypsx_toolkit.PSXTicker(symbol: str)
|
|
542
532
|
|
|
@@ -582,6 +572,7 @@ pypsx_toolkit.get_snapshot(symbol) # Get snapshot data from all tab
|
|
|
582
572
|
|
|
583
573
|
### Download Functions
|
|
584
574
|
|
|
575
|
+
<!-- pypsx-test: skip: API signature reference; parameter names are placeholders, not bound values -->
|
|
585
576
|
```python
|
|
586
577
|
pypsx_toolkit.download(symbols, period="1y", interval="1d") # Batch download
|
|
587
578
|
pypsx_toolkit.get_intraday_multiple(symbols) # Multiple intraday
|
|
@@ -4,16 +4,18 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pypsx-toolkit"
|
|
7
|
-
version = "3.0.
|
|
7
|
+
version = "3.0.4"
|
|
8
8
|
description = "Pakistan Stock Exchange (PSX) data library with yfinance-like API"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
|
|
10
|
+
# 3.10 is the real floor: `pandas>=2.0` already excludes 3.8/3.9, and
|
|
11
|
+
# core/utils.py uses PEP 604 (`int | None`) in a runtime signature.
|
|
12
|
+
requires-python = ">=3.10"
|
|
11
13
|
license = { file = "LICENSE" }
|
|
12
14
|
authors = [
|
|
13
|
-
{ name = "PyPSX Team", email = "
|
|
15
|
+
{ name = "PyPSX Team", email = "pypsxofficial@gmail.com" }
|
|
14
16
|
]
|
|
15
17
|
maintainers = [
|
|
16
|
-
{ name = "PyPSX Team", email = "
|
|
18
|
+
{ name = "PyPSX Team", email = "pypsxofficial@gmail.com" }
|
|
17
19
|
]
|
|
18
20
|
keywords = ["psx", "pakistan", "stock", "finance", "data", "api", "pandas", "yfinance", "market-data"]
|
|
19
21
|
classifiers = [
|
|
@@ -23,8 +25,6 @@ classifiers = [
|
|
|
23
25
|
"License :: Other/Proprietary License",
|
|
24
26
|
"Operating System :: OS Independent",
|
|
25
27
|
"Programming Language :: Python :: 3",
|
|
26
|
-
"Programming Language :: Python :: 3.8",
|
|
27
|
-
"Programming Language :: Python :: 3.9",
|
|
28
28
|
"Programming Language :: Python :: 3.10",
|
|
29
29
|
"Programming Language :: Python :: 3.11",
|
|
30
30
|
"Programming Language :: Python :: 3.12",
|
|
@@ -38,9 +38,13 @@ dependencies = [
|
|
|
38
38
|
"requests>=2.31.0",
|
|
39
39
|
"beautifulsoup4>=4.12.0",
|
|
40
40
|
"lxml>=4.9.0",
|
|
41
|
-
"pandas>=2.0.0",
|
|
41
|
+
"pandas>=2.0.0,<2.3.3",
|
|
42
42
|
"loguru>=0.7.0",
|
|
43
|
-
"numpy>=1.24.0",
|
|
43
|
+
"numpy>=1.24.0,<2.3.0",
|
|
44
|
+
# Imported by api.py, endpoints/historical.py and core/utils.py. Those imports
|
|
45
|
+
# are try/except-guarded with a no-op fallback, but the fallback path is
|
|
46
|
+
# untested and every target environment (including Colab) already ships tqdm.
|
|
47
|
+
"tqdm>=4.66.0",
|
|
44
48
|
]
|
|
45
49
|
|
|
46
50
|
[project.optional-dependencies]
|
|
@@ -61,8 +65,12 @@ Homepage = "https://pypsx.com"
|
|
|
61
65
|
Documentation = "https://markets.pypsx.com/docs"
|
|
62
66
|
Changelog = "https://markets.pypsx.com/docs/changelog"
|
|
63
67
|
|
|
64
|
-
[project.scripts]
|
|
65
|
-
pypsx-toolkit = "pypsx_toolkit:quick_start"
|
|
68
|
+
# NOTE: no [project.scripts]. This previously declared
|
|
69
|
+
# pypsx-toolkit = "pypsx_toolkit:quick_start"
|
|
70
|
+
# but `quick_start` does not exist anywhere in the package, so the installed
|
|
71
|
+
# console script raised AttributeError on every invocation. This library has no
|
|
72
|
+
# CLI; removing the entry point is the honest fix. If a CLI is wanted later,
|
|
73
|
+
# implement the target function first.
|
|
66
74
|
|
|
67
75
|
[tool.setuptools]
|
|
68
76
|
include-package-data = true
|
|
@@ -152,8 +160,10 @@ markers = [
|
|
|
152
160
|
"integration: marks tests as integration tests",
|
|
153
161
|
"unit: marks tests as unit tests",
|
|
154
162
|
]
|
|
155
|
-
filterwarnings = [
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
# NOTE: deliberately no blanket `filterwarnings = ["ignore::DeprecationWarning"]`
|
|
164
|
+
# here. The Colab-mirror suite (b2c/tests/colab_mirror/) runs under
|
|
165
|
+
# `filterwarnings = error` because a warning in a notebook cell is a user-visible
|
|
166
|
+
# defect. A blanket ignore at this level hides exactly the class of bug that
|
|
167
|
+
# suite exists to catch. Allow-list individual warnings there, with an owner and
|
|
168
|
+
# an expiry, instead of re-adding a catch-all.
|
|
159
169
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
PSX yfinance-style API
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "3.0.
|
|
5
|
+
__version__ = "3.0.4"
|
|
6
6
|
__author__ = "PyPSX Team"
|
|
7
|
-
__email__ = "
|
|
7
|
+
__email__ = "pypsxofficial@gmail.com"
|
|
8
8
|
|
|
9
9
|
from pypsx_toolkit.ticker import PSXTicker, Ticker
|
|
10
10
|
|
|
@@ -37,6 +37,14 @@ from pypsx_toolkit.endpoints.sectors import get_sector_constituents as _get_sect
|
|
|
37
37
|
from pypsx_toolkit.endpoints.compliant_listings import get_symbols_by_sector as _get_symbols_by_sector
|
|
38
38
|
from pypsx_toolkit.endpoints.snapshot import get_snapshot
|
|
39
39
|
|
|
40
|
+
# Financial reports. Implemented and working, but previously not reachable from
|
|
41
|
+
# anywhere in the public surface.
|
|
42
|
+
from pypsx_toolkit.endpoints.reports import get_reports
|
|
43
|
+
|
|
44
|
+
# Cache control. Useful in notebooks, where a stale cached response is a common
|
|
45
|
+
# source of "why is my data wrong" confusion.
|
|
46
|
+
from pypsx_toolkit.core.cache import clear_pypsx_cache, get_cache_stats
|
|
47
|
+
|
|
40
48
|
from pypsx_toolkit.market import (
|
|
41
49
|
top_performers,
|
|
42
50
|
sector_summary,
|
|
@@ -70,18 +78,29 @@ from pypsx_toolkit.models import (
|
|
|
70
78
|
DividendHistory,
|
|
71
79
|
)
|
|
72
80
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
# Exceptions. Previously these were reachable only via the private
|
|
82
|
+
# `pypsx_toolkit.core.errors` path, which the docs told users to import from.
|
|
83
|
+
# Re-exported here as the supported location; the old path still works.
|
|
84
|
+
from pypsx_toolkit.core.errors import (
|
|
85
|
+
PSXError,
|
|
86
|
+
PSXHTTPError,
|
|
87
|
+
PSXTimeoutError,
|
|
88
|
+
PSXNotFoundError,
|
|
89
|
+
PSXScopeError,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Analysis module. This is NOT optional -- it depends only on pandas and numpy,
|
|
93
|
+
# both hard requirements of this package. It was previously wrapped in a
|
|
94
|
+
# `try/except ImportError: pass`, which silently turned any genuine bug inside
|
|
95
|
+
# the analysis package into a confusing AttributeError at call time.
|
|
96
|
+
from pypsx_toolkit import analysis
|
|
97
|
+
from pypsx_toolkit.analysis import (
|
|
98
|
+
interpret_stock,
|
|
99
|
+
sharpe_ratio,
|
|
100
|
+
rsi,
|
|
101
|
+
macd,
|
|
102
|
+
bollinger_bands,
|
|
103
|
+
)
|
|
85
104
|
|
|
86
105
|
__all__ = [
|
|
87
106
|
"__version__",
|
|
@@ -129,6 +148,14 @@ __all__ = [
|
|
|
129
148
|
"get_sector_constituents",
|
|
130
149
|
"get_symbols_by_sector",
|
|
131
150
|
"get_snapshot",
|
|
151
|
+
"get_reports",
|
|
152
|
+
# Cache control
|
|
153
|
+
"clear_pypsx_cache",
|
|
154
|
+
"get_cache_stats",
|
|
155
|
+
# NOTE: `charts` is deliberately NOT listed here. It resolves lazily via
|
|
156
|
+
# __getattr__ and needs the optional `[plot]` extra, so including it would
|
|
157
|
+
# make `from pypsx_toolkit import *` raise ImportError on a plain install.
|
|
158
|
+
# Access it as `pypsx_toolkit.charts` (see the Charting docs).
|
|
132
159
|
# Models
|
|
133
160
|
"SymbolInfo",
|
|
134
161
|
"SectorSummary",
|
|
@@ -147,8 +174,53 @@ __all__ = [
|
|
|
147
174
|
"Announcement",
|
|
148
175
|
"DividendInfo",
|
|
149
176
|
"DividendHistory",
|
|
177
|
+
# Exceptions. Also available at the original `pypsx_toolkit.core.errors` path.
|
|
178
|
+
"PSXError",
|
|
179
|
+
"PSXHTTPError",
|
|
180
|
+
"PSXTimeoutError",
|
|
181
|
+
"PSXNotFoundError",
|
|
182
|
+
"PSXScopeError",
|
|
183
|
+
# Analysis. `analysis` is the submodule (documented usage is
|
|
184
|
+
# `import pypsx_toolkit.analysis as pa`); the rest are convenience
|
|
185
|
+
# re-exports that were importable before but absent from __all__.
|
|
186
|
+
"analysis",
|
|
187
|
+
"interpret_stock",
|
|
188
|
+
"sharpe_ratio",
|
|
189
|
+
"rsi",
|
|
190
|
+
"macd",
|
|
191
|
+
"bollinger_bands",
|
|
150
192
|
]
|
|
151
193
|
|
|
194
|
+
# `market_watch_func` / `get_indices_func` are internal aliases created to avoid
|
|
195
|
+
# colliding with the same-named api.py functions. They stay bound for backward
|
|
196
|
+
# compatibility but are deliberately excluded from __all__ above -- they are not
|
|
197
|
+
# part of the supported surface. Use `market_watch()` / `get_indices()`.
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def __getattr__(name: str):
|
|
201
|
+
"""
|
|
202
|
+
Lazily expose `pypsx_toolkit.charts`.
|
|
203
|
+
|
|
204
|
+
The charting helpers need matplotlib/seaborn, which ship only under the
|
|
205
|
+
optional `[plot]` extra. Importing them eagerly would make a plain
|
|
206
|
+
`pip install pypsx-toolkit` fail at `import pypsx_toolkit`, so resolution is
|
|
207
|
+
deferred until first access and a missing extra produces an actionable
|
|
208
|
+
message instead of a bare ModuleNotFoundError.
|
|
209
|
+
"""
|
|
210
|
+
if name == "charts":
|
|
211
|
+
import sys
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
from pypsx_toolkit.endpoints import charts as _charts
|
|
215
|
+
except ImportError as exc: # pragma: no cover - depends on install extras
|
|
216
|
+
raise ImportError(
|
|
217
|
+
"pypsx_toolkit.charts requires the optional plotting dependencies. "
|
|
218
|
+
"Install them with: pip install 'pypsx-toolkit[plot]'"
|
|
219
|
+
) from exc
|
|
220
|
+
setattr(sys.modules[__name__], "charts", _charts)
|
|
221
|
+
return _charts
|
|
222
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
223
|
+
|
|
152
224
|
|
|
153
225
|
# Legacy compatibility wrappers used by audit script (clean outputs only)
|
|
154
226
|
|
|
@@ -70,7 +70,16 @@ __all__ = [
|
|
|
70
70
|
|
|
71
71
|
# Insight engine
|
|
72
72
|
'interpret_stock', 'interpret_portfolio', 'detect_patterns',
|
|
73
|
-
'generate_trading_signals', 'market_sentiment_analysis'
|
|
73
|
+
'generate_trading_signals', 'market_sentiment_analysis',
|
|
74
|
+
|
|
75
|
+
# Composite helpers (defined in this module, below)
|
|
76
|
+
'quick_analysis', 'portfolio_analysis',
|
|
77
|
+
|
|
78
|
+
# Short aliases (defined at the bottom of this module). These are used in
|
|
79
|
+
# the published documentation, so they are part of the supported surface.
|
|
80
|
+
'ma', 'ema', 'bb', 'sharpe', 'sortino', 'calmar', 'cum_returns', 'max_dd',
|
|
81
|
+
'win_rate_pct', 'pl_ratio', 'recovery', 'perf_summary', 'interpret',
|
|
82
|
+
'patterns', 'signals', 'sentiment',
|
|
74
83
|
]
|
|
75
84
|
|
|
76
85
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cython build configuration for pypsx.
|
|
3
|
+
|
|
4
|
+
Compiles all implementation modules into platform-specific binary extensions
|
|
5
|
+
(.pyd on Windows, .so on Linux/macOS). __init__.py, models.py, and constants.py
|
|
6
|
+
are intentionally excluded so they remain as plain-text Python re-exporters.
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import glob
|
|
10
|
+
from setuptools import setup, Extension, find_packages
|
|
11
|
+
from setuptools.command.build_py import build_py as _build_py
|
|
12
|
+
from Cython.Build import cythonize
|
|
13
|
+
|
|
14
|
+
# Stems that must stay as plain Python (pickling, isinstance, re-exports)
|
|
15
|
+
EXCLUDE_STEMS = {"__init__", "constants", "models"}
|
|
16
|
+
|
|
17
|
+
# Folders to skip entirely
|
|
18
|
+
EXCLUDE_DIRS = {"backend", "build", "dist", "tests", "__pycache__", ".git"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# os.name == "nt" does not imply MSVC -- CC/CXX may point at a mingw
|
|
22
|
+
# toolchain (e.g. w64devkit) for local builds without Visual Studio, and
|
|
23
|
+
# mingw's gcc/g++ reject MSVC-style "/O2" the same way MSVC would reject "-O2".
|
|
24
|
+
_USING_MSVC = os.name == "nt" and "gcc" not in os.environ.get("CC", "").lower()
|
|
25
|
+
_OPT_FLAG = "/O2" if _USING_MSVC else "-O2"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def collect_extensions(pkg_root: str):
|
|
29
|
+
extensions = []
|
|
30
|
+
for path in glob.glob(f"{pkg_root}/**/*.py", recursive=True):
|
|
31
|
+
# Normalise separators
|
|
32
|
+
norm = path.replace("\\", "/")
|
|
33
|
+
|
|
34
|
+
# Skip excluded directories
|
|
35
|
+
parts = norm.split("/")
|
|
36
|
+
if any(p in EXCLUDE_DIRS for p in parts):
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
stem = os.path.splitext(os.path.basename(path))[0]
|
|
40
|
+
if stem in EXCLUDE_STEMS:
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
# Convert path to dotted module name (e.g. pypsx/core/fetchers.py -> pypsx.core.fetchers)
|
|
44
|
+
module = norm.replace("/", ".")[:-3]
|
|
45
|
+
extensions.append(
|
|
46
|
+
Extension(
|
|
47
|
+
module,
|
|
48
|
+
[path],
|
|
49
|
+
extra_compile_args=[_OPT_FLAG],
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
return extensions
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class build_py(_build_py):
|
|
56
|
+
"""
|
|
57
|
+
Drop .py source for modules that get Cython-compiled, so wheels ship only
|
|
58
|
+
the compiled extension (+ .pyi stub) for them -- MANIFEST.in's
|
|
59
|
+
include/exclude rules only govern sdist content, NOT bdist_wheel, so
|
|
60
|
+
without this override every wheel ships the full readable .py source
|
|
61
|
+
right alongside the .pyd/.so, silently defeating the whole point of
|
|
62
|
+
compiling in the first place.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def find_package_modules(self, package, package_dir):
|
|
66
|
+
modules = super().find_package_modules(package, package_dir)
|
|
67
|
+
return [(pkg, mod, path) for (pkg, mod, path) in modules if mod in EXCLUDE_STEMS]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main():
|
|
71
|
+
extensions = collect_extensions("pypsx_toolkit")
|
|
72
|
+
|
|
73
|
+
setup(
|
|
74
|
+
cmdclass={"build_py": build_py},
|
|
75
|
+
packages=find_packages(
|
|
76
|
+
exclude=["backend*", "build*", "dist*", "tests*", "*.egg-info*"]
|
|
77
|
+
),
|
|
78
|
+
ext_modules=cythonize(
|
|
79
|
+
extensions,
|
|
80
|
+
compiler_directives={
|
|
81
|
+
"language_level": "3",
|
|
82
|
+
# binding=True: wraps compiled functions so inspect.signature() works.
|
|
83
|
+
# Required if FastAPI or any DI framework introspects function args.
|
|
84
|
+
"binding": True,
|
|
85
|
+
# embedsignature=True: embeds the Python call signature in the docstring
|
|
86
|
+
# so IDEs can show it even when the .pyi stub is absent.
|
|
87
|
+
"embedsignature": True,
|
|
88
|
+
},
|
|
89
|
+
build_dir="build",
|
|
90
|
+
annotate=False,
|
|
91
|
+
nthreads=4,
|
|
92
|
+
),
|
|
93
|
+
package_data={"": ["*.pyi", "py.typed"]},
|
|
94
|
+
zip_safe=False,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
# cythonize(nthreads=4) spawns a multiprocessing pool. On Windows
|
|
100
|
+
# (spawn start method) child processes reimport this file as __main__;
|
|
101
|
+
# without this guard, each child re-enters main() and re-spawns its own
|
|
102
|
+
# pool, crashing the whole build with BrokenProcessPool.
|
|
103
|
+
main()
|
pypsx_toolkit-3.0.2/setup.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Cython build configuration for pypsx.
|
|
3
|
-
|
|
4
|
-
Compiles all implementation modules into platform-specific binary extensions
|
|
5
|
-
(.pyd on Windows, .so on Linux/macOS). __init__.py, models.py, and constants.py
|
|
6
|
-
are intentionally excluded so they remain as plain-text Python re-exporters.
|
|
7
|
-
"""
|
|
8
|
-
import os
|
|
9
|
-
import glob
|
|
10
|
-
from setuptools import setup, Extension, find_packages
|
|
11
|
-
from Cython.Build import cythonize
|
|
12
|
-
|
|
13
|
-
# Stems that must stay as plain Python (pickling, isinstance, re-exports)
|
|
14
|
-
EXCLUDE_STEMS = {"__init__", "constants", "models"}
|
|
15
|
-
|
|
16
|
-
# Folders to skip entirely
|
|
17
|
-
EXCLUDE_DIRS = {"backend", "build", "dist", "tests", "__pycache__", ".git"}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def collect_extensions(pkg_root: str):
|
|
21
|
-
extensions = []
|
|
22
|
-
for path in glob.glob(f"{pkg_root}/**/*.py", recursive=True):
|
|
23
|
-
# Normalise separators
|
|
24
|
-
norm = path.replace("\\", "/")
|
|
25
|
-
|
|
26
|
-
# Skip excluded directories
|
|
27
|
-
parts = norm.split("/")
|
|
28
|
-
if any(p in EXCLUDE_DIRS for p in parts):
|
|
29
|
-
continue
|
|
30
|
-
|
|
31
|
-
stem = os.path.splitext(os.path.basename(path))[0]
|
|
32
|
-
if stem in EXCLUDE_STEMS:
|
|
33
|
-
continue
|
|
34
|
-
|
|
35
|
-
# Convert path to dotted module name (e.g. pypsx/core/fetchers.py -> pypsx.core.fetchers)
|
|
36
|
-
module = norm.replace("/", ".")[:-3]
|
|
37
|
-
extensions.append(
|
|
38
|
-
Extension(
|
|
39
|
-
module,
|
|
40
|
-
[path],
|
|
41
|
-
extra_compile_args=["/O2"] if os.name == "nt" else ["-O2"],
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
return extensions
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
extensions = collect_extensions("pypsx_toolkit")
|
|
48
|
-
|
|
49
|
-
setup(
|
|
50
|
-
packages=find_packages(
|
|
51
|
-
exclude=["backend*", "build*", "dist*", "tests*", "*.egg-info*"]
|
|
52
|
-
),
|
|
53
|
-
ext_modules=cythonize(
|
|
54
|
-
extensions,
|
|
55
|
-
compiler_directives={
|
|
56
|
-
"language_level": "3",
|
|
57
|
-
# binding=True: wraps compiled functions so inspect.signature() works.
|
|
58
|
-
# Required if FastAPI or any DI framework introspects function args.
|
|
59
|
-
"binding": True,
|
|
60
|
-
# embedsignature=True: embeds the Python call signature in the docstring
|
|
61
|
-
# so IDEs can show it even when the .pyi stub is absent.
|
|
62
|
-
"embedsignature": True,
|
|
63
|
-
},
|
|
64
|
-
build_dir="build",
|
|
65
|
-
annotate=False,
|
|
66
|
-
nthreads=4,
|
|
67
|
-
),
|
|
68
|
-
package_data={"": ["*.pyi", "py.typed"]},
|
|
69
|
-
zip_safe=False,
|
|
70
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|