Files
qmt_bridge/reference/xtquant_big_convert/src/bigqmt_backtest/server.py
T

81 lines
3.0 KiB
Python

"""Command-line entry for the standalone ZMQ backtest bridge."""
import argparse
import json
import os
import sys
import uuid
from .data_feed import CsvBarFeed
from .engine import BacktestConfig, BacktestEngine
from .protocol import BacktestBridgeProtocol
from .zmq_server import ZmqBacktestServer
def _load_config(path):
if not path:
return {}
with open(os.path.abspath(path), encoding="utf-8") as handle:
payload = json.load(handle)
if not isinstance(payload, dict):
raise ValueError("config JSON must be an object")
return payload
def _parser():
parser = argparse.ArgumentParser(description="Standalone ZMQ backtest bridge")
parser.add_argument("--data", required=True, help="UTF-8 CSV historical bar file")
parser.add_argument("--config", default="", help="Optional UTF-8 JSON backtest config")
parser.add_argument("--run-id", default="", help="Unique run identity")
parser.add_argument("--output-dir", default="", help="Artifact directory")
parser.add_argument("--bind", default="tcp://127.0.0.1:16661", help="ZMQ REP bind endpoint")
parser.add_argument("--default-symbol", default="", help="Used when CSV has no symbol column")
parser.add_argument("--initial-cash", type=float, default=None)
parser.add_argument("--slippage-bps", type=float, default=None)
parser.add_argument("--max-volume-participation", type=float, default=None)
parser.add_argument("--keep-running", action="store_true", help="Do not stop server after finish")
return parser
def build_engine(args):
payload = _load_config(args.config)
run_id = str(args.run_id or payload.pop("run_id", "") or ("bt-" + uuid.uuid4().hex[:12]))
output_dir = args.output_dir or payload.pop("output_dir", "") or os.path.join("backtest_runs", run_id)
if args.initial_cash is not None:
payload["initial_cash"] = args.initial_cash
if args.slippage_bps is not None:
payload["slippage_bps"] = args.slippage_bps
if args.max_volume_participation is not None:
payload["max_volume_participation"] = args.max_volume_participation
config = BacktestConfig(run_id=run_id, output_dir=output_dir, **payload)
feed = CsvBarFeed(args.data, default_symbol=args.default_symbol)
return BacktestEngine(feed, config)
def main(argv=None):
args = _parser().parse_args(argv)
engine = build_engine(args)
server = ZmqBacktestServer(
BacktestBridgeProtocol(engine),
endpoint=args.bind,
exit_on_finish=not args.keep_running,
)
startup = {
"event": "backtest_bridge_starting",
"run_id": engine.config.run_id,
"bind": args.bind,
"data_hash": engine.feed.data_hash,
"output_dir": engine.config.output_dir,
"live_ready": False,
}
print(json.dumps(startup, ensure_ascii=False, sort_keys=True), flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
server.stop()
return 0
if __name__ == "__main__":
sys.exit(main())