@tangle-network/agent-bench 0.3.7 → 0.4.0
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.
- package/CHANGELOG.md +11 -0
- package/HARNESS.md +43 -0
- package/README.md +7 -0
- package/dist/adapters.js +23 -23
- package/dist/benchmarks/_harness.d.ts +1 -1
- package/dist/benchmarks/_harness.js +1 -1
- package/dist/benchmarks/aec-bench.js +2 -2
- package/dist/benchmarks/agentbench.js +2 -2
- package/dist/benchmarks/appworld.js +2 -2
- package/dist/benchmarks/bfcl.js +2 -2
- package/dist/benchmarks/commit0.js +2 -2
- package/dist/benchmarks/crag.js +2 -2
- package/dist/benchmarks/dabstep.js +2 -2
- package/dist/benchmarks/enterpriseops-gym.js +2 -2
- package/dist/benchmarks/finresearchbench.js +2 -2
- package/dist/benchmarks/nomiracl.js +2 -2
- package/dist/benchmarks/open-rag-bench.js +2 -2
- package/dist/benchmarks/programbench.js +2 -2
- package/dist/benchmarks/ragbench.js +2 -2
- package/dist/benchmarks/swe-bench.js +2 -2
- package/dist/benchmarks/t2-ragbench.js +2 -2
- package/dist/benchmarks/tau-bench-shared.js +2 -2
- package/dist/benchmarks/tau2-bench.js +3 -3
- package/dist/benchmarks/tau3-banking.js +3 -3
- package/dist/benchmarks/terminal-bench.js +2 -2
- package/dist/benchmarks/toollm.js +2 -2
- package/dist/benchmarks/webarena-verified.js +2 -2
- package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
- package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
- package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
- package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
- package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
- package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
- package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
- package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
- package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
- package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
- package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
- package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
- package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
- package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
- package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
- package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
- package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
- package/dist/{chunk-PWQVGAJB.js → chunk-V7AEBY6U.js} +21 -21
- package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
- package/dist/chunk-WSKWVEQB.js.map +1 -0
- package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
- package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
- package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
- package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
- package/dist/index.js +23 -23
- package/package.json +5 -5
- package/scripts/trata-hedge/README.md +6 -5
- package/scripts/verify-packed-consumer.mjs +1 -1
- package/src/benchmarks/_harness.ts +20 -2
- package/src/benchmarks/humaneval.test.mts +2 -2
- package/src/gate.ts +1 -1
- package/src/hev-eval.mts +5 -2
- package/src/hev-improve.mts +118 -73
- package/src/official-optimizer-config.mts +89 -0
- package/src/official-optimizer-config.test.mts +88 -0
- package/src/profiles.ts +2 -2
- package/src/quant-arena/README.md +144 -0
- package/src/quant-arena/backtest.test.mts +135 -0
- package/src/quant-arena/backtest.ts +218 -0
- package/src/quant-arena/data.test.mts +44 -0
- package/src/quant-arena/data.ts +141 -0
- package/src/quant-arena/driver.test.mts +253 -0
- package/src/quant-arena/driver.ts +219 -0
- package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
- package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
- package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
- package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
- package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
- package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
- package/src/quant-arena/holdout-certify.mts +206 -0
- package/src/quant-arena/holdout-certify.test.mts +82 -0
- package/src/quant-arena/leak-audit.test.mts +79 -0
- package/src/quant-arena/leak-audit.ts +95 -0
- package/src/quant-arena/make-fixtures.mts +161 -0
- package/src/quant-arena/multiplicity.test.mts +68 -0
- package/src/quant-arena/multiplicity.ts +87 -0
- package/src/quant-arena/nautilus-certify.ts +31 -0
- package/src/quant-arena/oms.ts +90 -0
- package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
- package/src/quant-arena/python/pyproject.toml +8 -0
- package/src/quant-arena/python/uv.lock +1297 -0
- package/src/quant-arena/python/vbt-worker.py +192 -0
- package/src/quant-arena/quant-loop.mts +813 -0
- package/src/quant-arena/quant-loop.test.mts +75 -0
- package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
- package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
- package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
- package/src/quant-arena/types.ts +133 -0
- package/src/quant-arena/vbt-client.ts +321 -0
- package/src/quant-arena/vbt-parity.test.mts +183 -0
- package/src/quant-arena/windows.test.mts +45 -0
- package/src/quant-arena/windows.ts +54 -0
- package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
- package/src/rollout-ledger/backfill-swe-arena.test.mts +342 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/smoke-structural-rollout.mts +15 -9
- package/src/swe-arena/activation.mts +225 -0
- package/src/swe-arena/activation.test.mts +300 -0
- package/src/swe-arena/arms.ts +30 -14
- package/src/swe-arena/briefing.mts +217 -0
- package/src/swe-arena/briefing.test.mts +178 -0
- package/src/swe-arena/calibrate.ts +102 -1
- package/src/swe-arena/cell-evidence.mts +41 -9
- package/src/swe-arena/factory-command-container.ts +284 -0
- package/src/swe-arena/factory-judge-child.mts +228 -0
- package/src/swe-arena/factory.test.mts +643 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
- package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
- package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
- package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
- package/src/swe-arena/fixtures/run-report/README.md +43 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
- package/src/swe-arena/fixtures.ts +135 -2
- package/src/swe-arena/gepa-seat.mts +877 -0
- package/src/swe-arena/gepa-seat.test.mts +1059 -0
- package/src/swe-arena/implementation-ref.test.mts +64 -0
- package/src/swe-arena/implementation-ref.ts +62 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/outer-loop.mts +710 -33
- package/src/swe-arena/premeasured-from-cells.mts +281 -0
- package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
- package/src/swe-arena/proposer-fanout.mts +327 -31
- package/src/swe-arena/proposer-fanout.test.mts +246 -0
- package/src/swe-arena/proposer-provenance.mts +172 -0
- package/src/swe-arena/proposer-provenance.test.mts +106 -0
- package/src/swe-arena/run-experiment.mts +367 -1
- package/src/swe-arena/run-report.mts +75 -0
- package/src/swe-arena/run-supervisor.test.mts +5 -3
- package/src/swe-arena/score-split.mts +140 -0
- package/src/swe-arena/score-split.test.mts +123 -0
- package/src/swe-arena/scratch-worktree.test.mts +55 -0
- package/src/swe-arena/scratch-worktree.ts +34 -0
- package/src/swe-arena/types.ts +52 -0
- package/src/swe-code-improve.mts +24 -25
- package/src/swe-improve.mts +129 -96
- package/src/swe-local-proof.mts +6 -1
- package/src/swe-stream.mts +4 -2
- package/src/swe-structural.mts +245 -837
- package/src/tb-container-executor.test.mts +30 -6
- package/src/tb-supervisor-sidecar.mts +2 -1
- package/src/trata-gepa.mts +182 -245
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- package/src/live-improve-campaign-mbpp.mts +0 -641
- package/src/live-improve-campaign.mts +0 -500
- /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
- /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
- /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
- /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
- /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
- /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
- /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
- /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
- /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
- /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
- /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
- /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
- /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
- /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
- /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
- /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
- /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
- /package/dist/{chunk-PWQVGAJB.js.map → chunk-V7AEBY6U.js.map} +0 -0
- /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
- /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
- /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
- /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""QUANT-ARENA vectorbt scoring worker.
|
|
2
|
+
|
|
3
|
+
Persistent process speaking JSON-lines over stdin/stdout. The TypeScript side
|
|
4
|
+
(vbt-client.ts) spawns ONE worker per campaign and streams score requests to
|
|
5
|
+
it; the worker replies with per-window stats plus the full-range equity curve.
|
|
6
|
+
|
|
7
|
+
Protocol (one JSON object per line):
|
|
8
|
+
request {"id": n, "op": "ping"}
|
|
9
|
+
reply {"id": n, "ok": true, "vbt": "<version>"}
|
|
10
|
+
|
|
11
|
+
request {"id": n, "op": "score",
|
|
12
|
+
"open_prices": [[T x N floats]], # fill prices (next-open model)
|
|
13
|
+
"close_prices": [[T x N floats]], # valuation prices (close-marked equity)
|
|
14
|
+
"weights": [[N floats] | null, ... T rows],
|
|
15
|
+
# decision at day t; null = no rebalance
|
|
16
|
+
# (JSON has no NaN — null plays that role)
|
|
17
|
+
"cost_bps": 10, "slippage_bps": 5,
|
|
18
|
+
"windows": [[start, end], ...]} # [start, end) day-index ranges
|
|
19
|
+
reply {"id": n, "ok": true,
|
|
20
|
+
"windows": [{"totalReturn", "maxDD", "sharpe", "trades"}, ...],
|
|
21
|
+
"full": {same shape, over [0, T)},
|
|
22
|
+
"equity": [T floats]} # group value, close-marked
|
|
23
|
+
errors {"id": n, "ok": false, "error": "<message>"}
|
|
24
|
+
|
|
25
|
+
Execution model (aligned with the TS reference engine, backtest.ts):
|
|
26
|
+
- a decision at the close of day t fills at day t+1's open: the worker
|
|
27
|
+
shifts the weights matrix down one row before handing it to vectorbt;
|
|
28
|
+
- Portfolio.from_orders(size_type='targetpercent', group_by=True,
|
|
29
|
+
cash_sharing=True, call_seq='auto'), fill price = open, valuation
|
|
30
|
+
(val_price for sizing) = open — matching the TS engine's "target dollars
|
|
31
|
+
are a fraction of equity marked at the fill's open";
|
|
32
|
+
- the TS engine charges slippage as a bps FEE on traded dollars, so the
|
|
33
|
+
worker folds slippage_bps into vectorbt's `fees` and passes slippage=0
|
|
34
|
+
(vectorbt's own slippage perturbs the fill price — a different model);
|
|
35
|
+
- stats replicate backtest.ts statsForRange exactly: sample-std (ddof=1)
|
|
36
|
+
annualized Sharpe at 252 days, positive-convention max drawdown with the
|
|
37
|
+
peak reset at each window start, trades counted on fill days inside
|
|
38
|
+
(start, end).
|
|
39
|
+
|
|
40
|
+
Determinism: NUMBA_NUM_THREADS=1 is pinned before import so JIT-compiled
|
|
41
|
+
reductions run single-threaded; a warmup call at startup absorbs the numba
|
|
42
|
+
cold-JIT cost before the first real request.
|
|
43
|
+
|
|
44
|
+
License note: vectorbt is Apache-2.0 + Commons Clause. This worker is for
|
|
45
|
+
internal research use (scoring candidate strategies in the lab); it is not
|
|
46
|
+
sold or offered as a commercial service.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from __future__ import annotations
|
|
50
|
+
|
|
51
|
+
import json
|
|
52
|
+
import math
|
|
53
|
+
import os
|
|
54
|
+
import sys
|
|
55
|
+
|
|
56
|
+
os.environ.setdefault("NUMBA_NUM_THREADS", "1")
|
|
57
|
+
|
|
58
|
+
import numpy as np # noqa: E402
|
|
59
|
+
import pandas as pd # noqa: E402
|
|
60
|
+
import vectorbt as vbt # noqa: E402
|
|
61
|
+
|
|
62
|
+
TRADING_DAYS_PER_YEAR = 252
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def range_stats(equity: np.ndarray, order_days: np.ndarray, start: int, end: int) -> dict:
|
|
66
|
+
"""Mirror of backtest.ts statsForRange over a value curve."""
|
|
67
|
+
if start < 0 or end > equity.shape[0] or end - start < 2:
|
|
68
|
+
raise ValueError(f"bad range [{start}, {end}) over {equity.shape[0]} days")
|
|
69
|
+
eq = equity[start:end]
|
|
70
|
+
total_return = float(eq[-1] / eq[0] - 1.0)
|
|
71
|
+
peak = -math.inf
|
|
72
|
+
max_dd = 0.0
|
|
73
|
+
for value in eq:
|
|
74
|
+
if value > peak:
|
|
75
|
+
peak = value
|
|
76
|
+
dd = (peak - value) / peak
|
|
77
|
+
if dd > max_dd:
|
|
78
|
+
max_dd = dd
|
|
79
|
+
returns = eq[1:] / eq[:-1] - 1.0
|
|
80
|
+
n = returns.shape[0]
|
|
81
|
+
mean = float(returns.mean())
|
|
82
|
+
if n > 1:
|
|
83
|
+
std = float(returns.std(ddof=1))
|
|
84
|
+
else:
|
|
85
|
+
std = 0.0
|
|
86
|
+
sharpe = (mean / std) * math.sqrt(TRADING_DAYS_PER_YEAR) if std > 0 else 0.0
|
|
87
|
+
trades = int(((order_days > start) & (order_days < end)).sum())
|
|
88
|
+
return {
|
|
89
|
+
"totalReturn": total_return,
|
|
90
|
+
"maxDD": float(max_dd),
|
|
91
|
+
"sharpe": float(sharpe),
|
|
92
|
+
"trades": trades,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_portfolio(req: dict) -> tuple[np.ndarray, np.ndarray]:
|
|
97
|
+
"""Returns (equity curve close-marked, order fill-day indices)."""
|
|
98
|
+
opens = np.asarray(req["open_prices"], dtype=np.float64)
|
|
99
|
+
closes = np.asarray(req["close_prices"], dtype=np.float64)
|
|
100
|
+
if opens.shape != closes.shape or opens.ndim != 2:
|
|
101
|
+
raise ValueError(f"price shape mismatch: open {opens.shape} vs close {closes.shape}")
|
|
102
|
+
T, N = opens.shape
|
|
103
|
+
weights_rows = req["weights"]
|
|
104
|
+
if len(weights_rows) != T:
|
|
105
|
+
raise ValueError(f"weights has {len(weights_rows)} rows for {T} days")
|
|
106
|
+
|
|
107
|
+
# Decision at close of t -> order row t+1, filled at t+1's open.
|
|
108
|
+
size = np.full((T, N), np.nan)
|
|
109
|
+
for t, row in enumerate(weights_rows):
|
|
110
|
+
if row is None:
|
|
111
|
+
continue
|
|
112
|
+
if t + 1 >= T:
|
|
113
|
+
continue # a decision on the final bar can never fill
|
|
114
|
+
if len(row) != N:
|
|
115
|
+
raise ValueError(f"decision at t={t} has {len(row)} weights for {N} assets")
|
|
116
|
+
size[t + 1] = row
|
|
117
|
+
|
|
118
|
+
fee_rate = (float(req["cost_bps"]) + float(req["slippage_bps"])) / 10_000.0
|
|
119
|
+
columns = [f"A{k}" for k in range(N)]
|
|
120
|
+
close_df = pd.DataFrame(closes, columns=columns)
|
|
121
|
+
open_df = pd.DataFrame(opens, columns=columns)
|
|
122
|
+
size_df = pd.DataFrame(size, columns=columns)
|
|
123
|
+
|
|
124
|
+
pf = vbt.Portfolio.from_orders(
|
|
125
|
+
close=close_df,
|
|
126
|
+
size=size_df,
|
|
127
|
+
price=open_df,
|
|
128
|
+
val_price=open_df,
|
|
129
|
+
size_type="targetpercent",
|
|
130
|
+
group_by=True,
|
|
131
|
+
cash_sharing=True,
|
|
132
|
+
call_seq="auto",
|
|
133
|
+
fees=fee_rate,
|
|
134
|
+
slippage=0.0,
|
|
135
|
+
init_cash=1.0,
|
|
136
|
+
freq="1D",
|
|
137
|
+
)
|
|
138
|
+
equity = np.asarray(pf.value().values, dtype=np.float64)
|
|
139
|
+
order_days = np.asarray(pf.orders.records["idx"], dtype=np.int64)
|
|
140
|
+
return equity, order_days
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def score(req: dict) -> dict:
|
|
144
|
+
equity, order_days = run_portfolio(req)
|
|
145
|
+
T = equity.shape[0]
|
|
146
|
+
windows = [range_stats(equity, order_days, int(w[0]), int(w[1])) for w in req["windows"]]
|
|
147
|
+
return {
|
|
148
|
+
"windows": windows,
|
|
149
|
+
"full": range_stats(equity, order_days, 0, T),
|
|
150
|
+
"equity": [float(v) for v in equity],
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def warmup() -> None:
|
|
155
|
+
req = {
|
|
156
|
+
"open_prices": [[100.0, 50.0]] * 8,
|
|
157
|
+
"close_prices": [[101.0, 49.5]] * 8,
|
|
158
|
+
"weights": [None, [0.5, 0.4], None, None, [0.2, 0.1], None, None, None],
|
|
159
|
+
"cost_bps": 10,
|
|
160
|
+
"slippage_bps": 5,
|
|
161
|
+
"windows": [[0, 8]],
|
|
162
|
+
}
|
|
163
|
+
score(req)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def main() -> None:
|
|
167
|
+
warmup()
|
|
168
|
+
sys.stdout.write(json.dumps({"ready": True, "vbt": vbt.__version__}) + "\n")
|
|
169
|
+
sys.stdout.flush()
|
|
170
|
+
for line in sys.stdin:
|
|
171
|
+
line = line.strip()
|
|
172
|
+
if not line:
|
|
173
|
+
continue
|
|
174
|
+
rid = None
|
|
175
|
+
try:
|
|
176
|
+
req = json.loads(line)
|
|
177
|
+
rid = req.get("id")
|
|
178
|
+
op = req.get("op")
|
|
179
|
+
if op == "ping":
|
|
180
|
+
reply = {"id": rid, "ok": True, "vbt": vbt.__version__}
|
|
181
|
+
elif op == "score":
|
|
182
|
+
reply = {"id": rid, "ok": True, **score(req)}
|
|
183
|
+
else:
|
|
184
|
+
reply = {"id": rid, "ok": False, "error": f"unknown op {op!r}"}
|
|
185
|
+
except Exception as exc: # fail per-request, keep serving
|
|
186
|
+
reply = {"id": rid, "ok": False, "error": f"{type(exc).__name__}: {exc}"}
|
|
187
|
+
sys.stdout.write(json.dumps(reply) + "\n")
|
|
188
|
+
sys.stdout.flush()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
main()
|