binance-quant-engine 0.1.1__tar.gz → 0.1.2__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.
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/PKG-INFO +21 -7
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/README.md +4 -4
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/pyproject.toml +25 -4
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/__init__.py +1 -1
- binance_quant_engine-0.1.2/tests/test_brackets.py +161 -0
- binance_quant_engine-0.1.2/tests/test_mcp_stdio_e2e.py +35 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.env.example +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.github/workflows/ci.yml +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.github/workflows/commit-identity.yml +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.github/workflows/publish.yml +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.gitignore +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/LICENSE +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/docs/ARCHITECTURE.md +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/docs/USAGE.md +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/docs/images/demo-backtest.png +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/backtest/__init__.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/backtest/vectorized.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/__init__.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/cache.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/klines.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/__init__.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/algo_api.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/brackets.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/host.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/utils.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/mcp/__init__.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/mcp/server.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/strategy/__init__.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/strategy/demo_squeeze.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/strategy/protocol.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/tests/test_backtest.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/tests/test_demo_squeeze.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/tests/test_execution_utils.py +0 -0
- {binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/tests/test_mcp_server.py +0 -0
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: binance-quant-engine
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Strategy-agnostic Binance USDT-M futures backtest & execution engine — no-look-ahead, backtest/live parity, optional MCP server
|
|
5
|
-
Project-URL: Homepage, https://
|
|
5
|
+
Project-URL: Homepage, https://github.com/younghwan91/binance-quant-engine
|
|
6
6
|
Project-URL: Repository, https://github.com/younghwan91/binance-quant-engine
|
|
7
|
+
Project-URL: Documentation, https://github.com/younghwan91/binance-quant-engine/blob/main/docs/USAGE.md
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/younghwan91/binance-quant-engine/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/younghwan91/binance-quant-engine/releases
|
|
7
10
|
Author-email: Younghwan Chae <chyohw97@gmail.com>
|
|
8
11
|
License-Expression: MIT
|
|
9
12
|
License-File: LICENSE
|
|
10
|
-
Keywords: algo-trading,backtest,binance,execution,futures,mcp,quant,trading
|
|
13
|
+
Keywords: algo-trading,algorithmic-trading,backtest,backtesting,binance,binance-futures,crypto,cryptocurrency,execution,futures,mcp,model-context-protocol,quant,quantitative-trading,trading,trading-bot
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
24
|
Requires-Python: >=3.10
|
|
12
25
|
Requires-Dist: numpy>=1.26
|
|
13
26
|
Requires-Dist: pandas>=2.0
|
|
14
27
|
Provides-Extra: dev
|
|
15
28
|
Requires-Dist: mcp>=1.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
16
30
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
17
31
|
Requires-Dist: python-binance>=1.0.29; extra == 'dev'
|
|
18
32
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
@@ -112,12 +126,12 @@ flowchart LR
|
|
|
112
126
|
subgraph Decide["binance_quant_engine/strategy/ — decide"]
|
|
113
127
|
PROTO{{"TradingStrategy\n(PEP 544 protocol)"}}
|
|
114
128
|
SQZ["demo_squeeze.py\nSqueezeStrategy"]
|
|
115
|
-
PROTO
|
|
129
|
+
PROTO -.->|"implements"| SQZ
|
|
116
130
|
end
|
|
117
131
|
|
|
118
132
|
CSV --> BT
|
|
119
133
|
SYN --> BT
|
|
120
|
-
CACHE
|
|
134
|
+
CACHE -.->|"caches"| CSV
|
|
121
135
|
|
|
122
136
|
subgraph Measure["binance_quant_engine/backtest/ — measure"]
|
|
123
137
|
BT["vectorized.run_backtest()\nhands close[:t+1] only\n(no look-ahead)"]
|
|
@@ -139,9 +153,9 @@ flowchart LR
|
|
|
139
153
|
subgraph MCP["binance_quant_engine/mcp/ — optional (.[mcp])"]
|
|
140
154
|
SRV["server.py\nrun_demo_backtest · backtest_csv"]
|
|
141
155
|
end
|
|
142
|
-
BT
|
|
156
|
+
BT -.->|"callable via"| SRV
|
|
143
157
|
|
|
144
|
-
PROTO
|
|
158
|
+
PROTO ==>|"same interface<br/>(backtest = live)"| HOST
|
|
145
159
|
ALGO --> BINANCE[("Binance USDT-M\nFutures Algo Order API")]
|
|
146
160
|
```
|
|
147
161
|
|
|
@@ -88,12 +88,12 @@ flowchart LR
|
|
|
88
88
|
subgraph Decide["binance_quant_engine/strategy/ — decide"]
|
|
89
89
|
PROTO{{"TradingStrategy\n(PEP 544 protocol)"}}
|
|
90
90
|
SQZ["demo_squeeze.py\nSqueezeStrategy"]
|
|
91
|
-
PROTO
|
|
91
|
+
PROTO -.->|"implements"| SQZ
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
CSV --> BT
|
|
95
95
|
SYN --> BT
|
|
96
|
-
CACHE
|
|
96
|
+
CACHE -.->|"caches"| CSV
|
|
97
97
|
|
|
98
98
|
subgraph Measure["binance_quant_engine/backtest/ — measure"]
|
|
99
99
|
BT["vectorized.run_backtest()\nhands close[:t+1] only\n(no look-ahead)"]
|
|
@@ -115,9 +115,9 @@ flowchart LR
|
|
|
115
115
|
subgraph MCP["binance_quant_engine/mcp/ — optional (.[mcp])"]
|
|
116
116
|
SRV["server.py\nrun_demo_backtest · backtest_csv"]
|
|
117
117
|
end
|
|
118
|
-
BT
|
|
118
|
+
BT -.->|"callable via"| SRV
|
|
119
119
|
|
|
120
|
-
PROTO
|
|
120
|
+
PROTO ==>|"same interface<br/>(backtest = live)"| HOST
|
|
121
121
|
ALGO --> BINANCE[("Binance USDT-M\nFutures Algo Order API")]
|
|
122
122
|
```
|
|
123
123
|
|
|
@@ -4,13 +4,30 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "binance-quant-engine"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Strategy-agnostic Binance USDT-M futures backtest & execution engine — no-look-ahead, backtest/live parity, optional MCP server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
|
-
keywords = [
|
|
12
|
+
keywords = [
|
|
13
|
+
"quant", "quantitative-trading", "backtest", "backtesting", "trading",
|
|
14
|
+
"binance", "binance-futures", "crypto", "cryptocurrency", "futures",
|
|
15
|
+
"algo-trading", "algorithmic-trading", "trading-bot", "execution", "mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
]
|
|
13
18
|
authors = [{ name = "Younghwan Chae", email = "chyohw97@gmail.com" }]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
23
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
]
|
|
14
31
|
dependencies = [
|
|
15
32
|
"numpy>=1.26",
|
|
16
33
|
"pandas>=2.0",
|
|
@@ -20,21 +37,25 @@ dependencies = [
|
|
|
20
37
|
# Live execution talks to Binance USDT-M Futures; not needed for backtests.
|
|
21
38
|
live = ["python-binance>=1.0.29"]
|
|
22
39
|
mcp = ["mcp>=1.0"]
|
|
23
|
-
dev = ["pytest>=8.0", "ruff>=0.6", "python-binance>=1.0.29", "mcp>=1.0"]
|
|
40
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.24", "ruff>=0.6", "python-binance>=1.0.29", "mcp>=1.0"]
|
|
24
41
|
|
|
25
42
|
[project.scripts]
|
|
26
43
|
bqe-backtest = "binance_quant_engine.backtest.vectorized:main"
|
|
27
44
|
binance-quant-engine-mcp = "binance_quant_engine.mcp.server:main"
|
|
28
45
|
|
|
29
46
|
[project.urls]
|
|
30
|
-
Homepage = "https://
|
|
47
|
+
Homepage = "https://github.com/younghwan91/binance-quant-engine"
|
|
31
48
|
Repository = "https://github.com/younghwan91/binance-quant-engine"
|
|
49
|
+
Documentation = "https://github.com/younghwan91/binance-quant-engine/blob/main/docs/USAGE.md"
|
|
50
|
+
"Bug Tracker" = "https://github.com/younghwan91/binance-quant-engine/issues"
|
|
51
|
+
Changelog = "https://github.com/younghwan91/binance-quant-engine/releases"
|
|
32
52
|
|
|
33
53
|
[tool.hatch.build.targets.wheel]
|
|
34
54
|
packages = ["src/binance_quant_engine"]
|
|
35
55
|
|
|
36
56
|
[tool.pytest.ini_options]
|
|
37
57
|
testpaths = ["tests"]
|
|
58
|
+
asyncio_mode = "strict"
|
|
38
59
|
|
|
39
60
|
[tool.ruff]
|
|
40
61
|
line-length = 100
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Integration tests for BracketMixin against a fake host.
|
|
2
|
+
|
|
3
|
+
Exercises the mixin the way the live bot actually drives it — through
|
|
4
|
+
``self`` — rather than testing the extracted :mod:`utils` functions in
|
|
5
|
+
isolation. This is what proves the two correctness fixes upstreamed from
|
|
6
|
+
the private system (tick-safe activationPrice, REJECTED as terminal) are
|
|
7
|
+
actually wired into the code path a real bot calls, not just correct as
|
|
8
|
+
standalone functions.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from binance_quant_engine.execution.brackets import BracketMixin
|
|
14
|
+
from binance_quant_engine.execution.host import TrailConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FakeAlgoApi:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.placed: list[dict] = []
|
|
20
|
+
self.next_algo_id = 1
|
|
21
|
+
self.raise_on_place: Exception | None = None
|
|
22
|
+
self.get_order_response: dict | None = None
|
|
23
|
+
|
|
24
|
+
def place_order(self, params: dict) -> dict:
|
|
25
|
+
if self.raise_on_place is not None:
|
|
26
|
+
raise self.raise_on_place
|
|
27
|
+
self.placed.append(params)
|
|
28
|
+
algo_id = self.next_algo_id
|
|
29
|
+
self.next_algo_id += 1
|
|
30
|
+
return {"algoId": algo_id}
|
|
31
|
+
|
|
32
|
+
def cancel_order(self, algo_id: int) -> dict:
|
|
33
|
+
return {"algoId": algo_id, "status": "CANCELLED"}
|
|
34
|
+
|
|
35
|
+
def get_order(self, algo_id: int) -> dict:
|
|
36
|
+
return self.get_order_response or {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FakeDiscord:
|
|
40
|
+
def __init__(self):
|
|
41
|
+
self.messages: list[str] = []
|
|
42
|
+
|
|
43
|
+
def send(self, content: str) -> None:
|
|
44
|
+
self.messages.append(content)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class FakeHost(BracketMixin):
|
|
48
|
+
"""Minimal concrete implementation of ScalperProtocol for tests."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, *, paper_mode: bool = False, tick_size: float = 0.01, price_precision: int = 2):
|
|
51
|
+
self.paper_mode = paper_mode
|
|
52
|
+
self.client = None
|
|
53
|
+
self.discord = FakeDiscord()
|
|
54
|
+
self.strategy = None
|
|
55
|
+
self.trail_config = TrailConfig()
|
|
56
|
+
self._algo_api = FakeAlgoApi()
|
|
57
|
+
self._tick_size = tick_size
|
|
58
|
+
self._price_precision = price_precision
|
|
59
|
+
|
|
60
|
+
def get_symbol_info(self, symbol: str) -> dict:
|
|
61
|
+
return {"tick_size": self._tick_size, "price_precision": self._price_precision}
|
|
62
|
+
|
|
63
|
+
def _round_qty(self, qty: float, symbol: str) -> float:
|
|
64
|
+
return qty
|
|
65
|
+
|
|
66
|
+
def _save_state(self) -> None:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_paper_mode_stop_loss_never_calls_algo_api():
|
|
71
|
+
host = FakeHost(paper_mode=True)
|
|
72
|
+
algo_id = host._place_server_stop_loss(
|
|
73
|
+
"BTCUSDT", signal=1, quantity=1.0, entry_price=100.0, move_size=0.05, stop_mult=1.0
|
|
74
|
+
)
|
|
75
|
+
assert algo_id.startswith("paper_sl_")
|
|
76
|
+
assert host._algo_api.placed == []
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_stop_loss_prices_are_tick_rounded():
|
|
80
|
+
# tick_size=0.01 forces a price like 95.037 to round to 95.04.
|
|
81
|
+
host = FakeHost(tick_size=0.01, price_precision=2)
|
|
82
|
+
host._place_server_stop_loss(
|
|
83
|
+
"BTCUSDT", signal=1, quantity=1.0, entry_price=100.037, move_size=0.05, stop_mult=1.0
|
|
84
|
+
)
|
|
85
|
+
assert len(host._algo_api.placed) == 1
|
|
86
|
+
params = host._algo_api.placed[0]
|
|
87
|
+
trigger = float(params["triggerPrice"])
|
|
88
|
+
limit = float(params["price"])
|
|
89
|
+
assert round(trigger / 0.01) * 0.01 == round(trigger, 10) # exactly on the tick grid
|
|
90
|
+
assert round(limit / 0.01) * 0.01 == round(limit, 10)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_stop_loss_negative_stop_mult_aborts_without_calling_api():
|
|
94
|
+
host = FakeHost()
|
|
95
|
+
algo_id = host._place_server_stop_loss(
|
|
96
|
+
"BTCUSDT", signal=1, quantity=1.0, entry_price=100.0, move_size=0.05, stop_mult=-1.0
|
|
97
|
+
)
|
|
98
|
+
assert algo_id is None
|
|
99
|
+
assert host._algo_api.placed == []
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_take_profit_invariant_guards_reject_bad_inputs():
|
|
103
|
+
host = FakeHost()
|
|
104
|
+
assert host._place_server_take_profit(
|
|
105
|
+
"BTCUSDT", signal=1, quantity=1.0, entry_price=100.0, move_size=0.05, target_retrace=0.0
|
|
106
|
+
) is None
|
|
107
|
+
assert host._algo_api.placed == []
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_trailing_stop_activation_price_is_tick_rounded():
|
|
111
|
+
# An activation price that lands off the 0.1 tick grid must be snapped
|
|
112
|
+
# before it reaches params — this is the exact bug fixed vs. the
|
|
113
|
+
# quantbox-engine predecessor.
|
|
114
|
+
host = FakeHost(tick_size=0.1, price_precision=1)
|
|
115
|
+
host._place_trailing_stop_market(
|
|
116
|
+
"BTCUSDT", side="SELL", quantity=1.0, callback_rate=1.0, activation_price=100.037
|
|
117
|
+
)
|
|
118
|
+
assert len(host._algo_api.placed) == 1
|
|
119
|
+
activation = float(host._algo_api.placed[0]["activationPrice"])
|
|
120
|
+
assert activation == 100.0 # 100.037 snapped to the nearest 0.1 tick
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_algo_order_would_trigger_returns_sentinel():
|
|
124
|
+
host = FakeHost()
|
|
125
|
+
host._algo_api.raise_on_place = Exception("APIError(code=-2021): Order would immediately trigger")
|
|
126
|
+
result = host._place_algo_order(
|
|
127
|
+
"BTCUSDT", side="SELL", order_type="STOP", quantity=1.0, trigger_price=100.0
|
|
128
|
+
)
|
|
129
|
+
assert result == "WOULD_TRIGGER"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_cancel_server_order_treats_unknown_order_as_success():
|
|
133
|
+
import json
|
|
134
|
+
|
|
135
|
+
from binance.exceptions import BinanceAPIException
|
|
136
|
+
|
|
137
|
+
class _FakeResponse:
|
|
138
|
+
status_code = 400
|
|
139
|
+
|
|
140
|
+
class _Raiser:
|
|
141
|
+
def cancel_order(self, algo_id):
|
|
142
|
+
body = json.dumps({"code": -2011, "msg": "Unknown order sent."})
|
|
143
|
+
raise BinanceAPIException(_FakeResponse(), 400, body)
|
|
144
|
+
|
|
145
|
+
host = FakeHost()
|
|
146
|
+
host._algo_api = _Raiser()
|
|
147
|
+
assert host._cancel_server_stop_loss("BTCUSDT", "12345") is True
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_check_order_filled_recognizes_rejected_as_dead():
|
|
151
|
+
# The exact upstreamed fix: quantbox-engine's predecessor set didn't
|
|
152
|
+
# include REJECTED, so a rejected algo order stayed "tracked" forever.
|
|
153
|
+
host = FakeHost()
|
|
154
|
+
host._algo_api.get_order_response = {"algoStatus": "REJECTED"}
|
|
155
|
+
result = host._check_order_filled("BTCUSDT", "999")
|
|
156
|
+
assert result == {"status": "CANCELLED", "algoId": "999"}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_verify_algo_order_active_paper_mode_is_always_working():
|
|
160
|
+
host = FakeHost(paper_mode=True)
|
|
161
|
+
assert host._verify_algo_order_active("999", "BTCUSDT") == "WORKING"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""End-to-end smoke test: spawn the actual `binance-quant-engine-mcp` process
|
|
2
|
+
and speak real MCP-over-stdio to it, instead of calling the tool functions
|
|
3
|
+
in-process. This is what proves the packaged entry point (not just the
|
|
4
|
+
Python module) works for a real MCP client.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import shutil
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
pytest.importorskip("mcp")
|
|
15
|
+
|
|
16
|
+
from mcp import ClientSession, StdioServerParameters # noqa: E402
|
|
17
|
+
from mcp.client.stdio import stdio_client # noqa: E402
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.asyncio
|
|
21
|
+
async def test_mcp_server_entry_point_lists_and_calls_tools():
|
|
22
|
+
exe = shutil.which("binance-quant-engine-mcp") or sys.executable
|
|
23
|
+
args = [] if exe != sys.executable else ["-m", "binance_quant_engine.mcp.server"]
|
|
24
|
+
|
|
25
|
+
params = StdioServerParameters(command=exe, args=args)
|
|
26
|
+
async with stdio_client(params) as (read, write):
|
|
27
|
+
async with ClientSession(read, write) as session:
|
|
28
|
+
await session.initialize()
|
|
29
|
+
|
|
30
|
+
tools = await session.list_tools()
|
|
31
|
+
names = {t.name for t in tools.tools}
|
|
32
|
+
assert {"run_demo_backtest", "backtest_csv", "describe_strategy_protocol"} <= names
|
|
33
|
+
|
|
34
|
+
result = await session.call_tool("run_demo_backtest", {"n_bars": 400, "seed": 3})
|
|
35
|
+
assert not result.is_error
|
|
File without changes
|
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/.github/workflows/commit-identity.yml
RENAMED
|
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
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/__init__.py
RENAMED
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/cache.py
RENAMED
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/data/klines.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/execution/host.py
RENAMED
|
File without changes
|
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/mcp/__init__.py
RENAMED
|
File without changes
|
{binance_quant_engine-0.1.1 → binance_quant_engine-0.1.2}/src/binance_quant_engine/mcp/server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|