ticker-classifier 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Stephan Akkerman
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.
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: ticker_classifier
3
+ Version: 0.1.0
4
+ Summary: A robust stock, crypto, and forex classifier with async support.
5
+ Author-email: Stephan Akkerman <stephan@akkerman.ai>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: requests
10
+ Requires-Dist: aiohttp
11
+ Dynamic: license-file
12
+
13
+ # ticker-classifier
14
+
15
+ <!-- Add a banner here like: https://github.com/StephanAkkerman/fintwit-bot/blob/main/img/logo/fintwit-banner.png -->
16
+
17
+ ---
18
+ <!-- Adjust the link of the first and second badges to your own repo -->
19
+ <p align="center">
20
+ <img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/StephanAkkerman/ticker-classifier/pyversions.yml?label=python%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13&logo=python&style=flat-square">
21
+ <img src="https://img.shields.io/github/license/StephanAkkerman/ticker-classifier.svg?color=brightgreen" alt="License">
22
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black"></a>
23
+ </p>
24
+
25
+ ## Introduction
26
+
27
+ `ticker-classifier` is a small Python library for classifying ticker-like symbols (for example `AAPL`, `BTC`, `EUR`, `GOLD`) into a simple market/category representation.
28
+ It uses Yahoo Finance for equities, CoinGecko for cryptocurrencies and a few heuristics for currencies/commodities. The output indicates the most likely category, a display name, market cap when available, and a `yahoo_lookup` value to fetch further data if desired.
29
+
30
+ ## Table of Contents 🗂
31
+
32
+ - [Key Features](#key-features)
33
+ - [Installation](#installation)
34
+ - [Usage](#usage)
35
+ - [API](#api)
36
+ - [Development](#development)
37
+ - [Release and Versioning](#release-and-versioning)
38
+ - [Citation](#citation)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
41
+
42
+ ## Key Features 🔑
43
+
44
+ - Classify symbols as `Equity`, `Crypto`, `Forex`, `Commodity`, `Index` or `Unknown`.
45
+ - Uses multiple public APIs and simple heuristics to make robust decisions.
46
+ - Provides both synchronous and asynchronous APIs.
47
+ - Lightweight disk cache to avoid repeated lookups (`TickerCache`).
48
+
49
+ ## Installation ⚙️
50
+
51
+ Install from pip using the provided `requirements.txt` or install the package directly from the repository for latest changes:
52
+
53
+ ```bash
54
+ pip install -r requirements.txt
55
+ ```
56
+
57
+ or
58
+
59
+ ```bash
60
+ pip install git+https://github.com/StephanAkkerman/ticker-classifier.git
61
+ ```
62
+
63
+ ## Usage ⌨️
64
+
65
+ Basic synchronous usage:
66
+
67
+ ```python
68
+ from ticker_classifier.classifier import TickerClassifier
69
+
70
+ classifier = TickerClassifier()
71
+ symbols = ["AAPL", "BTC", "EUR", "GOLD", "UNKNOWN123"]
72
+ results = classifier.classify(symbols)
73
+ for r in results:
74
+ print(r)
75
+ ```
76
+
77
+ Example asynchronous usage:
78
+
79
+ ```python
80
+ import asyncio
81
+ from ticker_classifier.classifier import TickerClassifier
82
+
83
+ async def main():
84
+ classifier = TickerClassifier()
85
+ symbols = ["AAPL", "BTC", "ETH", "JPY"]
86
+ results = await classifier.classify_async(symbols)
87
+ for r in results:
88
+ print(r)
89
+
90
+ asyncio.run(main())
91
+ ```
92
+
93
+ The output for each symbol is a dictionary like:
94
+
95
+ ```python
96
+ {'category': 'EQUITY', 'ticker': 'AAPL', 'name': 'Apple Inc.', 'market_cap': 4029017227264, 'yahoo_lookup': 'AAPL', 'alternatives': ['crypto'], 'source': 'api'}
97
+ {'category': 'crypto', 'ticker': 'BTC', 'name': 'Bitcoin', 'market_cap': 1736590593460.9607, 'yahoo_lookup': 'BTC-USD', 'alternatives': ['stock'], 'source': 'api'}
98
+ {'category': 'crypto', 'ticker': 'ETH', 'name': 'Ethereum', 'market_cap': 338145915081.1455, 'yahoo_lookup': 'ETH-USD', 'alternatives': ['stock'], 'source': 'cache'}
99
+ {'category': 'forex', 'ticker': 'JPY', 'name': 'JPY Currency', 'market_cap': None, 'yahoo_lookup': 'JPYUSD=X', 'alternatives': ['stock'], 'source': 'cache'}
100
+ ```
101
+
102
+ Notes
103
+ - The classifier caches positive classifications (non-`Unknown`) in an
104
+ SQLite database (default `ticker_cache.db`) for `24` hours by default.
105
+ - You can customize the cache filename and expiry by passing `db_name` and
106
+ `hours_to_expire` to `TickerClassifier`.
107
+
108
+ ## API
109
+
110
+ - `ticker_classifier.classifier.TickerClassifier`
111
+ - `classify(symbols: List[str]) -> List[dict]` – synchronous classification.
112
+ - `classify_async(symbols: List[str]) -> List[dict]` – async classification.
113
+ - `ticker_classifier.apis.yahoo.YahooClient` – low-level Yahoo quote fetcher (sync + async helpers).
114
+ - `ticker_classifier.apis.coingecko.CoinGeckoClient` – crypto lookup + market cap helpers (sync + async).
115
+ - `ticker_classifier.db.cache.TickerCache` – tiny SQLite-backed cache used by `TickerClassifier`.
116
+
117
+ ## Development
118
+
119
+ Run formatting and linting tools you prefer (project uses `black` code style).
120
+
121
+ Run a quick smoke check by running the `classifier.py` module directly:
122
+
123
+ ```powershell
124
+ & .venv\Scripts\python.exe ticker_classifier\classifier.py
125
+ ```
126
+
127
+ If you add tests, run them with your chosen test runner (e.g. `pytest`).
128
+
129
+ ## Release and Versioning
130
+
131
+ This package is published to PyPI through GitHub Actions:
132
+
133
+ - Workflow: `.github/workflows/publish.yml`
134
+ - Trigger: GitHub Release published
135
+ - Publisher: `pypa/gh-action-pypi-publish` using trusted publishing (OIDC)
136
+
137
+ Release flow:
138
+
139
+ 1. Update version in `pyproject.toml`.
140
+ 2. Update `ticker_classifier/__init__.py` `__version__` to match.
141
+ 3. Commit and push.
142
+ 4. Create a GitHub release with tag `vX.Y.Z` (or `X.Y.Z`).
143
+
144
+ The publish workflow validates that the release tag version matches `pyproject.toml` before uploading to PyPI.
145
+
146
+ ## Citation ✍️
147
+ If you use this project in your research, please cite as follows (adjust
148
+ metadata accordingly):
149
+
150
+ ```bibtex
151
+ @misc{ticker-classifier,
152
+ author = {Stephan Akkerman},
153
+ title = {ticker-classifier},
154
+ year = {2025},
155
+ publisher = {GitHub},
156
+ howpublished = {\url{https://github.com/StephanAkkerman/ticker-classifier}}
157
+ }
158
+ ```
159
+
160
+ ## Contributing 🛠
161
+
162
+ Contributions are welcome. Suggested workflow:
163
+
164
+ 1. Fork the repository and create a feature branch.
165
+ 2. Run tests and format your changes with `black`.
166
+ 3. Open a pull request with a clear description of the change.
167
+
168
+ Please open issues for feature requests or bugs and include a small
169
+ reproducible example when possible.
170
+
171
+ ![https://github.com/StephanAkkerman/ticker-classifier/graphs/contributors](https://contributors-img.firebaseapp.com/image?repo=StephanAkkerman/ticker-classifier)
172
+
173
+ ## License 📜
174
+
175
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,163 @@
1
+ # ticker-classifier
2
+
3
+ <!-- Add a banner here like: https://github.com/StephanAkkerman/fintwit-bot/blob/main/img/logo/fintwit-banner.png -->
4
+
5
+ ---
6
+ <!-- Adjust the link of the first and second badges to your own repo -->
7
+ <p align="center">
8
+ <img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/StephanAkkerman/ticker-classifier/pyversions.yml?label=python%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13&logo=python&style=flat-square">
9
+ <img src="https://img.shields.io/github/license/StephanAkkerman/ticker-classifier.svg?color=brightgreen" alt="License">
10
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black"></a>
11
+ </p>
12
+
13
+ ## Introduction
14
+
15
+ `ticker-classifier` is a small Python library for classifying ticker-like symbols (for example `AAPL`, `BTC`, `EUR`, `GOLD`) into a simple market/category representation.
16
+ It uses Yahoo Finance for equities, CoinGecko for cryptocurrencies and a few heuristics for currencies/commodities. The output indicates the most likely category, a display name, market cap when available, and a `yahoo_lookup` value to fetch further data if desired.
17
+
18
+ ## Table of Contents 🗂
19
+
20
+ - [Key Features](#key-features)
21
+ - [Installation](#installation)
22
+ - [Usage](#usage)
23
+ - [API](#api)
24
+ - [Development](#development)
25
+ - [Release and Versioning](#release-and-versioning)
26
+ - [Citation](#citation)
27
+ - [Contributing](#contributing)
28
+ - [License](#license)
29
+
30
+ ## Key Features 🔑
31
+
32
+ - Classify symbols as `Equity`, `Crypto`, `Forex`, `Commodity`, `Index` or `Unknown`.
33
+ - Uses multiple public APIs and simple heuristics to make robust decisions.
34
+ - Provides both synchronous and asynchronous APIs.
35
+ - Lightweight disk cache to avoid repeated lookups (`TickerCache`).
36
+
37
+ ## Installation ⚙️
38
+
39
+ Install from pip using the provided `requirements.txt` or install the package directly from the repository for latest changes:
40
+
41
+ ```bash
42
+ pip install -r requirements.txt
43
+ ```
44
+
45
+ or
46
+
47
+ ```bash
48
+ pip install git+https://github.com/StephanAkkerman/ticker-classifier.git
49
+ ```
50
+
51
+ ## Usage ⌨️
52
+
53
+ Basic synchronous usage:
54
+
55
+ ```python
56
+ from ticker_classifier.classifier import TickerClassifier
57
+
58
+ classifier = TickerClassifier()
59
+ symbols = ["AAPL", "BTC", "EUR", "GOLD", "UNKNOWN123"]
60
+ results = classifier.classify(symbols)
61
+ for r in results:
62
+ print(r)
63
+ ```
64
+
65
+ Example asynchronous usage:
66
+
67
+ ```python
68
+ import asyncio
69
+ from ticker_classifier.classifier import TickerClassifier
70
+
71
+ async def main():
72
+ classifier = TickerClassifier()
73
+ symbols = ["AAPL", "BTC", "ETH", "JPY"]
74
+ results = await classifier.classify_async(symbols)
75
+ for r in results:
76
+ print(r)
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ The output for each symbol is a dictionary like:
82
+
83
+ ```python
84
+ {'category': 'EQUITY', 'ticker': 'AAPL', 'name': 'Apple Inc.', 'market_cap': 4029017227264, 'yahoo_lookup': 'AAPL', 'alternatives': ['crypto'], 'source': 'api'}
85
+ {'category': 'crypto', 'ticker': 'BTC', 'name': 'Bitcoin', 'market_cap': 1736590593460.9607, 'yahoo_lookup': 'BTC-USD', 'alternatives': ['stock'], 'source': 'api'}
86
+ {'category': 'crypto', 'ticker': 'ETH', 'name': 'Ethereum', 'market_cap': 338145915081.1455, 'yahoo_lookup': 'ETH-USD', 'alternatives': ['stock'], 'source': 'cache'}
87
+ {'category': 'forex', 'ticker': 'JPY', 'name': 'JPY Currency', 'market_cap': None, 'yahoo_lookup': 'JPYUSD=X', 'alternatives': ['stock'], 'source': 'cache'}
88
+ ```
89
+
90
+ Notes
91
+ - The classifier caches positive classifications (non-`Unknown`) in an
92
+ SQLite database (default `ticker_cache.db`) for `24` hours by default.
93
+ - You can customize the cache filename and expiry by passing `db_name` and
94
+ `hours_to_expire` to `TickerClassifier`.
95
+
96
+ ## API
97
+
98
+ - `ticker_classifier.classifier.TickerClassifier`
99
+ - `classify(symbols: List[str]) -> List[dict]` – synchronous classification.
100
+ - `classify_async(symbols: List[str]) -> List[dict]` – async classification.
101
+ - `ticker_classifier.apis.yahoo.YahooClient` – low-level Yahoo quote fetcher (sync + async helpers).
102
+ - `ticker_classifier.apis.coingecko.CoinGeckoClient` – crypto lookup + market cap helpers (sync + async).
103
+ - `ticker_classifier.db.cache.TickerCache` – tiny SQLite-backed cache used by `TickerClassifier`.
104
+
105
+ ## Development
106
+
107
+ Run formatting and linting tools you prefer (project uses `black` code style).
108
+
109
+ Run a quick smoke check by running the `classifier.py` module directly:
110
+
111
+ ```powershell
112
+ & .venv\Scripts\python.exe ticker_classifier\classifier.py
113
+ ```
114
+
115
+ If you add tests, run them with your chosen test runner (e.g. `pytest`).
116
+
117
+ ## Release and Versioning
118
+
119
+ This package is published to PyPI through GitHub Actions:
120
+
121
+ - Workflow: `.github/workflows/publish.yml`
122
+ - Trigger: GitHub Release published
123
+ - Publisher: `pypa/gh-action-pypi-publish` using trusted publishing (OIDC)
124
+
125
+ Release flow:
126
+
127
+ 1. Update version in `pyproject.toml`.
128
+ 2. Update `ticker_classifier/__init__.py` `__version__` to match.
129
+ 3. Commit and push.
130
+ 4. Create a GitHub release with tag `vX.Y.Z` (or `X.Y.Z`).
131
+
132
+ The publish workflow validates that the release tag version matches `pyproject.toml` before uploading to PyPI.
133
+
134
+ ## Citation ✍️
135
+ If you use this project in your research, please cite as follows (adjust
136
+ metadata accordingly):
137
+
138
+ ```bibtex
139
+ @misc{ticker-classifier,
140
+ author = {Stephan Akkerman},
141
+ title = {ticker-classifier},
142
+ year = {2025},
143
+ publisher = {GitHub},
144
+ howpublished = {\url{https://github.com/StephanAkkerman/ticker-classifier}}
145
+ }
146
+ ```
147
+
148
+ ## Contributing 🛠
149
+
150
+ Contributions are welcome. Suggested workflow:
151
+
152
+ 1. Fork the repository and create a feature branch.
153
+ 2. Run tests and format your changes with `black`.
154
+ 3. Open a pull request with a clear description of the change.
155
+
156
+ Please open issues for feature requests or bugs and include a small
157
+ reproducible example when possible.
158
+
159
+ ![https://github.com/StephanAkkerman/ticker-classifier/graphs/contributors](https://contributors-img.firebaseapp.com/image?repo=StephanAkkerman/ticker-classifier)
160
+
161
+ ## License 📜
162
+
163
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "ticker_classifier"
3
+ version = "0.1.0"
4
+ description = "A robust stock, crypto, and forex classifier with async support."
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ authors = [
8
+ { name = "Stephan Akkerman", email = "stephan@akkerman.ai" },
9
+ ]
10
+ dependencies = [
11
+ "requests",
12
+ "aiohttp"
13
+ ]
14
+
15
+ [build-system]
16
+ requires = ["setuptools>=61.0"]
17
+ build-backend = "setuptools.build_meta"
18
+
19
+ [tool.setuptools.packages.find]
20
+ include = ["ticker_classifier*"]
21
+
22
+ [tool.isort]
23
+ multi_line_output = 3
24
+ include_trailing_comma = true
25
+ force_grid_wrap = 0
26
+ line_length = 88
27
+ profile = "black"
28
+
29
+ [tool.ruff]
30
+ line-length = 88
31
+ #select = ["I001"]
32
+
33
+ [tool.ruff.lint.pydocstyle]
34
+ # Use Google-style docstrings.
35
+ convention = "numpy"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .classifier import TickerClassifier
2
+
3
+ __all__ = ["TickerClassifier"]
4
+ __version__ = "0.1.0"
@@ -0,0 +1,217 @@
1
+ from collections import defaultdict
2
+ from typing import Dict, List
3
+
4
+ import aiohttp
5
+ import requests
6
+
7
+
8
+ class CoinGeckoClient:
9
+ def __init__(self):
10
+ """Initialize CoinGecko client.
11
+
12
+ Sets up base endpoints used for retrieving the coin list and simple
13
+ price information and initializes an internal cache for symbol -> id
14
+ mappings.
15
+
16
+ Notes
17
+ -----
18
+ The client keeps an in-memory `_crypto_map` that maps uppercase
19
+ symbols to a list of CoinGecko ids. This map is populated lazily by
20
+ `_load_map_sync` or `_load_map_async` when price lookup is requested.
21
+ """
22
+ self.list_url = "https://api.coingecko.com/api/v3/coins/list"
23
+ self.price_url = "https://api.coingecko.com/api/v3/simple/price"
24
+ self._crypto_map = None # { 'BTC': ['bitcoin', 'bitcoin-token'], ... }
25
+
26
+ def _load_map_sync(self):
27
+ """Load the CoinGecko symbol->id map synchronously.
28
+
29
+ This method fetches the full coin list from the CoinGecko API and
30
+ populates the in-memory `_crypto_map` mapping uppercase symbol strings
31
+ to lists of CoinGecko ids. If the map is already loaded this is a
32
+ no-op.
33
+
34
+ Errors
35
+ ------
36
+ Any exceptions raised while fetching/parsing are caught and the map
37
+ falls back to an empty dict.
38
+ """
39
+ if self._crypto_map:
40
+ return
41
+ try:
42
+ resp = requests.get(self.list_url, timeout=10)
43
+ data = resp.json()
44
+ self._crypto_map = defaultdict(list)
45
+ for coin in data:
46
+ self._crypto_map[coin["symbol"].upper()].append(coin["id"])
47
+ except Exception:
48
+ self._crypto_map = {}
49
+
50
+ async def _load_map_async(self, session: aiohttp.ClientSession):
51
+ """Asynchronously load the CoinGecko symbol->id map.
52
+
53
+ Parameters
54
+ ----------
55
+ session : aiohttp.ClientSession
56
+ Active aiohttp session used for making the HTTP request.
57
+
58
+ Notes
59
+ -----
60
+ This is the async counterpart to `_load_map_sync`. If the internal map
61
+ is already populated this method returns immediately. Exceptions are
62
+ caught and the map will be set to an empty dict on failure.
63
+ """
64
+ if self._crypto_map:
65
+ return
66
+ try:
67
+ async with session.get(self.list_url) as resp:
68
+ data = await resp.json()
69
+ self._crypto_map = defaultdict(list)
70
+ for coin in data:
71
+ self._crypto_map[coin["symbol"].upper()].append(coin["id"])
72
+ except Exception:
73
+ self._crypto_map = {}
74
+
75
+ def _get_candidate_ids(
76
+ self, symbols: List[str]
77
+ ) -> tuple[List[str], Dict[str, str]]:
78
+ """Return candidate CoinGecko ids for a list of symbols.
79
+
80
+ Parameters
81
+ ----------
82
+ symbols : list[str]
83
+ Uppercase ticker symbols to map to CoinGecko ids.
84
+
85
+ Returns
86
+ -------
87
+ ids : list[str]
88
+ Flat list of candidate CoinGecko ids (limited to first 10
89
+ collisions per symbol).
90
+ id_to_parent : dict
91
+ Mapping of coin id -> original symbol (parent) used to group
92
+ results later.
93
+ """
94
+ ids = []
95
+ id_to_parent = {}
96
+ if not self._crypto_map:
97
+ return ids, id_to_parent
98
+
99
+ for sym in symbols:
100
+ if sym in self._crypto_map:
101
+ # Top 10 collisions only
102
+ for cid in self._crypto_map[sym][:10]:
103
+ ids.append(cid)
104
+ id_to_parent[cid] = sym
105
+ return ids, id_to_parent
106
+
107
+ def get_prices_sync(self, symbols: List[str]) -> Dict[str, Dict]:
108
+ """Synchronous price lookup for a list of symbols using CoinGecko.
109
+
110
+ This method ensures the internal symbol->id map is loaded, finds
111
+ candidate CoinGecko ids for the requested symbols, and retrieves USD
112
+ prices and market caps in chunks. The highest market cap candidate is
113
+ selected per symbol in `_process_response`.
114
+
115
+ Parameters
116
+ ----------
117
+ symbols : list[str]
118
+ Uppercase ticker symbols to look up.
119
+
120
+ Returns
121
+ -------
122
+ dict[str, dict]
123
+ Mapping of symbol -> {"market_cap": ..., "name": ..., "id": ...}
124
+ for matches found. Returns an empty dict if nothing matched.
125
+ """
126
+ self._load_map_sync()
127
+ results = {}
128
+ ids, id_map = self._get_candidate_ids(symbols)
129
+ if not ids:
130
+ return results
131
+
132
+ chunk_size = 200
133
+ for i in range(0, len(ids), chunk_size):
134
+ chunk = ids[i : i + chunk_size]
135
+ try:
136
+ resp = requests.get(
137
+ self.price_url,
138
+ params={
139
+ "ids": ",".join(chunk),
140
+ "vs_currencies": "usd",
141
+ "include_market_cap": "true",
142
+ },
143
+ timeout=10,
144
+ )
145
+ data = resp.json()
146
+ self._process_response(data, id_map, results)
147
+ except Exception:
148
+ pass
149
+ return results
150
+
151
+ async def get_prices_async(
152
+ self, session: aiohttp.ClientSession, symbols: List[str]
153
+ ) -> Dict[str, Dict]:
154
+ """Asynchronously retrieve prices and market caps for symbols.
155
+
156
+ Parameters
157
+ ----------
158
+ session : aiohttp.ClientSession
159
+ Active aiohttp session used to make HTTP requests.
160
+ symbols : list[str]
161
+ Uppercase ticker symbols to query.
162
+
163
+ Returns
164
+ -------
165
+ dict[str, dict]
166
+ Mapping of symbol -> {"market_cap": ..., "name": ..., "id": ...}.
167
+
168
+ Notes
169
+ -----
170
+ Uses the async map loader `_load_map_async` and requests CoinGecko in
171
+ chunks. Failures for a chunk are swallowed and processing continues.
172
+ """
173
+ await self._load_map_async(session)
174
+ results = {}
175
+ ids, id_map = self._get_candidate_ids(symbols)
176
+ if not ids:
177
+ return results
178
+
179
+ chunk_size = 200
180
+ for i in range(0, len(ids), chunk_size):
181
+ chunk = ids[i : i + chunk_size]
182
+ try:
183
+ params = {
184
+ "ids": ",".join(chunk),
185
+ "vs_currencies": "usd",
186
+ "include_market_cap": "true",
187
+ }
188
+ async with session.get(self.price_url, params=params) as resp:
189
+ data = await resp.json()
190
+ self._process_response(data, id_map, results)
191
+ except Exception:
192
+ pass
193
+ return results
194
+
195
+ def _process_response(self, data, id_map, results):
196
+ """Process a CoinGecko price response and update results.
197
+
198
+ Parameters
199
+ ----------
200
+ data : dict
201
+ JSON-decoded response from the CoinGecko simple/price endpoint.
202
+ id_map : dict
203
+ Mapping of coin id -> parent symbol used to group results.
204
+ results : dict
205
+ Mutable mapping that will be updated in-place with the best
206
+ candidate per parent symbol (highest market cap wins).
207
+ """
208
+ for cid, val in data.items():
209
+ parent = id_map.get(cid)
210
+ if parent:
211
+ mcap = val.get("usd_market_cap", 0)
212
+ if mcap > results.get(parent, {}).get("market_cap", 0):
213
+ results[parent] = {
214
+ "market_cap": mcap,
215
+ "name": cid.title(),
216
+ "id": cid,
217
+ }