quant-signal-sdk 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.
Files changed (44) hide show
  1. quant_signal_sdk-0.1.0/LICENSE +21 -0
  2. quant_signal_sdk-0.1.0/PKG-INFO +163 -0
  3. quant_signal_sdk-0.1.0/README.md +127 -0
  4. quant_signal_sdk-0.1.0/pyproject.toml +67 -0
  5. quant_signal_sdk-0.1.0/setup.cfg +4 -0
  6. quant_signal_sdk-0.1.0/src/quant_signal_sdk/__init__.py +75 -0
  7. quant_signal_sdk-0.1.0/src/quant_signal_sdk/adapters.py +13 -0
  8. quant_signal_sdk-0.1.0/src/quant_signal_sdk/ccxt_client.py +41 -0
  9. quant_signal_sdk-0.1.0/src/quant_signal_sdk/cli.py +462 -0
  10. quant_signal_sdk-0.1.0/src/quant_signal_sdk/client.py +250 -0
  11. quant_signal_sdk-0.1.0/src/quant_signal_sdk/core_strategy.py +220 -0
  12. quant_signal_sdk-0.1.0/src/quant_signal_sdk/data_loader.py +198 -0
  13. quant_signal_sdk-0.1.0/src/quant_signal_sdk/data_provider.py +37 -0
  14. quant_signal_sdk-0.1.0/src/quant_signal_sdk/feature_engineer.py +41 -0
  15. quant_signal_sdk-0.1.0/src/quant_signal_sdk/funding_pipeline.py +362 -0
  16. quant_signal_sdk-0.1.0/src/quant_signal_sdk/interfaces.py +10 -0
  17. quant_signal_sdk-0.1.0/src/quant_signal_sdk/models.py +157 -0
  18. quant_signal_sdk-0.1.0/src/quant_signal_sdk/network.py +62 -0
  19. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runner.py +3 -0
  20. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runtime/__init__.py +29 -0
  21. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runtime/adapters.py +311 -0
  22. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runtime/backtest.py +741 -0
  23. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runtime/interfaces.py +42 -0
  24. quant_signal_sdk-0.1.0/src/quant_signal_sdk/runtime/runner.py +125 -0
  25. quant_signal_sdk-0.1.0/src/quant_signal_sdk/signing.py +19 -0
  26. quant_signal_sdk-0.1.0/src/quant_signal_sdk/strategy.py +59 -0
  27. quant_signal_sdk-0.1.0/src/quant_signal_sdk/translator.py +121 -0
  28. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/PKG-INFO +163 -0
  29. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/SOURCES.txt +42 -0
  30. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/dependency_links.txt +1 -0
  31. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/entry_points.txt +2 -0
  32. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/requires.txt +21 -0
  33. quant_signal_sdk-0.1.0/src/quant_signal_sdk.egg-info/top_level.txt +1 -0
  34. quant_signal_sdk-0.1.0/tests/test_backtest_engine.py +158 -0
  35. quant_signal_sdk-0.1.0/tests/test_backtest_exports.py +67 -0
  36. quant_signal_sdk-0.1.0/tests/test_bundle_loader_cli.py +168 -0
  37. quant_signal_sdk-0.1.0/tests/test_client.py +182 -0
  38. quant_signal_sdk-0.1.0/tests/test_contract_compatibility_sdk.py +58 -0
  39. quant_signal_sdk-0.1.0/tests/test_funding_arbitrage_bot.py +166 -0
  40. quant_signal_sdk-0.1.0/tests/test_models.py +86 -0
  41. quant_signal_sdk-0.1.0/tests/test_quant_signal_sdk.py +189 -0
  42. quant_signal_sdk-0.1.0/tests/test_sample_bot_example.py +24 -0
  43. quant_signal_sdk-0.1.0/tests/test_strategy.py +26 -0
  44. quant_signal_sdk-0.1.0/tests/test_translator_policies.py +21 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Organization
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,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: quant-signal-sdk
3
+ Version: 0.1.0
4
+ Summary: Developer SDK for validating and publishing trading signals
5
+ Author-email: Marcus SDK Team <dev@marcus.io>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/marcusio/quant-signal-sdk
8
+ Project-URL: Repository, https://github.com/marcusio/quant-signal-sdk
9
+ Project-URL: Issues, https://github.com/marcusio/quant-signal-sdk/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: pydantic<3.0,>=2.7
18
+ Requires-Dist: requests<3.0,>=2.32
19
+ Requires-Dist: urllib3<3.0,>=2.2
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
22
+ Requires-Dist: pytest-mock>=3.10; extra == "dev"
23
+ Requires-Dist: pyright>=1.1; extra == "dev"
24
+ Requires-Dist: build>=1.0; extra == "dev"
25
+ Requires-Dist: twine>=4.0; extra == "dev"
26
+ Provides-Extra: market-data
27
+ Requires-Dist: pandas<3.0,>=2.2; extra == "market-data"
28
+ Requires-Dist: ccxt<6.0,>=4.0; extra == "market-data"
29
+ Provides-Extra: ml
30
+ Requires-Dist: joblib<2.0,>=1.3; extra == "ml"
31
+ Requires-Dist: lightgbm<5.0,>=4.3; extra == "ml"
32
+ Requires-Dist: numpy<3.0,>=1.26; extra == "ml"
33
+ Requires-Dist: pandas<3.0,>=2.2; extra == "ml"
34
+ Requires-Dist: scikit-learn<2.0,>=1.4; extra == "ml"
35
+ Dynamic: license-file
36
+
37
+ # Quant Signal SDK
38
+
39
+ This repository now contains only the developer SDK package: `quant_signal_sdk`.
40
+
41
+ Local executor client code has been moved to `local-executor-client` in the workspace.
42
+
43
+ ## Features
44
+ - Pydantic signal models and enums (`SignalPayload`, `SignalSide`, `SignalAction`).
45
+ - Retry-enabled HTTP transport (`NetworkClient`) using `requests` and `urllib3.Retry`.
46
+ - Optional HMAC SHA-256 payload signing helper (`generate_hmac_signature`).
47
+ - High-level API client (`QuantSignalClient`) for authenticated signal submission.
48
+ - Minimal `BaseStrategy` contract for strategy inheritance.
49
+ - In-memory portfolio backtest runner with OHLCV replay and execution-policy enforcement.
50
+
51
+ ## Quickstart
52
+ ```python
53
+ from quant_signal_sdk import QuantSignalClient, SignalPayload
54
+
55
+ client = QuantSignalClient(
56
+ base_url="https://api.example.com",
57
+ api_key="your-api-key",
58
+ signer_secret="optional-signing-secret",
59
+ )
60
+
61
+ signal = SignalPayload(
62
+ side="LONG",
63
+ action="OPEN_LONG",
64
+ symbol="BTCUSDT",
65
+ tp=72000,
66
+ sl=68500,
67
+ confidence_score=0.84,
68
+ metadata={"strategy": "trend_v1"},
69
+ )
70
+
71
+ result = client.send_signal(signal)
72
+ print(result)
73
+ ```
74
+
75
+ ## Backtest
76
+
77
+ Create a `my_bot.py` file that exports a strategy class or `STRATEGY` object with `on_event(...)`, then run:
78
+
79
+ ```bash
80
+ quant-sdk backtest --bot-file my_bot.py --data-csv candles.csv --initial-cash 1000
81
+ ```
82
+
83
+ The backtest engine replays OHLCV candles, queues signals for the next tick, and prints a simple portfolio summary when the run completes.
84
+
85
+ If your OHLCV data is stored as Parquet (recommended per `GUIDE_DATA.md`), point the CLI at the Parquet file or directory. Example using the dataset layout in `GUIDE_DATA.md`:
86
+
87
+ ```powershell
88
+ python -m quant_signal_sdk.cli backtest --bot-file my_bot.py \
89
+ --data-parquet "D:\Code\Projects\self-projects\macd-overlay - Copy\data\ohlcv\BTCUSDT.parquet" \
90
+ --timestamp-column timestamp --initial-cash 1000 --output-dir backtest_output_parquet --export-html
91
+ ```
92
+
93
+ The CLI accepts either `--data-csv` (legacy) or `--data-parquet` (preferred). When a directory is passed to `--data-parquet` the first `*.parquet` file is used.
94
+
95
+ ## Requirements
96
+ - Python 3.10+
97
+
98
+ ## Setup
99
+ Install the library for local development:
100
+
101
+ ```bash
102
+ pip install -e .
103
+ ```
104
+
105
+ Install with SDK development tools:
106
+
107
+ ```bash
108
+ pip install -e .[dev]
109
+ ```
110
+
111
+ Install optional market-data helpers:
112
+
113
+ ```bash
114
+ pip install -e .[market-data]
115
+ ```
116
+
117
+ Install both dev and market-data extras together:
118
+
119
+ ```bash
120
+ pip install -e .[dev,market-data]
121
+ ```
122
+
123
+ After publishing to PyPI, users can install the released package with:
124
+
125
+ ```bash
126
+ pip install quant-signal-sdk
127
+ ```
128
+
129
+ ## Tests
130
+ ```bash
131
+ python -m unittest discover -s tests -v
132
+ ```
133
+
134
+ ## Release
135
+ Build the distribution and validate the artifacts before upload:
136
+
137
+ ```bash
138
+ python -m build --sdist --wheel
139
+ python -m twine check dist/*
140
+ python -m twine upload dist/*
141
+ ```
142
+
143
+ ## Example: register -> signer secret behavior
144
+
145
+ When registering a bot via the example `examples/sample_bot.py`, the backend returns
146
+ both an `apiKey` (runtime API key) and a `rawSecret` (signer secret). The example
147
+ attaches the returned `rawSecret` to the `QuantSignalClient` as the `signer_secret`
148
+ when the user did not already supply one. This ensures subsequent signal POSTs
149
+ include the required `X-Timestamp` and `X-Signature` headers that the server
150
+ validates.
151
+
152
+ If you prefer to manage signing secrets yourself, pass `--bot-signer-secret` to
153
+ the example and the returned `rawSecret` will not overwrite it.
154
+
155
+
156
+ ## Build
157
+ ```bash
158
+ python -m build --sdist --wheel
159
+ ```
160
+
161
+ ## Contract Fixtures
162
+ - SDK fixtures are versioned under `tests/fixtures/contracts`.
163
+ - SDK compatibility checks are in `tests/test_contract_compatibility_sdk.py`.
@@ -0,0 +1,127 @@
1
+ # Quant Signal SDK
2
+
3
+ This repository now contains only the developer SDK package: `quant_signal_sdk`.
4
+
5
+ Local executor client code has been moved to `local-executor-client` in the workspace.
6
+
7
+ ## Features
8
+ - Pydantic signal models and enums (`SignalPayload`, `SignalSide`, `SignalAction`).
9
+ - Retry-enabled HTTP transport (`NetworkClient`) using `requests` and `urllib3.Retry`.
10
+ - Optional HMAC SHA-256 payload signing helper (`generate_hmac_signature`).
11
+ - High-level API client (`QuantSignalClient`) for authenticated signal submission.
12
+ - Minimal `BaseStrategy` contract for strategy inheritance.
13
+ - In-memory portfolio backtest runner with OHLCV replay and execution-policy enforcement.
14
+
15
+ ## Quickstart
16
+ ```python
17
+ from quant_signal_sdk import QuantSignalClient, SignalPayload
18
+
19
+ client = QuantSignalClient(
20
+ base_url="https://api.example.com",
21
+ api_key="your-api-key",
22
+ signer_secret="optional-signing-secret",
23
+ )
24
+
25
+ signal = SignalPayload(
26
+ side="LONG",
27
+ action="OPEN_LONG",
28
+ symbol="BTCUSDT",
29
+ tp=72000,
30
+ sl=68500,
31
+ confidence_score=0.84,
32
+ metadata={"strategy": "trend_v1"},
33
+ )
34
+
35
+ result = client.send_signal(signal)
36
+ print(result)
37
+ ```
38
+
39
+ ## Backtest
40
+
41
+ Create a `my_bot.py` file that exports a strategy class or `STRATEGY` object with `on_event(...)`, then run:
42
+
43
+ ```bash
44
+ quant-sdk backtest --bot-file my_bot.py --data-csv candles.csv --initial-cash 1000
45
+ ```
46
+
47
+ The backtest engine replays OHLCV candles, queues signals for the next tick, and prints a simple portfolio summary when the run completes.
48
+
49
+ If your OHLCV data is stored as Parquet (recommended per `GUIDE_DATA.md`), point the CLI at the Parquet file or directory. Example using the dataset layout in `GUIDE_DATA.md`:
50
+
51
+ ```powershell
52
+ python -m quant_signal_sdk.cli backtest --bot-file my_bot.py \
53
+ --data-parquet "D:\Code\Projects\self-projects\macd-overlay - Copy\data\ohlcv\BTCUSDT.parquet" \
54
+ --timestamp-column timestamp --initial-cash 1000 --output-dir backtest_output_parquet --export-html
55
+ ```
56
+
57
+ The CLI accepts either `--data-csv` (legacy) or `--data-parquet` (preferred). When a directory is passed to `--data-parquet` the first `*.parquet` file is used.
58
+
59
+ ## Requirements
60
+ - Python 3.10+
61
+
62
+ ## Setup
63
+ Install the library for local development:
64
+
65
+ ```bash
66
+ pip install -e .
67
+ ```
68
+
69
+ Install with SDK development tools:
70
+
71
+ ```bash
72
+ pip install -e .[dev]
73
+ ```
74
+
75
+ Install optional market-data helpers:
76
+
77
+ ```bash
78
+ pip install -e .[market-data]
79
+ ```
80
+
81
+ Install both dev and market-data extras together:
82
+
83
+ ```bash
84
+ pip install -e .[dev,market-data]
85
+ ```
86
+
87
+ After publishing to PyPI, users can install the released package with:
88
+
89
+ ```bash
90
+ pip install quant-signal-sdk
91
+ ```
92
+
93
+ ## Tests
94
+ ```bash
95
+ python -m unittest discover -s tests -v
96
+ ```
97
+
98
+ ## Release
99
+ Build the distribution and validate the artifacts before upload:
100
+
101
+ ```bash
102
+ python -m build --sdist --wheel
103
+ python -m twine check dist/*
104
+ python -m twine upload dist/*
105
+ ```
106
+
107
+ ## Example: register -> signer secret behavior
108
+
109
+ When registering a bot via the example `examples/sample_bot.py`, the backend returns
110
+ both an `apiKey` (runtime API key) and a `rawSecret` (signer secret). The example
111
+ attaches the returned `rawSecret` to the `QuantSignalClient` as the `signer_secret`
112
+ when the user did not already supply one. This ensures subsequent signal POSTs
113
+ include the required `X-Timestamp` and `X-Signature` headers that the server
114
+ validates.
115
+
116
+ If you prefer to manage signing secrets yourself, pass `--bot-signer-secret` to
117
+ the example and the returned `rawSecret` will not overwrite it.
118
+
119
+
120
+ ## Build
121
+ ```bash
122
+ python -m build --sdist --wheel
123
+ ```
124
+
125
+ ## Contract Fixtures
126
+ - SDK fixtures are versioned under `tests/fixtures/contracts`.
127
+ - SDK compatibility checks are in `tests/test_contract_compatibility_sdk.py`.
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "quant-signal-sdk"
7
+ version = "0.1.0"
8
+ description = "Developer SDK for validating and publishing trading signals"
9
+ readme = { file = "README.md", content-type = "text/markdown" }
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Marcus SDK Team", email = "dev@marcus.io" }
13
+ ]
14
+ license = "MIT"
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Operating System :: OS Independent",
20
+ ]
21
+ dependencies = [
22
+ "pydantic>=2.7,<3.0",
23
+ "requests>=2.32,<3.0",
24
+ "urllib3>=2.2,<3.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/marcusio/quant-signal-sdk"
29
+ Repository = "https://github.com/marcusio/quant-signal-sdk"
30
+ Issues = "https://github.com/marcusio/quant-signal-sdk/issues"
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=8.0,<9.0",
35
+ "pytest-mock>=3.10",
36
+ "pyright>=1.1",
37
+ "build>=1.0",
38
+ "twine>=4.0",
39
+ ]
40
+
41
+ market-data = [
42
+ "pandas>=2.2,<3.0",
43
+ "ccxt>=4.0,<6.0",
44
+ ]
45
+
46
+ ml = [
47
+ "joblib>=1.3,<2.0",
48
+ "lightgbm>=4.3,<5.0",
49
+ "numpy>=1.26,<3.0",
50
+ "pandas>=2.2,<3.0",
51
+ "scikit-learn>=1.4,<2.0",
52
+ ]
53
+
54
+ [project.scripts]
55
+ quant-sdk = "quant_signal_sdk.cli:main"
56
+
57
+ [tool.setuptools]
58
+ package-dir = {"" = "src"}
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+
66
+ [tool.pyright]
67
+ extraPaths = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,75 @@
1
+ """Public API for quant_signal_sdk.
2
+
3
+ The package intentionally exposes a minimal surface area: `QuantSignalClient`,
4
+ the `SignalPayload` model and enums, `BaseStrategy`, and the `generate_hmac_signature`
5
+ helper. Example/demo bots and heavy deps (CCXT usage) live under `examples/`.
6
+ """
7
+ from .client import QuantSignalClient
8
+ from .models import (
9
+ MarginMode,
10
+ MarketType,
11
+ OrderType,
12
+ ExecutionPolicies,
13
+ SignalAction,
14
+ SignalPayload,
15
+ SignalSide,
16
+ SignalStatus,
17
+ )
18
+ from .signing import generate_hmac_signature
19
+ from .strategy import BaseStrategy
20
+ from .data_provider import DataProvider, CcxtDataProvider
21
+ from .feature_engineer import FeatureEngineer
22
+ from .interfaces import BaseDispatcher, BaseFeed, MarketEvent, PortfolioContext, Signal
23
+ from .runner import Runner
24
+ from .adapters import BaseTrigger, CronTrigger, DataFrameFeed, IntervalTrigger, LiveHTTPDispatcher, LiveRESTFeed, MockDispatcher, ParquetReplayFeed, ScheduledRESTFeed
25
+ from .runtime.backtest import BacktestConfig, BacktestFill, BacktestOrder, BacktestReport, OhlcvReplayFeed, PortfolioBacktestRunner
26
+ from .core_strategy import FundingArbitrageConfig, FundingArbitrageStrategy
27
+ from .data_loader import BundleLoader, BundleManifest
28
+ from .translator import SignalTranslator, BoundaryValidationException, RiskManager, PercentageRiskManager
29
+
30
+ __all__ = [
31
+ "QuantSignalClient",
32
+ "MarginMode",
33
+ "MarketType",
34
+ "OrderType",
35
+ "ExecutionPolicies",
36
+ "SignalAction",
37
+ "SignalPayload",
38
+ "SignalSide",
39
+ "SignalStatus",
40
+ "generate_hmac_signature",
41
+ "BaseStrategy",
42
+ "DataProvider",
43
+ "CcxtDataProvider",
44
+ "FeatureEngineer",
45
+ "BaseDispatcher",
46
+ "BaseFeed",
47
+ "BaseStrategy",
48
+ "MarketEvent",
49
+ "PortfolioContext",
50
+ "Signal",
51
+ "Runner",
52
+ "BacktestConfig",
53
+ "BacktestFill",
54
+ "BacktestOrder",
55
+ "BacktestReport",
56
+ "BaseTrigger",
57
+ "DataFrameFeed",
58
+ "CronTrigger",
59
+ "OhlcvReplayFeed",
60
+ "IntervalTrigger",
61
+ "PortfolioBacktestRunner",
62
+ "LiveHTTPDispatcher",
63
+ "LiveRESTFeed",
64
+ "MockDispatcher",
65
+ "ParquetReplayFeed",
66
+ "ScheduledRESTFeed",
67
+ "BundleLoader",
68
+ "BundleManifest",
69
+ "FundingArbitrageConfig",
70
+ "FundingArbitrageStrategy",
71
+ "SignalTranslator",
72
+ "BoundaryValidationException",
73
+ "RiskManager",
74
+ "PercentageRiskManager",
75
+ ]
@@ -0,0 +1,13 @@
1
+ from .runtime.adapters import BaseTrigger, CronTrigger, DataFrameFeed, IntervalTrigger, LiveHTTPDispatcher, LiveRESTFeed, MockDispatcher, ParquetReplayFeed, ScheduledRESTFeed
2
+
3
+ __all__ = [
4
+ "BaseTrigger",
5
+ "CronTrigger",
6
+ "DataFrameFeed",
7
+ "IntervalTrigger",
8
+ "LiveHTTPDispatcher",
9
+ "LiveRESTFeed",
10
+ "MockDispatcher",
11
+ "ParquetReplayFeed",
12
+ "ScheduledRESTFeed",
13
+ ]
@@ -0,0 +1,41 @@
1
+ """Small CCXT wrapper to fetch OHLCV data for strategies.
2
+
3
+ `ccxt` is an optional dependency. If it's not installed the module will raise
4
+ an ImportError with instructions to install the `market-data` extra.
5
+ """
6
+ from typing import List
7
+
8
+ try:
9
+ import ccxt # type: ignore
10
+ except Exception as exc: # ImportError or other import-time errors
11
+ ccxt = None # type: ignore
12
+ _CCXT_IMPORT_ERROR = exc
13
+
14
+
15
+ class CCXTClient:
16
+ def __init__(self, exchange_id: str = "binance"):
17
+ if ccxt is None:
18
+ raise ImportError(
19
+ "ccxt is not installed. Install it with: `pip install .[market-data]` "
20
+ "or `pip install ccxt`."
21
+ ) from _CCXT_IMPORT_ERROR
22
+
23
+ if not hasattr(ccxt, exchange_id):
24
+ raise ValueError(f"Unknown exchange: {exchange_id}")
25
+ self.exchange = getattr(ccxt, exchange_id)()
26
+ try:
27
+ self.exchange.load_markets()
28
+ except Exception:
29
+ # best-effort, some test envs may not allow network calls
30
+ pass
31
+
32
+ def fetch_ohlcv(self, symbol: str, timeframe: str = "1m", limit: int = 100) -> List[List[float]]:
33
+ """Return OHLCV rows as provided by ccxt: [timestamp, open, high, low, close, volume]
34
+
35
+ Raises ccxt.BaseError on network / exchange errors.
36
+ """
37
+ return self.exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=limit)
38
+
39
+
40
+ def close_prices_from_ohlcv(ohlcv: List[List[float]]) -> List[float]:
41
+ return [float(row[4]) for row in ohlcv if len(row) >= 5]