feat: preserve Quant OS target-package vertical slice
This commit is contained in:
+198
-16
@@ -7,14 +7,33 @@ import ast
|
||||
import hashlib
|
||||
import json
|
||||
import pprint
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
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:
|
||||
@@ -56,6 +75,73 @@ def inline_core(wrapper_source: str, core_source: str) -> str:
|
||||
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",
|
||||
@@ -71,20 +157,52 @@ def _qmt_symbol(symbol: str) -> str:
|
||||
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 = [str(symbol) for symbol in baseline["symbols"]]
|
||||
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] = {
|
||||
"universe_mode": str(universe_config["mode"]),
|
||||
"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(
|
||||
@@ -145,15 +263,19 @@ def bundle_one(
|
||||
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_config(
|
||||
inline_core(wrapper_source, core_source),
|
||||
effective_config,
|
||||
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:
|
||||
@@ -194,6 +316,7 @@ def bundle_one(
|
||||
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 = (
|
||||
@@ -209,43 +332,90 @@ def bundle_all(
|
||||
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"),
|
||||
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"),
|
||||
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 committed manifest must be portable and must not expose the
|
||||
# local account/home directory. Hashes remain byte-for-byte identities.
|
||||
logical_outputs = {
|
||||
# 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"
|
||||
)
|
||||
# The manifest describes the committed logical artifact, not the
|
||||
# caller's temporary build directory. This keeps clean-room builds
|
||||
# byte-for-byte comparable with committed ``dist``.
|
||||
entry["output"] = logical_outputs[platform_name]
|
||||
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"
|
||||
@@ -266,8 +436,20 @@ def _main(argv: Optional[list[str]] = None) -> int:
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user