Signal Quest: Build an Honest Machine with an LLM¶
Dr. Mallarapu · SEAS-8414 · Research and paper-trading only
This capstone teaches the complete Signal Quest textbook as one reproducible investigation. You will define a settlement rule, freeze an evidence boundary, build causal features, compare models, calibrate probabilities, replay a conservative policy, and monitor the system. The data are synthetic. A good conclusion may be NO_TRADE.
Non-negotiable rule: an LLM may draft bounded code, but it never receives secrets, production data, execution authority, or permission to run its own output. Every candidate is reviewed and tested before use.
Learning map¶
| Textbook movement | Notebook evidence | LLM role |
|---|---|---|
| ML, labels, and clocks | Synthetic event ledger and label contract | Draft a pure feature function |
| Metrics and fair tests | Baseline, chronological split, calibration | Explain a failed test, not invent a result |
| Three model families | Tabular baseline, encoder scaffold, causal sequence scaffold | Draft isolated modules and tests |
| Replay and paper trading | Conservative cost-aware NO_TRADE policy |
Propose a test case only |
| Agentic monitoring | Read-only health checks and reversible containment | Summarize evidence for a human |
Completion bar: all tests pass, the manifest is complete, the LLM trace is saved, and every conclusion is labeled simulated, illustrative, or design target as appropriate.
# Optional student environment. Run once in a fresh notebook kernel if needed.
# %pip install --upgrade numpy pandas scikit-learn
# Optional model-family extensions: %pip install catboost torch
import ast
import hashlib
import json
import os
import textwrap
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from pathlib import Path
import numpy as np
import pandas as pd
SEED = 8414
rng = np.random.default_rng(SEED)
WORKDIR = Path("signal_quest_lab")
WORKDIR.mkdir(exist_ok=True)
print(f"Working directory: {WORKDIR.resolve()}")
print("Epistemic status: simulated teaching lab; no live market or order authority.")
Working directory: $TEXTBOOK_ROOT/btc-polymarket-ml/labs/signal_quest_lab Epistemic status: simulated teaching lab; no live market or order authority.
1. The contract comes before the model¶
At decision time t, a model may receive only events whose receive time is no later than t. The label resolves later. Here, UP means the synthetic settlement price at the end of a five-minute horizon is greater than or equal to the start price. This is a teaching contract, not a claim about a real venue or market.
@dataclass(frozen=True)
class DecisionContract:
horizon_minutes: int = 5
tie_rule: str = "UP"
receive_time_required: bool = True
research_only: bool = True
CONTRACT = DecisionContract()
print(json.dumps(asdict(CONTRACT), indent=2))
def label_up(start_price: float, end_price: float) -> int:
"""Settlement contract: equality resolves UP."""
return int(end_price >= start_price)
assert label_up(100.0, 100.0) == 1
assert label_up(100.0, 99.9) == 0
{
"horizon_minutes": 5,
"tie_rule": "UP",
"receive_time_required": true,
"research_only": true
}
2. Build an event ledger and stop time travel¶
The ledger deliberately contains some events that occurred before a cutoff but arrived too late to be used. This distinction is the central causal test: occurrence time alone is not eligibility.
def synthetic_events(n: int = 720) -> pd.DataFrame:
event_time = pd.date_range("2026-01-01", periods=n, freq="min", tz="UTC")
mid = 100_000 + np.cumsum(rng.normal(0, 14, n))
spread = np.maximum(0.5, rng.lognormal(mean=0.4, sigma=0.35, size=n))
imbalance = np.tanh(rng.normal(0, 0.9, n))
receive_delay_seconds = rng.integers(0, 4, n)
receive_delay_seconds[::71] += 180 # deliberately late records
return pd.DataFrame({
"event_time": event_time,
"receive_time": event_time + pd.to_timedelta(receive_delay_seconds, unit="s"),
"mid": mid,
"spread": spread,
"imbalance": imbalance,
"event_id": [f"evt-{i:04d}" for i in range(n)],
})
events = synthetic_events()
events.head()
| event_time | receive_time | mid | spread | imbalance | event_id | |
|---|---|---|---|---|---|---|
| 0 | 2026-01-01 00:00:00+00:00 | 2026-01-01 00:03:01+00:00 | 99983.577156 | 1.086732 | -0.531457 | evt-0000 |
| 1 | 2026-01-01 00:01:00+00:00 | 2026-01-01 00:01:00+00:00 | 99958.995144 | 0.978767 | -0.589098 | evt-0001 |
| 2 | 2026-01-01 00:02:00+00:00 | 2026-01-01 00:02:03+00:00 | 99965.623805 | 1.235367 | -0.837308 | evt-0002 |
| 3 | 2026-01-01 00:03:00+00:00 | 2026-01-01 00:03:03+00:00 | 99976.513167 | 2.715420 | -0.673456 | evt-0003 |
| 4 | 2026-01-01 00:04:00+00:00 | 2026-01-01 00:04:02+00:00 | 99977.172296 | 0.827841 | 0.606708 | evt-0004 |
def eligible_events(events: pd.DataFrame, cutoff: pd.Timestamp) -> pd.DataFrame:
required = {"event_time", "receive_time", "mid", "spread", "imbalance", "event_id"}
missing = required.difference(events.columns)
if missing:
raise ValueError(f"Missing required evidence fields: {sorted(missing)}")
return events.loc[events["receive_time"] <= cutoff].copy()
cutoff = events.loc[72, "event_time"] # evt-0071 occurred but is received later.
eligible = eligible_events(events, cutoff)
assert (eligible["receive_time"] <= cutoff).all()
assert len(eligible) < len(events.loc[events["event_time"] <= cutoff])
print(f"Cutoff: {cutoff}; eligible rows: {len(eligible)}")
Cutoff: 2026-01-01 01:12:00+00:00; eligible rows: 71
3. Ask the LLM for a bounded code draft¶
The LLM receives a small, explicit contract. It must return only one pure Python function and tests. The notebook stores its response, hashes it, parses it, and refuses to execute it automatically. Set SIGNAL_QUEST_USE_LLM=1 and OLLAMA_API_KEY only when you are ready to make an API request. Otherwise, the cell emits a reviewable placeholder.
The prompt is outcome-first: required inputs, allowed output, safety boundary, and completion test are explicit.
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "kimi-k3:cloud")
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "https://ollama.com").rstrip("/")
USE_LLM = os.getenv("SIGNAL_QUEST_USE_LLM", "0") == "1"
FEATURE_PROMPT = """
Mission: draft one pure Python function named build_features(rows).
Input contract:
- rows is a pandas DataFrame with mid, spread, imbalance, receive_time, and event_time.
- rows contains only events eligible at a decision cutoff.
Output contract:
- Return a dict with float keys: last_mid, mean_spread, mean_imbalance, and return_3.
- Raise ValueError for fewer than four rows or missing fields.
Hard rules:
- Use no imports, file access, network calls, randomness, globals, eval, exec, classes, or model fitting.
- Do not mention market performance or trading.
- Return code only, followed by exactly three assert-style tests in comments.
Completion bar: a reviewer can read the function, parse it, and test it deterministically.
""".strip()
def request_llm_code(prompt: str) -> str:
"""Request an untrusted teaching-code draft from Ollama."""
if not USE_LLM:
return "# LLM disabled. Set SIGNAL_QUEST_USE_LLM=1 after reviewing the prompt.\n"
api_key = os.getenv("OLLAMA_API_KEY")
if not api_key:
raise RuntimeError("LLM mode requested but OLLAMA_API_KEY is not set.")
import urllib.error
import urllib.request
payload = json.dumps({
"model": OLLAMA_MODEL,
"messages": [{"role": "user", "content": prompt}],
"stream": False,
"options": {"temperature": 0},
}).encode("utf-8")
request = urllib.request.Request(
f"{OLLAMA_HOST}/api/chat",
data=payload,
headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=120) as response:
result = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
raise RuntimeError(f"Ollama request failed with HTTP {exc.code}.") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"Ollama is unreachable at {OLLAMA_HOST}.") from exc
content = result.get("message", {}).get("content")
if not isinstance(content, str) or not content.strip():
raise RuntimeError("Ollama returned no message content.")
return content
llm_draft = request_llm_code(FEATURE_PROMPT)
trace = {
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"provider": "ollama",
"host": OLLAMA_HOST,
"model": OLLAMA_MODEL if USE_LLM else "disabled",
"prompt": FEATURE_PROMPT,
"response": llm_draft,
"response_sha256": hashlib.sha256(llm_draft.encode()).hexdigest(),
"status": "review_required",
}
(WORKDIR / "llm_feature_draft.json").write_text(json.dumps(trace, indent=2))
print(trace["status"], trace["response_sha256"])
review_required a31c43d91a6436fa799ea9d740e80d7ac9841fcf86376d36620b0194e7aa771c
# The LLM's text is data. It is not executed. Validate its AST before human review.
FORBIDDEN = (ast.Import, ast.ImportFrom, ast.Global, ast.Nonlocal, ast.ClassDef, ast.AsyncFunctionDef)
FORBIDDEN_CALLS = {"eval", "exec", "open", "compile", "__import__", "input"}
def static_code_review(candidate: str) -> list[str]:
findings = []
try:
tree = ast.parse(candidate)
except SyntaxError as exc:
return [f"syntax error: {exc}"]
functions = [n for n in tree.body if isinstance(n, ast.FunctionDef)]
if len(functions) != 1 or functions[0].name != "build_features":
findings.append("require exactly one function named build_features")
for node in ast.walk(tree):
if isinstance(node, FORBIDDEN):
findings.append(f"forbidden syntax: {type(node).__name__}")
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id in FORBIDDEN_CALLS:
findings.append(f"forbidden call: {node.func.id}")
return sorted(set(findings))
review_findings = static_code_review(llm_draft) if USE_LLM else ["LLM disabled: no candidate submitted"]
print(review_findings)
print("Human gate: copy an approved candidate into the next cell; do not exec model output.")
['LLM disabled: no candidate submitted'] Human gate: copy an approved candidate into the next cell; do not exec model output.
# Student-controlled implementation after review. This is the only feature code used below.
def build_features(rows: pd.DataFrame) -> dict[str, float]:
required = {"mid", "spread", "imbalance"}
if missing := required.difference(rows.columns):
raise ValueError(f"missing fields: {sorted(missing)}")
if len(rows) < 4:
raise ValueError("need at least four rows")
mid = rows["mid"].astype(float)
return {
"last_mid": float(mid.iloc[-1]),
"mean_spread": float(rows["spread"].mean()),
"mean_imbalance": float(rows["imbalance"].mean()),
"return_3": float(mid.iloc[-1] / mid.iloc[-4] - 1.0),
}
assert set(build_features(eligible.tail(10))) == {"last_mid", "mean_spread", "mean_imbalance", "return_3"}
try:
build_features(eligible.head(3))
raise AssertionError("short-window test did not fail")
except ValueError:
pass
3A. On-demand Code Studio¶
This is the notebook's LLM workbench for the entire book. Choose one topic, write a narrow learning request, and set SIGNAL_QUEST_USE_LLM=1 only after you have read the generated prompt. The LLM may draft a small teaching artifact; it cannot access files, networks, credentials, live markets, or a trading account. Its draft is saved with a trace and is never executed automatically.
LESSON_CATALOG = {
"contract": {
"chapter": "Observations, decision time, later settlement",
"function": "define_decision_contract",
"goal": "Create a dataclass that rejects a label horizon, tie rule, or evidence boundary that is missing.",
},
"features": {
"chapter": "Causal features and the evidence boundary",
"function": "build_causal_features",
"goal": "Create pure rolling features from rows already eligible at a decision cutoff.",
},
"leakage": {
"chapter": "Time travel and leakage tests",
"function": "assert_no_future_events",
"goal": "Create a test that fails when an event received after the cutoff is present.",
},
"catboost": {
"chapter": "Tabular probability model",
"function": "train_tabular_candidate",
"goal": "Draft a chronological CatBoost training function that returns validation probabilities only.",
},
"encoder": {
"chapter": "Self-supervised limit-order-book encoder",
"function": "masked_window_loss",
"goal": "Draft a small tensor-only masked reconstruction loss with shape checks.",
},
"transformer": {
"chapter": "Causal limit-order-book Transformer",
"function": "causal_attention_mask",
"goal": "Create a strictly upper-triangular attention mask for an ordered sequence.",
},
"calibration": {
"chapter": "Probabilities, calibration, and abstention",
"function": "calibration_summary",
"goal": "Calculate reliability-bin summaries without choosing an action.",
},
"replay": {
"chapter": "Conservative replay and costs",
"function": "paper_replay_row",
"goal": "Score one paper-trade record with supplied price, conservative cost, and later outcome.",
},
"guardian": {
"chapter": "Agentic monitoring and reversible containment",
"function": "guardian_check",
"goal": "Return an evidence-only incident record; never modify a model, policy, or account.",
},
}
print("Available topics:", ", ".join(LESSON_CATALOG))
Available topics: contract, features, leakage, catboost, encoder, transformer, calibration, replay, guardian
ON_DEMAND_TOPIC = "leakage" # Change to any key in LESSON_CATALOG.
ON_DEMAND_REQUEST = "Explain the idea with one small, deterministic function and three assert-style tests."
def on_demand_prompt(topic: str, learner_request: str) -> str:
if topic not in LESSON_CATALOG:
raise ValueError(f"Unknown topic {topic!r}. Choose one of: {sorted(LESSON_CATALOG)}")
lesson = LESSON_CATALOG[topic]
return f"""
You are the Signal Quest teaching-code assistant. Produce one small, reviewable Python teaching artifact.
Book chapter: {lesson['chapter']}
Required function name: {lesson['function']}
Teaching goal: {lesson['goal']}
Student request: {learner_request}
Hard safety and evidence rules:
- Return code only, followed by exactly three assert-style tests in comments.
- Define exactly one function. Use only parameters passed to that function.
- No imports, files, network, environment variables, randomness, subprocesses, eval, exec, classes, model persistence, live data, or order placement.
- Do not claim predictive performance, profitability, or deployment readiness.
- For model topics, return probabilities or tensors only; do not make a trading decision.
- For guardian topics, return observations and a human-review requirement only.
The output will be statically reviewed by a human before any manual copy into an approved cell.
""".strip()
ON_DEMAND_PROMPT = on_demand_prompt(ON_DEMAND_TOPIC, ON_DEMAND_REQUEST)
on_demand_draft = request_llm_code(ON_DEMAND_PROMPT)
on_demand_trace = {
"topic": ON_DEMAND_TOPIC,
"chapter": LESSON_CATALOG[ON_DEMAND_TOPIC]["chapter"],
"provider": "ollama",
"model": OLLAMA_MODEL if USE_LLM else "disabled",
"prompt": ON_DEMAND_PROMPT,
"response": on_demand_draft,
"response_sha256": hashlib.sha256(on_demand_draft.encode()).hexdigest(),
"status": "review_required",
}
(WORKDIR / "llm_on_demand_code_trace.json").write_text(json.dumps(on_demand_trace, indent=2))
print(on_demand_trace["status"], on_demand_trace["topic"], on_demand_trace["response_sha256"])
review_required leakage a31c43d91a6436fa799ea9d740e80d7ac9841fcf86376d36620b0194e7aa771c
ON_DEMAND_FORBIDDEN = {
"eval", "exec", "open", "compile", "__import__", "input", "requests", "urlopen",
"subprocess", "system", "popen", "fit", "predict", "save", "load",
}
def review_on_demand_draft(candidate: str, required_function: str) -> list[str]:
try:
tree = ast.parse(candidate)
except SyntaxError as exc:
return [f"syntax error: {exc}"]
findings = []
functions = [node for node in tree.body if isinstance(node, ast.FunctionDef)]
if len(functions) != 1 or functions[0].name != required_function:
findings.append(f"require exactly one function named {required_function}")
for node in ast.walk(tree):
if isinstance(node, FORBIDDEN):
findings.append(f"forbidden syntax: {type(node).__name__}")
if isinstance(node, ast.Attribute) and node.attr.startswith("__"):
findings.append("forbidden dunder attribute")
if isinstance(node, ast.Call):
name = node.func.id if isinstance(node.func, ast.Name) else getattr(node.func, "attr", "")
if name in ON_DEMAND_FORBIDDEN:
findings.append(f"forbidden call: {name}")
return sorted(set(findings))
if USE_LLM:
on_demand_findings = review_on_demand_draft(
on_demand_draft, LESSON_CATALOG[ON_DEMAND_TOPIC]["function"]
)
else:
on_demand_findings = ["LLM disabled: set SIGNAL_QUEST_USE_LLM=1 and supply OLLAMA_API_KEY."]
print(on_demand_findings)
print("Human gate: inspect the trace, review the draft, then manually copy an approved function below.")
['LLM disabled: set SIGNAL_QUEST_USE_LLM=1 and supply OLLAMA_API_KEY.'] Human gate: inspect the trace, review the draft, then manually copy an approved function below.
Human approval gate¶
The LLM draft is not program behavior. Read the saved trace, check the static findings, test the artifact in isolation, and then manually copy only an approved version into a new cell. If the draft fails the evidence boundary or safety contract, reject it and improve the prompt—do not weaken the guardrail.
4. Freeze features, then define a later label¶
Each row below is a decision record. Features are built from an eligible history at the cutoff. The label is resolved five minutes later. The notebook discards unresolved horizons instead of guessing.
def make_dataset(events: pd.DataFrame, lookback: int = 20, horizon: int = 5) -> pd.DataFrame:
rows = []
for i in range(lookback, len(events) - horizon):
cutoff = events.loc[i, "event_time"]
history = eligible_events(events.iloc[: i + 1], cutoff).tail(lookback)
if len(history) < lookback:
continue
features = build_features(history)
start_mid = float(events.loc[i, "mid"])
end_mid = float(events.loc[i + horizon, "mid"])
rows.append({**features, "cutoff": cutoff, "label": label_up(start_mid, end_mid)})
return pd.DataFrame(rows)
dataset = make_dataset(events)
assert dataset["cutoff"].is_monotonic_increasing
assert dataset["label"].isin([0, 1]).all()
dataset.head()
| last_mid | mean_spread | mean_imbalance | return_3 | cutoff | label | |
|---|---|---|---|---|---|---|
| 0 | 99960.588725 | 1.535299 | 0.049990 | 0.000086 | 2026-01-01 00:20:00+00:00 | 0 |
| 1 | 99976.596972 | 1.615599 | 0.083696 | 0.000199 | 2026-01-01 00:21:00+00:00 | 1 |
| 2 | 99965.585331 | 1.677618 | 0.080641 | 0.000144 | 2026-01-01 00:22:00+00:00 | 0 |
| 3 | 99978.688297 | 1.675101 | 0.120764 | 0.000181 | 2026-01-01 00:23:00+00:00 | 1 |
| 4 | 99954.378232 | 1.583342 | 0.110286 | -0.000222 | 2026-01-01 00:24:00+00:00 | 1 |
5. Evaluate fairly: chronological split, simple baseline, and calibration¶
Random splitting would let nearby observations leak across train and test. We use time order. The baseline is the training prevalence; a score only earns attention if it beats that reference under the same evidence contract.
def chronological_split(frame: pd.DataFrame, train_fraction: float = 0.60, valid_fraction: float = 0.20):
n = len(frame)
a, b = int(n * train_fraction), int(n * (train_fraction + valid_fraction))
return frame.iloc[:a].copy(), frame.iloc[a:b].copy(), frame.iloc[b:].copy()
train, valid, test = chronological_split(dataset)
base_probability = float(train["label"].mean())
def brier(y: pd.Series, p: np.ndarray) -> float:
return float(np.mean((np.asarray(y) - np.asarray(p)) ** 2))
baseline_brier = brier(test["label"], np.full(len(test), base_probability))
print({"train": len(train), "valid": len(valid), "test": len(test), "base_rate": base_probability, "baseline_brier": baseline_brier})
{'train': 417, 'valid': 139, 'test': 139, 'base_rate': 0.4460431654676259, 'baseline_brier': 0.2571813053154599}
# A small transparent research baseline, not a production model.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix
FEATURES = ["mean_spread", "mean_imbalance", "return_3"]
baseline_model = LogisticRegression(random_state=SEED, class_weight="balanced")
baseline_model.fit(train[FEATURES], train["label"])
valid_probability = baseline_model.predict_proba(valid[FEATURES])[:, 1]
test_probability = baseline_model.predict_proba(test[FEATURES])[:, 1]
print("Validation Brier:", brier(valid["label"], valid_probability))
print("Test Brier:", brier(test["label"], test_probability))
print("Test accuracy:", accuracy_score(test["label"], test_probability >= 0.5))
print("Confusion matrix:\n", confusion_matrix(test["label"], test_probability >= 0.5))
print("Interpretation: simulated result only. Accuracy alone is not a claim of usefulness.")
Validation Brier: 0.25171696377318237 Test Brier: 0.2710190132809431 Test accuracy: 0.39568345323741005 Confusion matrix: [[44 20] [64 11]] Interpretation: simulated result only. Accuracy alone is not a claim of usefulness.
def calibration_table(y: pd.Series, p: np.ndarray, bins: int = 5) -> pd.DataFrame:
frame = pd.DataFrame({"y": np.asarray(y), "p": np.asarray(p)})
frame["bin"] = pd.cut(frame["p"], bins=np.linspace(0, 1, bins + 1), include_lowest=True)
return frame.groupby("bin", observed=False).agg(count=("y", "size"), predicted=("p", "mean"), observed=("y", "mean")).reset_index()
calibration_table(valid["label"], valid_probability)
| bin | count | predicted | observed | |
|---|---|---|---|---|
| 0 | (-0.001, 0.2] | 0 | NaN | NaN |
| 1 | (0.2, 0.4] | 0 | NaN | NaN |
| 2 | (0.4, 0.6] | 139 | 0.490995 | 0.388489 |
| 3 | (0.6, 0.8] | 0 | NaN | NaN |
| 4 | (0.8, 1.0] | 0 | NaN | NaN |
6. The three-model ladder¶
The first rung is a tabular candidate. The second learns a representation from permitted unlabeled sequence windows. The third applies a causal mask to ordered limit-order-book states. None proves an edge by existing. Each must survive the same temporal, calibration, replay, and safety gates.
Use the LLM to draft one module at a time from the prompts below. Review the output exactly as you reviewed build_features; do not paste generated code directly into a training run.
MODEL_PROMPTS = {
"catboost": "Draft a single Python function train_tabular_candidate(X_train, y_train, X_valid, y_valid). Use CatBoostClassifier only; no file, network, or plotting access. Return model and validation probabilities. Include an early-stopping parameter and do not claim performance.",
"encoder": "Draft a PyTorch nn.Module named MaskedLOBEncoder. It accepts [batch, time, features], masks only supplied positions, and returns embeddings. Do not create data loaders, files, or training loops. Include shape assertions in comments.",
"causal_transformer": "Draft a PyTorch nn.Module named CausalLOBTransformer. It accepts ordered [batch, time, features] data and applies a strictly upper-triangular causal attention mask. Return one probability logit per sequence. No file, network, or execution code.",
}
for name, prompt in MODEL_PROMPTS.items():
print(f"\n{name.upper()} PROMPT\n{textwrap.fill(prompt, width=100)}")
CATBOOST PROMPT Draft a single Python function train_tabular_candidate(X_train, y_train, X_valid, y_valid). Use CatBoostClassifier only; no file, network, or plotting access. Return model and validation probabilities. Include an early-stopping parameter and do not claim performance. ENCODER PROMPT Draft a PyTorch nn.Module named MaskedLOBEncoder. It accepts [batch, time, features], masks only supplied positions, and returns embeddings. Do not create data loaders, files, or training loops. Include shape assertions in comments. CAUSAL_TRANSFORMER PROMPT Draft a PyTorch nn.Module named CausalLOBTransformer. It accepts ordered [batch, time, features] data and applies a strictly upper-triangular causal attention mask. Return one probability logit per sequence. No file, network, or execution code.
# Design-target acceptance tests for every advanced candidate.
# These tests are intentionally stated before implementation.
ADVANCED_MODEL_GATES = [
"chronological train/validation/test split is recorded",
"normalization fit uses training period only",
"sequence window contains no record received after its cutoff",
"causal mask blocks every future position",
"probabilities are calibrated on validation data only",
"comparison includes baseline and conservative replay",
"artifact manifest records code, config, data, and model hashes",
]
for gate in ADVANCED_MODEL_GATES:
print("[ ]", gate)
[ ] chronological train/validation/test split is recorded [ ] normalization fit uses training period only [ ] sequence window contains no record received after its cutoff [ ] causal mask blocks every future position [ ] probabilities are calibrated on validation data only [ ] comparison includes baseline and conservative replay [ ] artifact manifest records code, config, data, and model hashes
7. Probability is not permission¶
A model probability becomes a possible action only after conservative cost, calibration, data-health, and risk gates. The policy below is deliberately fail-closed: missing evidence, a bad calibration result, or an unsafe cost assumption produces NO_TRADE. It does not place orders.
@dataclass(frozen=True)
class PolicyInput:
probability_up: float
executable_up_price: float
conservative_cost: float
calibration_ok: bool
data_health_ok: bool
risk_ok: bool
def decide_paper_only(x: PolicyInput, buffer: float = 0.03) -> str:
if not all([x.calibration_ok, x.data_health_ok, x.risk_ok]):
return "NO_TRADE"
conservative_probability = x.probability_up - buffer
return "PAPER_UP" if conservative_probability > x.executable_up_price + x.conservative_cost else "NO_TRADE"
assert decide_paper_only(PolicyInput(.80, .60, .02, True, True, True)) == "PAPER_UP"
assert decide_paper_only(PolicyInput(.80, .60, .02, False, True, True)) == "NO_TRADE"
8. Replay the policy under a reconstructed clock¶
This minimal replay uses synthetic executable prices and conservative costs. It does not model a live book and it does not predict a real outcome. Its purpose is to make assumptions inspectable and to demonstrate that a score may still lead to abstention.
def paper_replay(frame: pd.DataFrame, probabilities: np.ndarray, cost: float = 0.02) -> pd.DataFrame:
ledger = []
for (_, row), probability in zip(frame.iterrows(), probabilities):
synthetic_price = float(np.clip(0.50 + 4 * row["return_3"], 0.05, 0.95))
decision = decide_paper_only(PolicyInput(float(probability), synthetic_price, cost, True, True, True))
ledger.append({
"cutoff": row["cutoff"],
"probability_up": float(probability),
"synthetic_executable_price": synthetic_price,
"cost": cost,
"decision": decision,
"later_label": int(row["label"]),
"epistemic_status": "simulated",
})
return pd.DataFrame(ledger)
ledger = paper_replay(test, test_probability)
assert set(ledger["decision"]) <= {"PAPER_UP", "NO_TRADE"}
ledger["decision"].value_counts(dropna=False)
decision NO_TRADE 139 Name: count, dtype: int64
9. Agentic monitoring: observe, contain, escalate¶
The guardian may summarize evidence, quarantine a bad artifact, and request human review. It may not alter a model, policy, dataset, account, or external system. The LLM may draft a human-readable incident summary only from the provided facts.
def guardian_check(frame: pd.DataFrame, calibration_ok: bool, manifest_ok: bool) -> dict:
issues = []
if not frame["cutoff"].is_monotonic_increasing:
issues.append("non_monotonic_cutoff")
if not calibration_ok:
issues.append("calibration_gate_failed")
if not manifest_ok:
issues.append("artifact_manifest_missing")
return {
"status": "HEALTHY" if not issues else "QUARANTINE_AND_ESCALATE",
"issues": issues,
"allowed_actions": ["write_incident_record", "quarantine_research_artifact", "request_human_review"],
"forbidden_actions": ["place_order", "change_model", "change_policy", "delete_evidence"],
}
guardian = guardian_check(ledger, calibration_ok=True, manifest_ok=True)
print(json.dumps(guardian, indent=2))
{
"status": "HEALTHY",
"issues": [],
"allowed_actions": [
"write_incident_record",
"quarantine_research_artifact",
"request_human_review"
],
"forbidden_actions": [
"place_order",
"change_model",
"change_policy",
"delete_evidence"
]
}
INCIDENT_PROMPT = """
Summarize this research incident for a human reviewer. Use only supplied facts.
State: observed evidence, allowed reversible containment, required human decision, and what cannot be inferred.
Do not recommend trading, policy changes, or automatic remediation.
Facts: {facts}
""".strip()
incident_trace = {
"prompt": INCIDENT_PROMPT.format(facts=json.dumps(guardian)),
"allowed_output": "human-readable incident summary",
"requires_human_review": True,
"epistemic_status": "design target",
}
(WORKDIR / "guardian_llm_prompt.json").write_text(json.dumps(incident_trace, indent=2))
print("Saved bounded incident-summary prompt; no remediation is invoked.")
Saved bounded incident-summary prompt; no remediation is invoked.
10. Reproducibility and doctoral defense¶
A result without a data boundary, code trace, configuration, and limitations is not a result that another student can audit. The final manifest records this lab's synthetic inputs and prompts. It is not a benchmark report.
def sha256_path(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
manifest = {
"title": "Signal Quest LLM-guided capstone",
"epistemic_status": "simulated teaching lab",
"decision_contract": asdict(CONTRACT),
"seed": SEED,
"rows": {"events": len(events), "dataset": len(dataset), "test": len(test)},
"feature_draft": "llm_feature_draft.json",
"feature_draft_sha256": sha256_path(WORKDIR / "llm_feature_draft.json"),
"policy": "paper-only, fail-closed",
"guardian": guardian,
"limitations": [
"synthetic data",
"no executable venue data",
"no live performance claim",
"no order authority",
"advanced-model cells are design-target scaffolds until independently implemented and tested",
],
}
(WORKDIR / "experiment_manifest.json").write_text(json.dumps(manifest, indent=2))
print(json.dumps(manifest, indent=2))
{
"title": "Signal Quest LLM-guided capstone",
"epistemic_status": "simulated teaching lab",
"decision_contract": {
"horizon_minutes": 5,
"tie_rule": "UP",
"receive_time_required": true,
"research_only": true
},
"seed": 8414,
"rows": {
"events": 720,
"dataset": 695,
"test": 139
},
"feature_draft": "llm_feature_draft.json",
"feature_draft_sha256": "b74624f36136a6c2a34ab5df574afcaceae1e4f09471393cf1bab87181b2bcf3",
"policy": "paper-only, fail-closed",
"guardian": {
"status": "HEALTHY",
"issues": [],
"allowed_actions": [
"write_incident_record",
"quarantine_research_artifact",
"request_human_review"
],
"forbidden_actions": [
"place_order",
"change_model",
"change_policy",
"delete_evidence"
]
},
"limitations": [
"synthetic data",
"no executable venue data",
"no live performance claim",
"no order authority",
"advanced-model cells are design-target scaffolds until independently implemented and tested"
]
}
Capstone defense prompts¶
- Show one event that occurred before the cutoff but arrived too late. Why is it forbidden?
- Read the LLM prompt and trace. What exact code behavior was requested, and what behavior was prohibited?
- Why is the student-controlled feature function the only function used in the experiment?
- Compare the baseline Brier score to the candidate score. What does the comparison fail to prove?
- Which advanced model gate would catch a future-looking sequence implementation?
- Find a case where the policy abstains despite a high model probability.
- What can the guardian do automatically, and what is it structurally unable to do?
- State the strongest defensible conclusion from this notebook without using the words “profitable,” “edge,” or “prediction success.”