"""Inline one portable core into JoinQuant and QMT single-file artifacts.""" from __future__ import annotations import argparse import ast import hashlib import json import pprint import sys from pathlib import Path from typing import Any, Dict, List, Optional START = "# __PORTABLE_CORE_BUNDLE_START__" END = "# __PORTABLE_CORE_BUNDLE_END__" CONFIG_START = "# __BASELINE_CONFIG_START__" CONFIG_END = "# __BASELINE_CONFIG_END__" TARGET_START = "# __TARGET_PACKAGE_TAPE_START__" TARGET_END = "# __TARGET_PACKAGE_TAPE_END__" MOMENTUM_SMOKE_MODE = "portable_momentum_smoke" TARGET_PACKAGE_MODE = "target_package" JOINQUANT_EVIDENCE_PREFIX = "QUANT60_JOINQUANT_EVIDENCE_V1 " JOINQUANT_EVIDENCE_CHUNK_CHARACTERS = 1800 JOINQUANT_TARGET_PRICE_SOURCE = "CURRENT_DATA_DAY_OPEN" JOINQUANT_EVIDENCE_EVENTS = [ "INIT", "TARGET_PACKAGE_NOOP", "TARGET_PACKAGE_HIT", "PLATFORM_BINDING_INPUT", "TARGET_PACKAGE_PLAN", "SKIPPED_ORDER", "ORDER_REQUEST", "ORDER_RETURN", "EOD_STATUS", ] def sha256_bytes(value: bytes) -> str: return hashlib.sha256(value).hexdigest() def _portable_body(source: str) -> str: """Remove the core module docstring and future imports before inlining.""" tree = ast.parse(source) lines = source.splitlines() first_line = 0 if ( tree.body and isinstance(tree.body[0], ast.Expr) and isinstance(tree.body[0].value, ast.Constant) and isinstance(tree.body[0].value.value, str) ): first_line = int(tree.body[0].end_lineno or 0) body = lines[first_line:] body = [ line for line in body if not line.lstrip().startswith("from __future__ import ") ] return "\n".join(body).strip() + "\n" def inline_core(wrapper_source: str, core_source: str) -> str: if wrapper_source.count(START) != 1 or wrapper_source.count(END) != 1: raise ValueError("wrapper must contain exactly one portable-core marker pair") start = wrapper_source.index(START) end = wrapper_source.index(END, start) + len(END) replacement = ( START + "\n# Inlined by tools/bundle_platforms.py; do not edit this region.\n" + _portable_body(core_source) + END ) return wrapper_source[:start] + replacement + wrapper_source[end:] def _canonical_target_tape_bytes( target_packages: List[Dict[str, Any]], ) -> bytes: return json.dumps( target_packages, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False, ).encode("utf-8") def inline_target_packages( wrapper_source: str, target_packages: List[Dict[str, Any]], ) -> str: """Inline a pre-validated, quantity-free decision tape.""" if ( wrapper_source.count(TARGET_START) != 1 or wrapper_source.count(TARGET_END) != 1 ): raise ValueError( "wrapper must contain exactly one target-package marker pair" ) start = wrapper_source.index(TARGET_START) end = wrapper_source.index(TARGET_END, start) + len(TARGET_END) body = pprint.pformat( target_packages, width=88, sort_dicts=False, ) body = body.encode("ascii", "backslashreplace").decode("ascii") replacement = ( TARGET_START + "\n# Generated by tools/bundle_platforms.py; do not edit this region.\n" + "TARGET_PACKAGES = " + body + "\n" + TARGET_END ) return wrapper_source[:start] + replacement + wrapper_source[end:] def load_verified_target_packages( project_root: Path, path: Path, ) -> List[Dict[str, Any]]: """Load one package or a multi-decision tape through the canonical verifier.""" root = project_root.expanduser().resolve() source = path.expanduser().resolve() src_path = str(root / "src") inserted = src_path not in sys.path if inserted: sys.path.insert(0, src_path) try: from quant60.target_package import ( # pylint: disable=import-outside-toplevel load_target_package_tape, ) return list(load_target_package_tape(source)) finally: if inserted: sys.path.remove(src_path) def _qmt_symbol(symbol: str) -> str: suffixes = { ".XSHG": ".SH", ".XSHE": ".SZ", ".XBSE": ".BJ", } for suffix, replacement in suffixes.items(): if symbol.endswith(suffix): return symbol[: -len(suffix)] + replacement raise ValueError(f"unsupported canonical symbol in baseline config: {symbol}") def effective_platform_config( baseline: Dict[str, Any], platform: str, *, decision_mode: str = MOMENTUM_SMOKE_MODE, target_packages: Optional[List[Dict[str, Any]]] = None, ) -> Dict[str, Any]: packages = list(target_packages or []) if decision_mode not in {MOMENTUM_SMOKE_MODE, TARGET_PACKAGE_MODE}: raise ValueError(f"unsupported decision_mode: {decision_mode}") if decision_mode == TARGET_PACKAGE_MODE and not packages: raise ValueError("target_package mode requires a non-empty package tape") if decision_mode == MOMENTUM_SMOKE_MODE and packages: raise ValueError("momentum smoke mode cannot embed target packages") strategy = baseline["strategy"] execution = baseline["execution"] fees = baseline["fees"] universe_config = baseline["universe"] symbols = ( sorted( { str(symbol) for package in packages for symbol in package["universe"] } ) if packages else [str(symbol) for symbol in baseline["symbols"]] ) if platform == "qmt_builtin": universe = [_qmt_symbol(symbol) for symbol in symbols] elif platform == "joinquant": universe = symbols else: raise ValueError(f"unsupported platform config: {platform}") tape_sha256 = ( sha256_bytes(_canonical_target_tape_bytes(packages)) if packages else None ) common: Dict[str, Any] = { "decision_mode": decision_mode, "target_package_count": len(packages), "target_package_tape_sha256": tape_sha256, "universe_mode": ( "fixed" if decision_mode == TARGET_PACKAGE_MODE else str(universe_config["mode"]) ), "index_symbol": str(universe_config["index_symbol"]), "qmt_sector_name": str(universe_config["qmt_sector_name"]), "dedicated_account_required": bool( universe_config["dedicated_account_required"] ), "exclude_st": bool(universe_config["exclude_st"]), "universe": universe, "lookback": int(strategy["lookback"]), "skip": int(strategy["skip"]), "top_n": int(strategy["top_n"]), "max_weight": float(strategy["max_weight"]), "gross_target": float(strategy["gross_target"]), "cash_buffer": float(strategy["cash_buffer"]), "lot_size": int(execution["lot_size"]), "participation_rate": float(execution["participation_rate"]), "slippage_bps": float(execution["slippage_bps"]), "commission_rate": float(fees["commission_rate"]), "minimum_commission": float(fees["minimum_commission"]), "stamp_duty_rate": float(fees["stamp_duty_rate"]), "transfer_fee_rate": float(fees["transfer_fee_rate"]), "rebalance_schedule": str(strategy["rebalance_schedule"]), } if platform == "qmt_builtin": return { "account_id": "test", **common, } return common def inline_config(wrapper_source: str, config: Dict[str, Any]) -> str: if ( wrapper_source.count(CONFIG_START) != 1 or wrapper_source.count(CONFIG_END) != 1 ): raise ValueError("wrapper must contain exactly one baseline-config marker pair") start = wrapper_source.index(CONFIG_START) end = wrapper_source.index(CONFIG_END, start) + len(CONFIG_END) body = pprint.pformat(config, width=88, sort_dicts=False) # QMT's built-in strategy is shipped with a GBK coding declaration but # kept byte-for-byte ASCII for portability. Preserve non-ASCII config # values (for example a Chinese sector name) as Python unicode escapes. body = body.encode("ascii", "backslashreplace").decode("ascii") replacement = ( CONFIG_START + "\n# Generated from configs/baseline.json; do not edit this region.\n" + "CONFIG = " + body + "\n" + CONFIG_END ) return wrapper_source[:start] + replacement + wrapper_source[end:] def bundle_one( *, core_path: Path, wrapper_path: Path, output_path: Path, effective_config: Dict[str, Any], target_packages: Optional[List[Dict[str, Any]]] = None, require_ascii: bool = False, ) -> Dict[str, str]: core_bytes = core_path.read_bytes() wrapper_bytes = wrapper_path.read_bytes() core_source = core_bytes.decode("utf-8") wrapper_source = wrapper_bytes.decode("utf-8") bundled = inline_target_packages( inline_config( inline_core(wrapper_source, core_source), effective_config, ), list(target_packages or []), ) output_bytes = bundled.encode("utf-8") if require_ascii: try: bundled.encode("ascii") except UnicodeEncodeError as exc: raise ValueError( "QMT #coding:gbk bundle must remain ASCII-only" ) from exc # The built-in QMT runtime is documented as Python 3.6. Parse against # that grammar explicitly; compiling only on the build host would miss # accidental modern syntax. ast.parse(bundled, filename=str(output_path), feature_version=(3, 6)) compile(bundled, str(output_path), "exec") output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_bytes(output_bytes) return { "core": str(core_path), "core_sha256": sha256_bytes(core_bytes), "wrapper": str(wrapper_path), "wrapper_sha256": sha256_bytes(wrapper_bytes), "effective_config_sha256": sha256_bytes( ( json.dumps( effective_config, ensure_ascii=True, separators=(",", ":"), sort_keys=True, ) + "\n" ).encode("ascii") ), "output": str(output_path), "output_sha256": sha256_bytes(output_bytes), } def bundle_all( project_root: Path, output_dir: Optional[Path] = None, target_package_path: Optional[Path] = None, ) -> Dict[str, object]: root = project_root.expanduser().resolve() output = ( output_dir.expanduser().resolve() if output_dir else root / "dist" ) core = root / "src" / "quant60" / "portable_core.py" if not core.is_file(): raise FileNotFoundError(core) config_path = root / "configs" / "baseline.json" baseline_bytes = config_path.read_bytes() baseline = json.loads(baseline_bytes.decode("utf-8")) if not isinstance(baseline, dict): raise ValueError("configs/baseline.json must contain an object") target_packages = ( load_verified_target_packages(root, target_package_path) if target_package_path is not None else [] ) decision_mode = ( TARGET_PACKAGE_MODE if target_packages else MOMENTUM_SMOKE_MODE ) entries = { "joinquant": bundle_one( core_path=core, wrapper_path=root / "platforms" / "joinquant_strategy.py", output_path=output / "joinquant_strategy.py", effective_config=effective_platform_config( baseline, "joinquant", decision_mode=decision_mode, target_packages=target_packages, ), target_packages=target_packages, ), "qmt_builtin": bundle_one( core_path=core, wrapper_path=root / "platforms" / "qmt_builtin_strategy.py", output_path=output / "qmt_builtin_strategy.py", effective_config=effective_platform_config( baseline, "qmt_builtin", decision_mode=decision_mode, target_packages=target_packages, ), target_packages=target_packages, require_ascii=True, ), } # Paths in a manifest must be portable and must not expose the local # account/home directory. Preserve a real project-relative location for # outputs under the project (for example dist/target-package/...). A # clean-room output outside the project keeps the committed logical names # so its manifest remains byte-for-byte comparable with committed dist. default_logical_outputs = { "joinquant": "dist/joinquant_strategy.py", "qmt_builtin": "dist/qmt_builtin_strategy.py", } for platform_name, entry in entries.items(): physical_output = Path(entry["output"]) entry["core"] = "src/quant60/portable_core.py" entry["wrapper"] = ( "platforms/joinquant_strategy.py" if "joinquant" in entry["wrapper"] else "platforms/qmt_builtin_strategy.py" ) try: entry["output"] = physical_output.relative_to(root).as_posix() except ValueError: entry["output"] = default_logical_outputs[platform_name] manifest = { "schema_version": 1, "baseline_config": "configs/baseline.json", "baseline_config_sha256": sha256_bytes(baseline_bytes), "portable_core_sha256": entries["joinquant"]["core_sha256"], "decision_mode": decision_mode, "target_package_count": len(target_packages), "target_package_tape_sha256": ( sha256_bytes(_canonical_target_tape_bytes(target_packages)) if target_packages else None ), "target_package_ids": [ package["package_id"] for package in target_packages ], "target_package_sha256": [ package["package_sha256"] for package in target_packages ], "joinquant_observation": { "eod_chunk_characters": JOINQUANT_EVIDENCE_CHUNK_CHARACTERS, "eod_chunk_encoding": "base64-canonical-json", "eod_api_policy": "best_effort_get_orders_get_trades", "events": JOINQUANT_EVIDENCE_EVENTS, "fail_soft": True, "prefix": JOINQUANT_EVIDENCE_PREFIX, "schema_version": "1.0", "target_price_source": JOINQUANT_TARGET_PRICE_SOURCE, }, "artifacts": entries, } manifest_path = output / "bundle_manifest.json" encoded = ( json.dumps(manifest, ensure_ascii=True, indent=2, sort_keys=True) + "\n" ).encode("ascii") manifest_path.write_bytes(encoded) try: manifest["manifest"] = str(manifest_path.relative_to(root)) except ValueError: manifest["manifest"] = manifest_path.name manifest["manifest_sha256"] = sha256_bytes(encoded) return manifest def _main(argv: Optional[list[str]] = None) -> int: default_root = Path(__file__).resolve().parents[1] parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--project-root", type=Path, default=default_root) parser.add_argument("--output-dir", type=Path) parser.add_argument( "--target-package", type=Path, help=( "verified TargetPackageV1 JSON/JSONL tape; switches both hosted " "artifacts from momentum smoke to target-package execution" ), ) args = parser.parse_args(argv) manifest = bundle_all( args.project_root, args.output_dir, args.target_package, ) print(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True)) return 0 if __name__ == "__main__": raise SystemExit(_main())