arithmion-core 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 (42) hide show
  1. arithmion_core-0.1.0/LICENSE +21 -0
  2. arithmion_core-0.1.0/PKG-INFO +172 -0
  3. arithmion_core-0.1.0/README.md +148 -0
  4. arithmion_core-0.1.0/pyproject.toml +40 -0
  5. arithmion_core-0.1.0/setup.cfg +4 -0
  6. arithmion_core-0.1.0/src/apexquant/core/__init__.py +6 -0
  7. arithmion_core-0.1.0/src/apexquant/core/backtest/__init__.py +17 -0
  8. arithmion_core-0.1.0/src/apexquant/core/backtest/result.py +192 -0
  9. arithmion_core-0.1.0/src/apexquant/core/backtest/serialize.py +108 -0
  10. arithmion_core-0.1.0/src/apexquant/core/bar.py +186 -0
  11. arithmion_core-0.1.0/src/apexquant/core/broker.py +613 -0
  12. arithmion_core-0.1.0/src/apexquant/core/calendar.py +54 -0
  13. arithmion_core-0.1.0/src/apexquant/core/config_schema.py +126 -0
  14. arithmion_core-0.1.0/src/apexquant/core/datasource.py +471 -0
  15. arithmion_core-0.1.0/src/apexquant/core/engine.py +212 -0
  16. arithmion_core-0.1.0/src/apexquant/core/metric.py +234 -0
  17. arithmion_core-0.1.0/src/apexquant/core/model_meta.py +149 -0
  18. arithmion_core-0.1.0/src/apexquant/core/panel.py +196 -0
  19. arithmion_core-0.1.0/src/apexquant/core/plugins.py +192 -0
  20. arithmion_core-0.1.0/src/apexquant/core/portfolio_strategy.py +106 -0
  21. arithmion_core-0.1.0/src/apexquant/core/predictor.py +348 -0
  22. arithmion_core-0.1.0/src/apexquant/core/py.typed +0 -0
  23. arithmion_core-0.1.0/src/apexquant/core/registry.py +110 -0
  24. arithmion_core-0.1.0/src/apexquant/core/result.py +271 -0
  25. arithmion_core-0.1.0/src/apexquant/core/risk/__init__.py +56 -0
  26. arithmion_core-0.1.0/src/apexquant/core/risk/manager.py +114 -0
  27. arithmion_core-0.1.0/src/apexquant/core/risk/sizing.py +101 -0
  28. arithmion_core-0.1.0/src/apexquant/core/risk/stops.py +129 -0
  29. arithmion_core-0.1.0/src/apexquant/core/signals.py +162 -0
  30. arithmion_core-0.1.0/src/apexquant/core/strategy.py +770 -0
  31. arithmion_core-0.1.0/src/apexquant/core/streaming.py +196 -0
  32. arithmion_core-0.1.0/src/apexquant/core/trade_plan.py +53 -0
  33. arithmion_core-0.1.0/src/apexquant/core/training/__init__.py +40 -0
  34. arithmion_core-0.1.0/src/apexquant/core/training/rolling.py +121 -0
  35. arithmion_core-0.1.0/src/apexquant/core/training/trainer.py +163 -0
  36. arithmion_core-0.1.0/src/apexquant/core/universe.py +94 -0
  37. arithmion_core-0.1.0/src/apexquant/core/validation.py +249 -0
  38. arithmion_core-0.1.0/src/arithmion_core.egg-info/PKG-INFO +172 -0
  39. arithmion_core-0.1.0/src/arithmion_core.egg-info/SOURCES.txt +40 -0
  40. arithmion_core-0.1.0/src/arithmion_core.egg-info/dependency_links.txt +1 -0
  41. arithmion_core-0.1.0/src/arithmion_core.egg-info/requires.txt +5 -0
  42. arithmion_core-0.1.0/src/arithmion_core.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Arithmion-git
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,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: arithmion-core
3
+ Version: 0.1.0
4
+ Summary: Interface tier of the Arithmion agent-native quant platform: contracts, typed data carriers, and the plugin registry.
5
+ Author: Arithmion-git
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://arithmion.com
8
+ Project-URL: Repository, https://github.com/Arithmion-git/arithmion-core
9
+ Keywords: quant,trading,backtesting,framework,plugins,agent
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Financial and Insurance Industry
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: pandas>=2.0
20
+ Requires-Dist: loguru>=0.7
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest; extra == "test"
23
+ Dynamic: license-file
24
+
25
+ # arithmion-core
26
+
27
+ The interface tier of [Arithmion](https://arithmion.com), an AI-native
28
+ quantitative research and trading platform. This package contains the
29
+ contracts the platform is built on: abstract base classes, typed data
30
+ carriers, and the entry-point plugin registry. It contains no engine, no
31
+ providers, and no agent runtime — those live in the Arithmion product, which
32
+ implements these contracts.
33
+
34
+ **Links** — [arithmion.com](https://arithmion.com) for the product;
35
+ [arithmion.com/learn](https://arithmion.com/learn/) for the learning center, a
36
+ free bilingual (English / 中文) quant curriculum covering market structure,
37
+ statistics, machine learning, and a validation track on multiple testing, PBO
38
+ and the deflated Sharpe ratio — also available as markdown in
39
+ [quant-learning-path](https://github.com/Arithmion-git/quant-learning-path).
40
+
41
+ The package installs into the `apexquant` namespace (`import apexquant.core`),
42
+ which is the platform's historical package name; the import path is shared
43
+ with the full product so that code written against these contracts runs
44
+ unchanged in both.
45
+
46
+ ## What is in here
47
+
48
+ **Data contracts** — the shapes that cross every boundary:
49
+
50
+ - `bar` / `signals` / `result` / `trade_plan` — OHLCV bars, signal proxies,
51
+ prediction results, trade plans
52
+ - `backtest.result` / `backtest.serialize` — the backtest result carrier and
53
+ its canonical JSON wire shape
54
+ - `panel` — the cross-sectional factor-panel shape and its OHLCV converters
55
+ - `config_schema` — the typed schema plugins use to declare their
56
+ configuration surface
57
+ - `model_meta` — the `meta.json` schema describing trained-model artifacts
58
+
59
+ **Plugin contracts** — subclass one of these, register it via an entry
60
+ point, and the platform discovers it:
61
+
62
+ - `strategy` / `portfolio_strategy` — bar-driven and rebalance-driven
63
+ strategies
64
+ - `predictor` — per-bar inference
65
+ - `datasource` / `streaming` — bounded (batch) and unbounded (live) data
66
+ - `broker` — order routing
67
+ - `engine` — the backtest-engine protocol and factory
68
+ - `metric` / `validation` — result metrics and trustworthiness validators
69
+ - `risk` — risk manager, position sizer, stop policy
70
+ - `training` — the trainer contract and the walk-forward window partitioner
71
+ - `universe` / `calendar` — point-in-time stock pools and trading calendars
72
+
73
+ **Mechanism**:
74
+
75
+ - `registry` / `plugins` — the domain registry and the entry-point scanner
76
+ that performs discovery
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ pip install -e .
82
+ ```
83
+
84
+ Requires Python ≥ 3.11. Runtime dependencies are `pandas` and `loguru` only.
85
+
86
+ ## A minimal strategy
87
+
88
+ ```python
89
+ from apexquant.core.bar import Bar
90
+ from apexquant.core.strategy import BaseStrategy, Signal
91
+
92
+ class Momentum(BaseStrategy):
93
+ name = "momentum"
94
+ freq = "daily"
95
+
96
+ def on_bar(self, bar: Bar) -> Signal:
97
+ if bar.close > bar.open:
98
+ return Signal.BUY
99
+ return Signal.HOLD
100
+ ```
101
+
102
+ Third-party packages expose implementations through entry-point groups
103
+ (`apexquant.strategies`, `apexquant.datasources`, `apexquant.metrics`, …);
104
+ see `plugins.py` for the group inventory and `registry.py` for the domain
105
+ registry the scanner populates.
106
+
107
+ ## Stability
108
+
109
+ Interface versioning follows the platform's roadmap: during v0.x no
110
+ interface is frozen, and breaking changes are recorded in `CHANGELOG.md`.
111
+ Contracts carry an `__apexquant_api_version__` tuple where versioned.
112
+
113
+ ## Relationship to the main repository
114
+
115
+ The Arithmion product repository is the source of truth; this repository is
116
+ a curated export of its `src/apexquant/core` interface tier
117
+ (`scripts/sync_from_apexquant.py` performs the sync and enforces the scope).
118
+ Issues and contributions against the contracts are welcome here.
119
+
120
+ ## Why `apexquant` (and `aq`)?
121
+
122
+ Arithmion grew out of **ApexQuant**, a graduate thesis project on applying
123
+ deep time-series learning to stock forecasting and quantitative trading:
124
+
125
+ - [Applying Deep Time-Series Learning to Stock Forecasting and Quant
126
+ Trading](https://github.com/Heeeeeeliang/Applying-Deep-Time-Series-Learning-to-Stock-Forecasting-and-Quant-Trading)
127
+ — the thesis
128
+ - [Apexquant](https://github.com/Heeeeeeliang/Apexquant) — the thesis-era
129
+ codebase this platform descends from
130
+
131
+ When the project became the Arithmion platform, the brand changed but the
132
+ Python package kept its original `apexquant` name — frankly, because
133
+ renaming it was not worth the churn: imports, entry-point groups
134
+ (`apexquant.strategies`, …), and the `__apexquant_api_version__` markers
135
+ would all have had to move for zero functional gain. A convenient side
136
+ effect is that thesis-era and current code interoperate as-is. The same
137
+ applies to the `aq` shorthand you may see elsewhere in the platform (CSS
138
+ tokens, UI prefixes). Wherever you read `apexquant` or `aq`, it means
139
+ Arithmion.
140
+
141
+ ## License
142
+
143
+ MIT — see [LICENSE](LICENSE).
144
+
145
+ ---
146
+
147
+ ## 中文说明
148
+
149
+ arithmion-core 是 Arithmion(AI 原生量化平台)的接口层:平台赖以构建的
150
+ 契约——抽象基类、类型化数据载体、以及基于 entry-point 的插件注册机制。
151
+ 本仓库不包含引擎实现、数据/券商 provider 或 agent 运行时;它们属于实现了
152
+ 这些契约的 Arithmion 产品本体。
153
+
154
+ **相关链接**——产品在 [arithmion.com](https://arithmion.com);学习中心在
155
+ [arithmion.com/learn](https://arithmion.com/learn/):免费的中英双语量化课程,
156
+ 覆盖市场结构、数理统计、机器学习,以及围绕多重检验、PBO 与 Deflated Sharpe
157
+ 的验证专线,markdown 版在 [quant-learning-path](https://github.com/Arithmion-git/quant-learning-path)。
158
+
159
+ 包安装在 `apexquant` 命名空间下(`import apexquant.core`),与完整产品共享
160
+ 导入路径:针对这些契约编写的代码在两侧无需修改即可运行。
161
+
162
+ 安装:`pip install -e .`,要求 Python ≥ 3.11,运行时依赖仅 pandas 与 loguru。
163
+
164
+ **关于 `apexquant` 与 `aq` 命名**:Arithmion 的前身是毕业设计项目
165
+ **ApexQuant**([论文](https://github.com/Heeeeeeliang/Applying-Deep-Time-Series-Learning-to-Stock-Forecasting-and-Quant-Trading)、
166
+ [当时的代码库](https://github.com/Heeeeeeliang/Apexquant))。品牌更名后
167
+ Python 包沿用了原名 `apexquant`——说白了是改名不值得折腾:导入路径、
168
+ entry-point 组名、`__apexquant_api_version__` 标记全都得跟着动,却没有
169
+ 任何功能收益。顺带的好处是论文时期与当前的代码无需迁移即可互通;平台
170
+ 其它位置的 `aq` 简写同理。代码中的 `apexquant` 与 `aq` 均指 Arithmion。
171
+
172
+ v0.x 期间接口均不冻结,破坏性变更记录于 `CHANGELOG.md`。许可证为 MIT。
@@ -0,0 +1,148 @@
1
+ # arithmion-core
2
+
3
+ The interface tier of [Arithmion](https://arithmion.com), an AI-native
4
+ quantitative research and trading platform. This package contains the
5
+ contracts the platform is built on: abstract base classes, typed data
6
+ carriers, and the entry-point plugin registry. It contains no engine, no
7
+ providers, and no agent runtime — those live in the Arithmion product, which
8
+ implements these contracts.
9
+
10
+ **Links** — [arithmion.com](https://arithmion.com) for the product;
11
+ [arithmion.com/learn](https://arithmion.com/learn/) for the learning center, a
12
+ free bilingual (English / 中文) quant curriculum covering market structure,
13
+ statistics, machine learning, and a validation track on multiple testing, PBO
14
+ and the deflated Sharpe ratio — also available as markdown in
15
+ [quant-learning-path](https://github.com/Arithmion-git/quant-learning-path).
16
+
17
+ The package installs into the `apexquant` namespace (`import apexquant.core`),
18
+ which is the platform's historical package name; the import path is shared
19
+ with the full product so that code written against these contracts runs
20
+ unchanged in both.
21
+
22
+ ## What is in here
23
+
24
+ **Data contracts** — the shapes that cross every boundary:
25
+
26
+ - `bar` / `signals` / `result` / `trade_plan` — OHLCV bars, signal proxies,
27
+ prediction results, trade plans
28
+ - `backtest.result` / `backtest.serialize` — the backtest result carrier and
29
+ its canonical JSON wire shape
30
+ - `panel` — the cross-sectional factor-panel shape and its OHLCV converters
31
+ - `config_schema` — the typed schema plugins use to declare their
32
+ configuration surface
33
+ - `model_meta` — the `meta.json` schema describing trained-model artifacts
34
+
35
+ **Plugin contracts** — subclass one of these, register it via an entry
36
+ point, and the platform discovers it:
37
+
38
+ - `strategy` / `portfolio_strategy` — bar-driven and rebalance-driven
39
+ strategies
40
+ - `predictor` — per-bar inference
41
+ - `datasource` / `streaming` — bounded (batch) and unbounded (live) data
42
+ - `broker` — order routing
43
+ - `engine` — the backtest-engine protocol and factory
44
+ - `metric` / `validation` — result metrics and trustworthiness validators
45
+ - `risk` — risk manager, position sizer, stop policy
46
+ - `training` — the trainer contract and the walk-forward window partitioner
47
+ - `universe` / `calendar` — point-in-time stock pools and trading calendars
48
+
49
+ **Mechanism**:
50
+
51
+ - `registry` / `plugins` — the domain registry and the entry-point scanner
52
+ that performs discovery
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install -e .
58
+ ```
59
+
60
+ Requires Python ≥ 3.11. Runtime dependencies are `pandas` and `loguru` only.
61
+
62
+ ## A minimal strategy
63
+
64
+ ```python
65
+ from apexquant.core.bar import Bar
66
+ from apexquant.core.strategy import BaseStrategy, Signal
67
+
68
+ class Momentum(BaseStrategy):
69
+ name = "momentum"
70
+ freq = "daily"
71
+
72
+ def on_bar(self, bar: Bar) -> Signal:
73
+ if bar.close > bar.open:
74
+ return Signal.BUY
75
+ return Signal.HOLD
76
+ ```
77
+
78
+ Third-party packages expose implementations through entry-point groups
79
+ (`apexquant.strategies`, `apexquant.datasources`, `apexquant.metrics`, …);
80
+ see `plugins.py` for the group inventory and `registry.py` for the domain
81
+ registry the scanner populates.
82
+
83
+ ## Stability
84
+
85
+ Interface versioning follows the platform's roadmap: during v0.x no
86
+ interface is frozen, and breaking changes are recorded in `CHANGELOG.md`.
87
+ Contracts carry an `__apexquant_api_version__` tuple where versioned.
88
+
89
+ ## Relationship to the main repository
90
+
91
+ The Arithmion product repository is the source of truth; this repository is
92
+ a curated export of its `src/apexquant/core` interface tier
93
+ (`scripts/sync_from_apexquant.py` performs the sync and enforces the scope).
94
+ Issues and contributions against the contracts are welcome here.
95
+
96
+ ## Why `apexquant` (and `aq`)?
97
+
98
+ Arithmion grew out of **ApexQuant**, a graduate thesis project on applying
99
+ deep time-series learning to stock forecasting and quantitative trading:
100
+
101
+ - [Applying Deep Time-Series Learning to Stock Forecasting and Quant
102
+ Trading](https://github.com/Heeeeeeliang/Applying-Deep-Time-Series-Learning-to-Stock-Forecasting-and-Quant-Trading)
103
+ — the thesis
104
+ - [Apexquant](https://github.com/Heeeeeeliang/Apexquant) — the thesis-era
105
+ codebase this platform descends from
106
+
107
+ When the project became the Arithmion platform, the brand changed but the
108
+ Python package kept its original `apexquant` name — frankly, because
109
+ renaming it was not worth the churn: imports, entry-point groups
110
+ (`apexquant.strategies`, …), and the `__apexquant_api_version__` markers
111
+ would all have had to move for zero functional gain. A convenient side
112
+ effect is that thesis-era and current code interoperate as-is. The same
113
+ applies to the `aq` shorthand you may see elsewhere in the platform (CSS
114
+ tokens, UI prefixes). Wherever you read `apexquant` or `aq`, it means
115
+ Arithmion.
116
+
117
+ ## License
118
+
119
+ MIT — see [LICENSE](LICENSE).
120
+
121
+ ---
122
+
123
+ ## 中文说明
124
+
125
+ arithmion-core 是 Arithmion(AI 原生量化平台)的接口层:平台赖以构建的
126
+ 契约——抽象基类、类型化数据载体、以及基于 entry-point 的插件注册机制。
127
+ 本仓库不包含引擎实现、数据/券商 provider 或 agent 运行时;它们属于实现了
128
+ 这些契约的 Arithmion 产品本体。
129
+
130
+ **相关链接**——产品在 [arithmion.com](https://arithmion.com);学习中心在
131
+ [arithmion.com/learn](https://arithmion.com/learn/):免费的中英双语量化课程,
132
+ 覆盖市场结构、数理统计、机器学习,以及围绕多重检验、PBO 与 Deflated Sharpe
133
+ 的验证专线,markdown 版在 [quant-learning-path](https://github.com/Arithmion-git/quant-learning-path)。
134
+
135
+ 包安装在 `apexquant` 命名空间下(`import apexquant.core`),与完整产品共享
136
+ 导入路径:针对这些契约编写的代码在两侧无需修改即可运行。
137
+
138
+ 安装:`pip install -e .`,要求 Python ≥ 3.11,运行时依赖仅 pandas 与 loguru。
139
+
140
+ **关于 `apexquant` 与 `aq` 命名**:Arithmion 的前身是毕业设计项目
141
+ **ApexQuant**([论文](https://github.com/Heeeeeeliang/Applying-Deep-Time-Series-Learning-to-Stock-Forecasting-and-Quant-Trading)、
142
+ [当时的代码库](https://github.com/Heeeeeeliang/Apexquant))。品牌更名后
143
+ Python 包沿用了原名 `apexquant`——说白了是改名不值得折腾:导入路径、
144
+ entry-point 组名、`__apexquant_api_version__` 标记全都得跟着动,却没有
145
+ 任何功能收益。顺带的好处是论文时期与当前的代码无需迁移即可互通;平台
146
+ 其它位置的 `aq` 简写同理。代码中的 `apexquant` 与 `aq` 均指 Arithmion。
147
+
148
+ v0.x 期间接口均不冻结,破坏性变更记录于 `CHANGELOG.md`。许可证为 MIT。
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "arithmion-core"
7
+ version = "0.1.0"
8
+ description = "Interface tier of the Arithmion agent-native quant platform: contracts, typed data carriers, and the plugin registry."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [{ name = "Arithmion-git" }]
12
+ requires-python = ">=3.11"
13
+ dependencies = [
14
+ "pandas>=2.0",
15
+ "loguru>=0.7",
16
+ ]
17
+ keywords = ["quant", "trading", "backtesting", "framework", "plugins", "agent"]
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Intended Audience :: Financial and Insurance Industry",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Office/Business :: Financial :: Investment",
24
+ "Typing :: Typed",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://arithmion.com"
29
+ Repository = "https://github.com/Arithmion-git/arithmion-core"
30
+
31
+ [project.optional-dependencies]
32
+ test = ["pytest"]
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+ include = ["apexquant*"]
37
+ namespaces = true
38
+
39
+ [tool.setuptools.package-data]
40
+ "apexquant.core" = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """apexquant.core — interface contracts, registry, and plugin discovery.
2
+
3
+ The *interface tier*: minimal
4
+ dependencies, no provider-specific code. Concrete provider implementations
5
+ live under ``apexquant.bundled`` (Phase 1.6).
6
+ """
@@ -0,0 +1,17 @@
1
+ """apexquant.core.backtest — backtest RESULT contracts (contracts-only export).
2
+
3
+ This repo ships the result carrier and its canonical JSON wire shape only.
4
+ The engine implementations (``BacktestEngine``, the portfolio engine) live in
5
+ the full Arithmion product, which satisfies these contracts; any engine that
6
+ produces a :class:`BacktestResult` interoperates with tooling built on them.
7
+
8
+ NOTE (sync): this ``__init__`` is OWNED BY THIS REPO — the main repo's
9
+ counterpart also re-exports the engine implementation. See
10
+ ``scripts/sync_from_apexquant.py`` OVERRIDES.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ __all__ = ["CASH_TICKER", "BacktestResult", "CashFlowEvent"]
16
+
17
+ from apexquant.core.backtest.result import CASH_TICKER, BacktestResult, CashFlowEvent
@@ -0,0 +1,192 @@
1
+ """Backtest result carrier (Phase E1 — relocated from ``backtest/engine.py``).
2
+
3
+ ``BacktestResult`` is the structurally-identical successor to the
4
+ ``BacktestResult`` defined in both legacy engines (``backtest/engine.py`` and
5
+ ``backtest/_legacy.py``). It carries trades, the equity curve, headline
6
+ metrics (populated by :func:`backtest.metrics.compute_metrics`), and run
7
+ metadata.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ __all__ = ["CASH_TICKER", "BacktestResult", "CashFlowEvent"]
13
+
14
+ from dataclasses import dataclass, field
15
+ from datetime import datetime
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ import pandas as pd
19
+
20
+ if TYPE_CHECKING:
21
+ from apexquant.core.strategy import Trade
22
+
23
+ #: Reserved pseudo-ticker for the uninvested cash sleeve in ``weights_curve``.
24
+ #: Cash is carried EXPLICITLY rather than derived as ``1 - sum(weights)`` so an
25
+ #: all-cash date survives serialization: a date whose weights dict is empty
26
+ #: would otherwise vanish from the long-table CSV entirely, and the consumer
27
+ #: could not tell "flat that day" from "no data that day". The ``$`` prefix
28
+ #: keeps it out of any real ticker namespace.
29
+ CASH_TICKER = "$CASH"
30
+
31
+
32
+ @dataclass
33
+ class CashFlowEvent:
34
+ """One external cash injection/withdrawal during a backtest.
35
+
36
+ Distinct from trading P&L -- this is money that entered or left the
37
+ account from OUTSIDE the strategy's own decisions (a deposit, a
38
+ withdrawal, a scheduled contribution). ``metrics.compute_metrics``
39
+ reads a list of these to compute TWR/MWR without ever needing to know
40
+ WHY the flow happened.
41
+
42
+ Attributes:
43
+ timestamp: When the cash flow occurred (the bar timestamp it was
44
+ applied on).
45
+ amount: Positive = contribution/deposit, negative = withdrawal.
46
+ """
47
+
48
+ timestamp: datetime
49
+ amount: float # positive = contribution/deposit, negative = withdrawal
50
+
51
+ def to_dict(self) -> dict[str, Any]:
52
+ """JSON-native projection (round-trips via :meth:`from_dict`).
53
+
54
+ Follows the same ISO-timestamp convention as :meth:`Trade.to_dict`.
55
+ """
56
+ return {
57
+ "timestamp": self.timestamp.isoformat() if self.timestamp else None,
58
+ "amount": self.amount,
59
+ }
60
+
61
+ @classmethod
62
+ def from_dict(cls, d: dict[str, Any]) -> CashFlowEvent:
63
+ """Reconstruct a :class:`CashFlowEvent` from :meth:`to_dict` output."""
64
+ return cls(
65
+ timestamp=datetime.fromisoformat(d["timestamp"]),
66
+ amount=d.get("amount", 0.0),
67
+ )
68
+
69
+
70
+ @dataclass
71
+ class BacktestResult:
72
+ """Container for backtest output.
73
+
74
+ Attributes:
75
+ trades: All closed trades.
76
+ equity_curve: Portfolio value over time.
77
+ metrics: Performance metrics (populated by
78
+ :func:`backtest.metrics.compute_metrics`).
79
+ strategy_name: Name of the strategy that was tested — comes from the
80
+ strategy's own ``Strategy.name`` class attribute, so it need NOT
81
+ match the id the caller submitted (e.g. a user-authored workspace
82
+ strategy can set any ``name`` it likes). Kept for display/grouping;
83
+ do not use it to look the run back up in the strategy workspace.
84
+ start_date: First bar timestamp.
85
+ end_date: Last bar timestamp.
86
+ config_snapshot: Frozen copy of the config used.
87
+ strategy_id: The strategy-workspace id (or built-in type name, e.g.
88
+ ``"ai"``/``"technical"``) the caller actually submitted, captured
89
+ *before* ``_resolve_strategy`` rewrites it to an on-disk path.
90
+ ``None`` when the run has no such association (e.g. produced by a
91
+ legacy run predating this field, or a CLI/script invocation that
92
+ never went through the workspace-aware submission routes). Unlike
93
+ ``strategy_name``, this is reliable for looking the run back up in
94
+ the strategy workspace.
95
+ weights_curve: Per-date portfolio composition as
96
+ ``[(timestamp, {ticker: weight})]``, weights being **signed**
97
+ fractions of total equity (short exposure is negative) that sum to
98
+ 1 including the :data:`CASH_TICKER` sleeve. **Experimental.**
99
+ Populated by portfolio mode (per rebalance mark) and by the
100
+ default event engine in mark-to-market mode (sampled once per
101
+ calendar day); left empty in stepped equity mode, which has no
102
+ marks to compose from. Every consumer must treat "empty" as a
103
+ normal case and degrade rather than assume composition data exists.
104
+ cash_flow_events: External cash injections/withdrawals applied during
105
+ the run, e.g. scheduled contributions or a one-off
106
+ deposit/withdrawal. Empty for every run that didn't configure
107
+ ``backtest.contributions`` -- additive field, default ``[]``, so
108
+ existing callers that don't know about it are unaffected.
109
+ """
110
+
111
+ trades: list[Trade] = field(default_factory=list)
112
+ equity_curve: pd.Series = field(default_factory=lambda: pd.Series(dtype=float))
113
+ metrics: dict[str, Any] = field(default_factory=dict)
114
+ strategy_name: str = ""
115
+ start_date: datetime | None = None
116
+ end_date: datetime | None = None
117
+ config_snapshot: dict[str, Any] = field(default_factory=dict)
118
+ strategy_id: str | None = None
119
+ weights_curve: list[tuple[Any, dict[str, float]]] = field(default_factory=list)
120
+ cash_flow_events: list[CashFlowEvent] = field(default_factory=list)
121
+
122
+ def summary(self) -> dict[str, Any]:
123
+ """Return a summary dict of key fields.
124
+
125
+ Returns:
126
+ Dict with strategy name, trade count, date range, and metrics.
127
+ """
128
+ return {
129
+ "strategy": self.strategy_name,
130
+ "trades": len(self.trades),
131
+ "start": self.start_date,
132
+ "end": self.end_date,
133
+ **self.metrics,
134
+ }
135
+
136
+ def to_dict(self) -> dict[str, Any]:
137
+ """JSON-native projection (round-trips via :meth:`from_dict`).
138
+
139
+ Trades use :meth:`Trade.to_dict`; the equity curve becomes a list of
140
+ ``[iso_timestamp, value]`` pairs; datetimes become ISO strings. Makes the
141
+ backtest-output contract serializable end to end (agent / API / SaaS).
142
+ """
143
+ eq: list[list[Any]] = []
144
+ for ts, val in self.equity_curve.items():
145
+ key = ts.isoformat() if hasattr(ts, "isoformat") else str(ts)
146
+ eq.append([key, float(val)])
147
+ wt: list[list[Any]] = []
148
+ for ts, weights in self.weights_curve:
149
+ key = ts.isoformat() if hasattr(ts, "isoformat") else str(ts)
150
+ wt.append([key, {t: float(w) for t, w in weights.items()}])
151
+ return {
152
+ "trades": [t.to_dict() for t in self.trades],
153
+ "equity_curve": eq,
154
+ "weights_curve": wt,
155
+ "metrics": self.metrics,
156
+ "strategy_name": self.strategy_name,
157
+ "start_date": self.start_date.isoformat() if self.start_date else None,
158
+ "end_date": self.end_date.isoformat() if self.end_date else None,
159
+ "config_snapshot": self.config_snapshot,
160
+ "strategy_id": self.strategy_id,
161
+ "cash_flow_events": [cfe.to_dict() for cfe in self.cash_flow_events],
162
+ }
163
+
164
+ @classmethod
165
+ def from_dict(cls, d: dict[str, Any]) -> BacktestResult:
166
+ """Reconstruct a :class:`BacktestResult` from :meth:`to_dict` output."""
167
+ from apexquant.core.strategy import Trade
168
+
169
+ pairs = d.get("equity_curve") or []
170
+ index = pd.DatetimeIndex([pd.Timestamp(p[0]) for p in pairs]) if pairs else None
171
+ values = [float(p[1]) for p in pairs]
172
+ equity = pd.Series(values, index=index, dtype=float)
173
+ weights_curve = [
174
+ (pd.Timestamp(p[0]), {str(t): float(w) for t, w in (p[1] or {}).items()})
175
+ for p in (d.get("weights_curve") or [])
176
+ ]
177
+ start = d.get("start_date")
178
+ end = d.get("end_date")
179
+ return cls(
180
+ trades=[Trade.from_dict(t) for t in d.get("trades", [])],
181
+ equity_curve=equity,
182
+ metrics=dict(d.get("metrics") or {}),
183
+ strategy_name=d.get("strategy_name", ""),
184
+ start_date=datetime.fromisoformat(start) if start else None,
185
+ end_date=datetime.fromisoformat(end) if end else None,
186
+ config_snapshot=dict(d.get("config_snapshot") or {}),
187
+ strategy_id=d.get("strategy_id"),
188
+ weights_curve=weights_curve,
189
+ cash_flow_events=[
190
+ CashFlowEvent.from_dict(cfe) for cfe in d.get("cash_flow_events") or []
191
+ ],
192
+ )