nepsepy 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.
nepsepy-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nepsepy contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
nepsepy-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: nepsepy
3
+ Version: 1.0.0
4
+ Summary: Unofficial Python client for public Nepal Stock Exchange market data
5
+ Author: nepsepy contributors
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: httpx>=0.27
19
+ Requires-Dist: wasmtime>=12
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # nepsepy
25
+
26
+ ![nepsepy banner](https://res.cloudinary.com/dzbc5mlm9/image/upload/v1790524377/nepsepybanner_ucetgq.png)
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/nepsepy?logo=pypi&logoColor=white)](https://pypi.org/project/nepsepy/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/nepsepy?logo=python&logoColor=white)](https://pypi.org/project/nepsepy/)
30
+ [![Tests](https://github.com/menaceXnadin/nepsepy/actions/workflows/tests.yml/badge.svg)](https://github.com/menaceXnadin/nepsepy/actions/workflows/tests.yml)
31
+ [![License](https://img.shields.io/badge/license-MIT-0b7f52)](LICENSE)
32
+
33
+ > A clean Python interface for public Nepal Stock Exchange (NEPSE) market data.
34
+
35
+ `nepsepy` provides a small, synchronous API for market status, prices, floor
36
+ sheets, indices, company information, notices, and other data used by the
37
+ NEPSE website.
38
+
39
+ It is not affiliated with the Nepal Stock Exchange.
40
+
41
+ ## Why nepsepy?
42
+
43
+ - **Simple API** — use named methods such as `today_price()` and
44
+ `security_profile()` instead of manually assembling HTTP requests.
45
+ - **Public session handling** — performs the same login-free bootstrap used by
46
+ the NEPSE frontend; no user credentials are needed.
47
+ - **Careful by default** — requests are paced, token values are kept in memory,
48
+ and rate limiting is surfaced clearly instead of retried aggressively.
49
+
50
+ ## Installation
51
+
52
+ Requires Python 3.10 or later.
53
+
54
+ ```bash
55
+ python -m pip install nepsepy
56
+ ```
57
+
58
+ The PyPI distribution and Python import package are both named `nepsepy`.
59
+
60
+ ## Quick start
61
+
62
+ ```python
63
+ from nepsepy import NepseClient
64
+
65
+ with NepseClient() as client:
66
+ status = client.market_status()
67
+ gainers = client.top_gainers()
68
+ prices = client.today_price(page=1, size=20)
69
+
70
+ print(status["isOpen"])
71
+ print(gainers[:3])
72
+ print(prices["content"][:3])
73
+ ```
74
+
75
+ Most methods return the JSON object or list supplied by NEPSE. Pagination uses
76
+ 1-based page numbers, matching the website UI. Dates use `yyyy-MM-dd`.
77
+
78
+ ## Features
79
+
80
+ | Area | Included data |
81
+ | --- | --- |
82
+ | Market | Status, summaries, live market data, ticker, and top-ten lists |
83
+ | Prices & trades | Today’s prices, floor sheets, depth, supply/demand, and trade history |
84
+ | Charts | NEPSE indices, index history, and market/company chart data |
85
+ | Companies | Profiles, corporate actions, financial reports, dividends, AGMs, and news |
86
+ | Directories | Companies, sectors, share groups, promoters, brokers, and dealers |
87
+ | News & files | Notices, disclosures, holidays, reports, events, CSV exports, and downloads |
88
+
89
+ ## Authentication and rate limits
90
+
91
+ No account credentials are required. `NepseClient` performs the same public
92
+ session bootstrap used by NEPSE’s frontend when it is first needed. Session
93
+ tokens stay in memory and are redacted from package diagnostics.
94
+
95
+ Requests are paced conservatively. A `429 Too Many Requests` response raises
96
+ `RateLimitedError`; the client does not retry in a loop.
97
+
98
+ ## Common examples
99
+
100
+ ```python
101
+ from nepsepy import NepseClient
102
+
103
+ with NepseClient() as client:
104
+ # Use numeric security IDs; resolve them from companies() or securities().
105
+ companies = client.companies()
106
+ barun = next(row for row in companies if row["symbol"] == "BARUN")
107
+
108
+ profile = client.security_profile(barun["id"])
109
+ floorsheet = client.floorsheets(stock_id=barun["id"])
110
+
111
+ print(profile)
112
+ print(floorsheet["totalTrades"])
113
+ ```
114
+
115
+ ## Interactive explorer
116
+
117
+ The repository includes a terminal explorer for local use:
118
+
119
+ ```bash
120
+ python tui.py
121
+ ```
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ python -m pip install -e ".[dev]"
127
+ pytest -q
128
+ ```
129
+
130
+ ## Scope and disclaimer
131
+
132
+ This package is for public, read-only market-data workflows. It does not handle
133
+ user login, trading, portfolio actions, or other state-changing operations.
134
+ NEPSE data may be delayed or corrected by the exchange; verify information
135
+ independently before making financial decisions.
136
+
137
+ ## License
138
+
139
+ Distributed under the [MIT License](LICENSE).
@@ -0,0 +1,116 @@
1
+ # nepsepy
2
+
3
+ ![nepsepy banner](https://res.cloudinary.com/dzbc5mlm9/image/upload/v1790524377/nepsepybanner_ucetgq.png)
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/nepsepy?logo=pypi&logoColor=white)](https://pypi.org/project/nepsepy/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/nepsepy?logo=python&logoColor=white)](https://pypi.org/project/nepsepy/)
7
+ [![Tests](https://github.com/menaceXnadin/nepsepy/actions/workflows/tests.yml/badge.svg)](https://github.com/menaceXnadin/nepsepy/actions/workflows/tests.yml)
8
+ [![License](https://img.shields.io/badge/license-MIT-0b7f52)](LICENSE)
9
+
10
+ > A clean Python interface for public Nepal Stock Exchange (NEPSE) market data.
11
+
12
+ `nepsepy` provides a small, synchronous API for market status, prices, floor
13
+ sheets, indices, company information, notices, and other data used by the
14
+ NEPSE website.
15
+
16
+ It is not affiliated with the Nepal Stock Exchange.
17
+
18
+ ## Why nepsepy?
19
+
20
+ - **Simple API** — use named methods such as `today_price()` and
21
+ `security_profile()` instead of manually assembling HTTP requests.
22
+ - **Public session handling** — performs the same login-free bootstrap used by
23
+ the NEPSE frontend; no user credentials are needed.
24
+ - **Careful by default** — requests are paced, token values are kept in memory,
25
+ and rate limiting is surfaced clearly instead of retried aggressively.
26
+
27
+ ## Installation
28
+
29
+ Requires Python 3.10 or later.
30
+
31
+ ```bash
32
+ python -m pip install nepsepy
33
+ ```
34
+
35
+ The PyPI distribution and Python import package are both named `nepsepy`.
36
+
37
+ ## Quick start
38
+
39
+ ```python
40
+ from nepsepy import NepseClient
41
+
42
+ with NepseClient() as client:
43
+ status = client.market_status()
44
+ gainers = client.top_gainers()
45
+ prices = client.today_price(page=1, size=20)
46
+
47
+ print(status["isOpen"])
48
+ print(gainers[:3])
49
+ print(prices["content"][:3])
50
+ ```
51
+
52
+ Most methods return the JSON object or list supplied by NEPSE. Pagination uses
53
+ 1-based page numbers, matching the website UI. Dates use `yyyy-MM-dd`.
54
+
55
+ ## Features
56
+
57
+ | Area | Included data |
58
+ | --- | --- |
59
+ | Market | Status, summaries, live market data, ticker, and top-ten lists |
60
+ | Prices & trades | Today’s prices, floor sheets, depth, supply/demand, and trade history |
61
+ | Charts | NEPSE indices, index history, and market/company chart data |
62
+ | Companies | Profiles, corporate actions, financial reports, dividends, AGMs, and news |
63
+ | Directories | Companies, sectors, share groups, promoters, brokers, and dealers |
64
+ | News & files | Notices, disclosures, holidays, reports, events, CSV exports, and downloads |
65
+
66
+ ## Authentication and rate limits
67
+
68
+ No account credentials are required. `NepseClient` performs the same public
69
+ session bootstrap used by NEPSE’s frontend when it is first needed. Session
70
+ tokens stay in memory and are redacted from package diagnostics.
71
+
72
+ Requests are paced conservatively. A `429 Too Many Requests` response raises
73
+ `RateLimitedError`; the client does not retry in a loop.
74
+
75
+ ## Common examples
76
+
77
+ ```python
78
+ from nepsepy import NepseClient
79
+
80
+ with NepseClient() as client:
81
+ # Use numeric security IDs; resolve them from companies() or securities().
82
+ companies = client.companies()
83
+ barun = next(row for row in companies if row["symbol"] == "BARUN")
84
+
85
+ profile = client.security_profile(barun["id"])
86
+ floorsheet = client.floorsheets(stock_id=barun["id"])
87
+
88
+ print(profile)
89
+ print(floorsheet["totalTrades"])
90
+ ```
91
+
92
+ ## Interactive explorer
93
+
94
+ The repository includes a terminal explorer for local use:
95
+
96
+ ```bash
97
+ python tui.py
98
+ ```
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ python -m pip install -e ".[dev]"
104
+ pytest -q
105
+ ```
106
+
107
+ ## Scope and disclaimer
108
+
109
+ This package is for public, read-only market-data workflows. It does not handle
110
+ user login, trading, portfolio actions, or other state-changing operations.
111
+ NEPSE data may be delayed or corrected by the exchange; verify information
112
+ independently before making financial decisions.
113
+
114
+ ## License
115
+
116
+ Distributed under the [MIT License](LICENSE).
@@ -0,0 +1,18 @@
1
+ """Public NEPSE market-data client (normal public-site flow only)."""
2
+
3
+ from .auth import ProveResponse, TokenState
4
+ from .checksum import base as checksum_base
5
+ from .checksum import current_day as checksum_day
6
+ from .checksum import variant_a as checksum_a
7
+ from .checksum import variant_b as checksum_b
8
+ from .checksum import variant_c as checksum_c
9
+ from .client import NepseClient
10
+ from .exceptions import (AuthExpiredError, BootstrapError, NepseError,
11
+ PublicEndpointError, RateLimitedError, WasmError)
12
+ from .wasm import WasmCleaner, redact, strip_at_indexes
13
+
14
+ __all__ = ["NepseClient", "ProveResponse", "TokenState", "WasmCleaner",
15
+ "redact", "strip_at_indexes", "NepseError", "BootstrapError",
16
+ "WasmError", "AuthExpiredError", "RateLimitedError",
17
+ "PublicEndpointError", "checksum_base", "checksum_day",
18
+ "checksum_a", "checksum_b", "checksum_c"]
@@ -0,0 +1,115 @@
1
+ """Per-security data and company tabs. endpoints (mixin for NepseClient)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+
8
+ class CompanyMixin:
9
+ # -- per-security data (GET; mirrors company tabs) --------------------------
10
+ def security_detail(self, security_id: int) -> dict:
11
+ """{securityData, securityMcsData} (Market Depth page header)."""
12
+ return self.get_json(f"/api/nots/security/{security_id}") # type: ignore[return-value]
13
+
14
+ def security_profile(self, security_id: int) -> dict:
15
+ return self.get_json(f"/api/nots/security/profile/{security_id}") # type: ignore[return-value]
16
+
17
+ def board_of_directors(self, security_id: int) -> list:
18
+ return self.get_json(f"/api/nots/security/boardOfDirectors/{security_id}") # type: ignore[return-value]
19
+
20
+ def corporate_actions(self, security_id: int) -> list:
21
+ """Bonus/cash/right [{fiscalYear, bonusPercentage, ...}]."""
22
+ return self.get_json(f"/api/nots/security/corporate-actions/{security_id}") # type: ignore[return-value]
23
+
24
+ def financial_reports(self, security_id: int) -> list:
25
+ return self.get_json(f"/api/nots/application/reports/{security_id}") # type: ignore[return-value]
26
+
27
+ def agm(self, security_id: int) -> list:
28
+ return self.get_json(f"/api/nots/application/agm/{security_id}") # type: ignore[return-value]
29
+
30
+ def security_company_news(self, security_id: int) -> list:
31
+ return self.get_json(f"/api/nots/application/company-news/{security_id}") # type: ignore[return-value]
32
+
33
+ def market_security(self, security_id: int) -> dict:
34
+ """Flat daily-trade snapshot + nested security (Listing Information)."""
35
+ return self.get_json(f"/api/nots/market/security/{security_id}") # type: ignore[return-value]
36
+
37
+ def security_market_picture(self, security_id: int) -> dict:
38
+ """Compact quote {receivedDateTime, securityId, lastTradedPrice,
39
+ openPrice, highPrice, lowPrice} (site's getSecurityDetailsFromMarketPicture)."""
40
+ return self.get_json(f"/api/nots/security-detail/{security_id}") # type: ignore[return-value]
41
+
42
+ def dividends(self, security_id: int) -> list:
43
+ """Dividend applications/news for a security (site's getSecurityDividendNewsById)."""
44
+ return self.get_json(f"/api/nots/application/dividend/{security_id}") # type: ignore[return-value]
45
+
46
+ def book_close(self, news_id: int) -> dict:
47
+ """Book-closure news by id (site's getSecurityBookCloseNewsById).
48
+
49
+ NOTE: currently HTTP 404 for every id tried (security ids and
50
+ company-news ids alike), so this is mapped-but-dead like
51
+ security_floorsheet() until the backend serves it again.
52
+ """
53
+ return self.get_json(f"/api/nots/news/book-close/{news_id}") # type: ignore[return-value]
54
+
55
+ def security_price_history(self, security_id: int, page: int = 1,
56
+ size: int = 20,
57
+ business_date: Optional[str] = None) -> dict:
58
+ """Price History tab (Spring page of OHLCV rows; page 1-based)."""
59
+ query = f"?&size={size}"
60
+ if business_date:
61
+ query += f"&businessDate={business_date}"
62
+ if page > 1:
63
+ query = f"?page={page - 1}&size={size}" + (
64
+ f"&businessDate={business_date}" if business_date else "")
65
+ return self.get_json(f"/api/nots/market/security/price/{security_id}{query}") # type: ignore[return-value]
66
+
67
+ def stock_trading_history(self, security_id: int, page: int = 1,
68
+ size: int = 20,
69
+ start: Optional[str] = None,
70
+ end: Optional[str] = None) -> dict:
71
+ """Stock Trading page (page 1-based; dates 'yyyy-MM-dd')."""
72
+ query = f"?&size={size}"
73
+ if start:
74
+ query += f"&startDate={start}"
75
+ if end:
76
+ query += f"&endDate={end}"
77
+ if page > 1:
78
+ query = f"?page={page - 1}&size={size}" + (
79
+ f"&startDate={start}" if start else "") + (
80
+ f"&endDate={end}" if end else "")
81
+ return self.get_json(f"/api/nots/market/history/security/{security_id}{query}") # type: ignore[return-value]
82
+
83
+ def trading_average(self, n_days: int = 120,
84
+ business_date: Optional[str] = None,
85
+ stock_id: Optional[int] = None) -> list:
86
+ """N-day average price per security (Trading Average page)."""
87
+ path = f"/api/nots/nepse-data/trading-average?nDays={n_days}"
88
+ if business_date:
89
+ path += f"&businessDate={business_date}"
90
+ if stock_id is not None:
91
+ path += f"&stockId={stock_id}"
92
+ return self.get_json(path) # type: ignore[return-value]
93
+
94
+ def market_cap_history(self, page: int = 1) -> list:
95
+ """Daily market caps [{businessDate, marCap, ...}] (filter client-side)."""
96
+ path = "/api/nots/nepse-data/marcapbydate/?"
97
+ if page > 1:
98
+ path = f"/api/nots/nepse-data/marcapbydate/?page={page - 1}"
99
+ return self.get_json(path) # type: ignore[return-value]
100
+
101
+ # -- signed POST: company data --------------------------------------------------
102
+ def company_info(self, security_id: int) -> dict:
103
+ """Company header {securityDailyTradeDto, security, ...} (detail page)."""
104
+ return self.post_json(f"/api/nots/security/{security_id}", # type: ignore[return-value]
105
+ {"id": self._signed("raw")})
106
+
107
+ def company_graph(self, security_id: int) -> list:
108
+ """Company OHLC history [{businessDate, openPrice, ...}] (chart tab)."""
109
+ return self.post_json(f"/api/nots/market/graphdata/{security_id}", # type: ignore[return-value]
110
+ {"id": self._signed("raw")})
111
+
112
+ def company_graph_intraday(self, security_id: int) -> list:
113
+ """Company intraday LTP (Charts Company tab, 1D range)."""
114
+ return self.post_json(f"/api/nots/market/graphdata/daily/{security_id}", # type: ignore[return-value]
115
+ {"id": self._signed("raw")})
@@ -0,0 +1,101 @@
1
+ """Market data, indices, top tens and depth. endpoints (mixin for NepseClient)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+
8
+ class MarketMixin:
9
+ # -- market status / summary / ticker --------------------------------
10
+ def market_status(self) -> dict:
11
+ """Market open state (homepage + every page reads this)."""
12
+ return self.get_json("/api/nots/nepse-data/market-open") # type: ignore[return-value]
13
+
14
+ def market_summary(self) -> list:
15
+ """Homepage summary tiles [{detail, value}]."""
16
+ return self.get_json("/api/nots/market-summary/") # type: ignore[return-value]
17
+
18
+ def market_summary_history(self) -> list:
19
+ """Daily history [{businessDate, totalTurnover, ...}]."""
20
+ return self.get_json("/api/nots/market-summary-history") # type: ignore[return-value]
21
+
22
+ def ticker(self, index_id: int = 58) -> list:
23
+ """Live ticker strip [{symbol, ltp, ...}] (every page load)."""
24
+ return self.get_json(f"/api/nots/securityDailyTradeStat/{index_id}") # type: ignore[return-value]
25
+
26
+ def live_market(self) -> list:
27
+ """Full live snapshot (356 rows: OHLC, volumes, per-security indexId).
28
+
29
+ The Live Market *page* redirects to `/` while the market is closed,
30
+ but the API itself answers 200 regardless — verified live.
31
+ """
32
+ return self.get_json("/api/nots/lives-market") # type: ignore[return-value]
33
+
34
+ def nepse_indices(self) -> list:
35
+ """Index snapshot [{index, close, change, perChange, ...}]."""
36
+ return self.get_json("/api/nots/nepse-index") # type: ignore[return-value]
37
+
38
+ def sub_indices(self) -> list:
39
+ """Sub-index snapshot [{id, index, change, currentValue}]."""
40
+ return self.get_json("/api/nots") # type: ignore[return-value]
41
+
42
+ def indices_list(self) -> list:
43
+ """Index directory [{id, indexCode, indexName, ...}]."""
44
+ return self.get_json("/api/nots/index") # type: ignore[return-value]
45
+
46
+ def index_history(self, index_id: int = 58, page: int = 1,
47
+ size: int = 20) -> dict:
48
+ """Datewise index OHLC (Indices page; page is 1-based like the UI)."""
49
+ path = f"/api/nots/index/history/{index_id}?&size={size}"
50
+ if page > 1:
51
+ path = f"/api/nots/index/history/{index_id}?page={page - 1}&size={size}"
52
+ return self.get_json(path) # type: ignore[return-value]
53
+
54
+ def sector_summary(self, business_date: Optional[str] = None) -> list:
55
+ """Sector turnover (Sector Summary page; date 'yyyy-MM-dd')."""
56
+ path = "/api/nots/sectorwise"
57
+ if business_date:
58
+ path += f"?businessDate={business_date}"
59
+ return self.get_json(path) # type: ignore[return-value]
60
+
61
+ # -- top tens ------------------------------------------------------------
62
+ def _top_ten(self, kind: str, full: bool = False) -> list:
63
+ return self.get_json(f"/api/nots/top-ten/{kind}?all={str(full).lower()}") # type: ignore[return-value]
64
+
65
+ def top_gainers(self, full: bool = False) -> list:
66
+ """Top gainers [{symbol, ltp, pointChange, percentageChange, ...}]."""
67
+ return self._top_ten("top-gainer", full)
68
+
69
+ def top_losers(self, full: bool = False) -> list:
70
+ """Top losers (same shape as gainers)."""
71
+ return self._top_ten("top-loser", full)
72
+
73
+ def top_turnover(self, full: bool = False) -> list:
74
+ """Top by turnover."""
75
+ return self._top_ten("turnover", full)
76
+
77
+ def top_traded_shares(self, full: bool = False) -> list:
78
+ """Top by shares traded."""
79
+ return self._top_ten("trade", full)
80
+
81
+ def top_transactions(self, full: bool = False) -> list:
82
+ """Top by transaction count."""
83
+ return self._top_ten("transaction", full)
84
+
85
+ def top_active(self, full: bool = False) -> list:
86
+ """Most active scrips (trade quantity)."""
87
+ return self._top_ten("trade-qty", full)
88
+
89
+ # -- supply / demand / depth ----------------------------------------------
90
+ def supply_demand(self, full: bool = False) -> dict:
91
+ """Top supply/demand {supplyList, demandList}."""
92
+ return self.get_json( # type: ignore[return-value]
93
+ f"/api/nots/nepse-data/supplydemand?all={str(full).lower()}")
94
+
95
+ def market_depth(self, security_id: int) -> dict:
96
+ """Order book {totalBuyQty, totalSellQty, marketDepth{...}}."""
97
+ return self.get_json(f"/api/nots/nepse-data/marketdepth/{security_id}") # type: ignore[return-value]
98
+
99
+ def odd_lot_depth(self, security_id: int) -> dict:
100
+ """Odd-lot order book (same shape as market_depth)."""
101
+ return self.get_json(f"/api/nots/nepse-data/marketdepth-ol/{security_id}") # type: ignore[return-value]