Skip to content

Commit 745f938

Browse files
committed
scaffold long horizon signal pipeline
0 parents  commit 745f938

14 files changed

Lines changed: 564 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v6
13+
- uses: actions/setup-python@v6
14+
with:
15+
python-version: "3.11"
16+
- name: Install package
17+
run: python -m pip install -e '.[test]'
18+
- name: Run tests
19+
run: python -m pytest -q
20+
- name: Validate example signal
21+
run: python scripts/validate_latest_signal.py examples/latest_signal.example.json
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
name: Long Horizon Shadow Signal
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
provider:
7+
description: "Bridge provider"
8+
required: false
9+
type: choice
10+
default: "auto"
11+
options:
12+
- auto
13+
- api
14+
- anthropic
15+
- codex
16+
- openai
17+
source_ref:
18+
description: "Ref to pass to CodexAuditBridge"
19+
required: false
20+
default: "main"
21+
22+
permissions:
23+
contents: read
24+
issues: write
25+
26+
jobs:
27+
dispatch-shadow-signal:
28+
runs-on: ubuntu-latest
29+
env:
30+
BRIDGE_REPOSITORY: ${{ vars.SELFHOSTED_CODEX_REVIEW_REPOSITORY || 'QuantStrategyLab/CodexAuditBridge' }}
31+
BRIDGE_TASK: long_horizon_signal_shadow
32+
BRIDGE_PROVIDER: ${{ inputs.provider || 'auto' }}
33+
SOURCE_REF: ${{ inputs.source_ref || 'main' }}
34+
steps:
35+
- uses: actions/checkout@v6
36+
37+
- uses: actions/setup-python@v6
38+
with:
39+
python-version: "3.11"
40+
41+
- name: Validate existing latest signal if present
42+
run: python scripts/validate_latest_signal.py --allow-missing
43+
44+
- name: Detect GitHub App Credentials
45+
id: app_credentials
46+
env:
47+
APP_ID: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
48+
APP_PRIVATE_KEY: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
49+
run: |
50+
set -euo pipefail
51+
if [ -n "${APP_ID:-}" ] && [ -n "${APP_PRIVATE_KEY:-}" ]; then
52+
echo "available=true" >> "$GITHUB_OUTPUT"
53+
else
54+
echo "available=false" >> "$GITHUB_OUTPUT"
55+
fi
56+
57+
- name: Create GitHub App Token For Bridge Repository
58+
id: bridge_app_token
59+
if: steps.app_credentials.outputs.available == 'true'
60+
continue-on-error: true
61+
uses: actions/create-github-app-token@v3
62+
with:
63+
app-id: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
64+
private-key: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
65+
owner: ${{ github.repository_owner }}
66+
repositories: |
67+
CodexAuditBridge
68+
permission-actions: write
69+
70+
- name: Create shadow signal issue and dispatch bridge
71+
env:
72+
GH_TOKEN: ${{ github.token }}
73+
APP_TOKEN: ${{ steps.bridge_app_token.outputs.token }}
74+
CODEX_AUDIT_DISPATCH_TOKEN: ${{ secrets.CODEX_AUDIT_DISPATCH_TOKEN }}
75+
run: |
76+
python - <<'PY'
77+
import json
78+
import os
79+
import urllib.request
80+
import urllib.error
81+
82+
repo = os.environ["GITHUB_REPOSITORY"]
83+
bridge_repo = os.environ["BRIDGE_REPOSITORY"]
84+
token = os.environ["GH_TOKEN"]
85+
dispatch_token = os.environ.get("APP_TOKEN") or os.environ.get("CODEX_AUDIT_DISPATCH_TOKEN")
86+
if not dispatch_token:
87+
raise SystemExit("Bridge dispatch requires a GitHub App token or CODEX_AUDIT_DISPATCH_TOKEN")
88+
89+
def request(method, url, payload=None, auth_token=token):
90+
data = json.dumps(payload).encode("utf-8") if payload is not None else None
91+
req = urllib.request.Request(
92+
url,
93+
data=data,
94+
method=method,
95+
headers={
96+
"Authorization": f"Bearer {auth_token}",
97+
"Accept": "application/vnd.github+json",
98+
"Content-Type": "application/json",
99+
},
100+
)
101+
with urllib.request.urlopen(req, timeout=60) as response:
102+
body = response.read().decode("utf-8")
103+
return response.status, json.loads(body) if body else None
104+
105+
issue_body = "\n".join([
106+
"## Long-Horizon Shadow Signal Request",
107+
"",
108+
f"- Source ref: `{os.environ['SOURCE_REF']}`",
109+
"- Mode: `shadow`",
110+
"- Required output: `data/output/latest_signal.json`",
111+
"- AI output must not place orders or change live strategy rules.",
112+
"",
113+
"## Context",
114+
"",
115+
"Use repository examples and any committed context bundles as evidence.",
116+
"If evidence is insufficient, report findings and do not edit artifacts.",
117+
])
118+
_, issue = request(
119+
"POST",
120+
f"https://api.github.com/repos/{repo}/issues",
121+
{"title": "Long-horizon AI shadow signal review", "body": issue_body},
122+
)
123+
124+
dispatch_payload = {
125+
"ref": "main",
126+
"inputs": {
127+
"source_repo": repo,
128+
"issue_number": str(issue["number"]),
129+
"source_ref": os.environ["SOURCE_REF"],
130+
"mode": "review_and_fix",
131+
"provider": os.environ["BRIDGE_PROVIDER"],
132+
"task": os.environ["BRIDGE_TASK"],
133+
"auto_merge": "false",
134+
},
135+
}
136+
status, _ = request(
137+
"POST",
138+
f"https://api.github.com/repos/{bridge_repo}/actions/workflows/selfhosted_monthly_review.yml/dispatches",
139+
dispatch_payload,
140+
auth_token=dispatch_token,
141+
)
142+
if status not in (201, 204):
143+
raise RuntimeError(f"unexpected dispatch status: {status}")
144+
print(f"Created issue #{issue['number']} and dispatched {bridge_repo}")
145+
PY

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.pytest_cache/
2+
.ruff_cache/
3+
.venv/
4+
__pycache__/
5+
*.pyc
6+
7+
# Keep local/private research inputs out of git by default.
8+
data/input/*
9+
!data/input/.gitkeep
10+
11+
# Generated shadow outputs may be checked in only when intentionally promoted.
12+
data/output/tmp/

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# AiLongHorizonSignalPipelines
2+
3+
Research-only long-horizon AI signal artifact repository for QuantStrategyLab.
4+
5+
This repository does not place trades, store broker credentials, or own live
6+
allocation policy. It prepares and validates shadow signal artifacts that can
7+
later be consumed by sidecar plugins after a separate review and promotion
8+
process.
9+
10+
## Boundary
11+
12+
This repo owns:
13+
14+
- long-horizon AI context bundle examples
15+
- shadow signal JSON schema expectations
16+
- validation tooling for `latest_signal.json`
17+
- issue/workflow handoff to `QuantStrategyLab/CodexAuditBridge`
18+
- replay-ready artifact records for later research review
19+
20+
This repo does not own:
21+
22+
- broker API access
23+
- order placement
24+
- live portfolio allocation
25+
- deterministic strategy rules in `UsEquityStrategies`
26+
- runtime plugin execution in `QuantStrategyPlugins`
27+
- API keys for model providers
28+
29+
## Operating Model
30+
31+
1. A workflow creates a long-horizon shadow-signal issue.
32+
2. The issue is dispatched to `QuantStrategyLab/CodexAuditBridge` with task
33+
`long_horizon_signal_shadow`.
34+
3. `CodexAuditBridge` tries self-hosted Codex first and uses its own OpenAI or
35+
Anthropic API fallback only when configured.
36+
4. Any AI-generated artifact must remain `mode=shadow` and pass local schema
37+
validation.
38+
5. Downstream runtimes must treat the artifact as advisory context only until a
39+
separate deterministic policy engine explicitly consumes it.
40+
41+
## Local Validation
42+
43+
Validate the example artifact:
44+
45+
```bash
46+
python scripts/validate_latest_signal.py examples/latest_signal.example.json
47+
```
48+
49+
Validate the promoted latest artifact when it exists:
50+
51+
```bash
52+
python scripts/validate_latest_signal.py
53+
```
54+
55+
## Artifact Contract
56+
57+
The latest artifact path is:
58+
59+
```text
60+
data/output/latest_signal.json
61+
```
62+
63+
Historical generated copies can be stored under:
64+
65+
```text
66+
data/output/signal_history/YYYY-MM-DD.json
67+
```
68+
69+
All artifacts must remain shadow-only. They cannot encode broker orders, target
70+
quantities, or live allocation overrides.

data/input/.gitkeep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

data/output/.gitkeep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

docs/architecture.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Architecture
2+
3+
## Current Architecture Understanding
4+
5+
QuantStrategyLab already separates strategy math, snapshot generation, runtime
6+
execution, and broker adapters. This repository adds a research-only AI signal
7+
pipeline without changing that production boundary.
8+
9+
## Main Design Pressure
10+
11+
LLM output is not naturally deterministic or backtestable. The repository must
12+
therefore preserve every generated artifact and keep AI output away from live
13+
order routing.
14+
15+
## Recommended Low-Risk Shape
16+
17+
- `AiLongHorizonSignalPipelines` stores context examples, schema, validation,
18+
and shadow artifacts.
19+
- `CodexAuditBridge` owns provider routing and API keys.
20+
- `QuantStrategyPlugins` may later read promoted artifacts as sidecar context.
21+
- Platform repositories remain unchanged.
22+
23+
## Not Recommended
24+
25+
- Giving AI broker credentials.
26+
- Parsing free text into orders.
27+
- Letting AI change strategy thresholds, max leverage, universe membership, or
28+
execution mode.
29+
- Re-generating old AI judgments during replay instead of replaying stored
30+
artifacts.
31+
32+
## Validation Strategy
33+
34+
The current minimum check is schema validation for `latest_signal.json`. Future
35+
promotion should add replay tests that consume stored artifacts without calling
36+
model APIs.
37+
38+
## Risk Notes
39+
40+
The artifact is research evidence, not a trading instruction. Missing evidence,
41+
expired artifacts, low confidence, or schema failures should default to no-op in
42+
any downstream consumer.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"as_of": "2026-05-28",
3+
"horizon": "1-3 months",
4+
"universe": ["SPY", "QQQ", "SOXX", "TQQQ", "BIL", "BOXX"],
5+
"price_context": {
6+
"SPY": {"trend": "above_200d", "volatility": "normal"},
7+
"QQQ": {"trend": "above_200d", "volatility": "normal"},
8+
"SOXX": {"trend": "mixed", "volatility": "elevated"}
9+
},
10+
"existing_strategy_context": {
11+
"ai_may_place_orders": false,
12+
"ai_mode": "shadow",
13+
"downstream_policy_required": true
14+
},
15+
"notes": [
16+
"Synthetic example only; do not use as live evidence."
17+
]
18+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"schema_version": "1",
3+
"as_of": "2026-05-28",
4+
"generated_at": "2026-05-28T00:00:00Z",
5+
"mode": "shadow",
6+
"horizon": "1-3 months",
7+
"universe": ["SPY", "QQQ", "SOXX", "TQQQ", "BIL", "BOXX"],
8+
"regime": "mixed",
9+
"risk_flags": ["example_only", "requires_shadow_replay"],
10+
"candidate_bias": {
11+
"SPY": "neutral",
12+
"QQQ": "watch",
13+
"SOXX": "watch",
14+
"TQQQ": "avoid",
15+
"BIL": "neutral",
16+
"BOXX": "neutral"
17+
},
18+
"confidence": 0.25,
19+
"evidence": {
20+
"sources": ["examples/context_bundle.example.json"],
21+
"summary": "Synthetic example artifact for schema validation only.",
22+
"data_gaps": ["No live data was used.", "No replay evidence is attached."]
23+
},
24+
"expires_at": "2026-06-28",
25+
"policy": {
26+
"execution_allowed": false,
27+
"downstream_use": "Shadow context only; deterministic policy must explicitly opt in before any future use."
28+
}
29+
}

pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[build-system]
2+
requires = ["setuptools>=68"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "ai-long-horizon-signal-pipelines"
7+
version = "0.1.0"
8+
description = "Shadow-only long-horizon AI signal artifacts for QuantStrategyLab research."
9+
requires-python = ">=3.11"
10+
dependencies = []
11+
12+
[project.optional-dependencies]
13+
test = ["pytest>=8"]
14+
15+
[tool.setuptools.packages.find]
16+
where = ["src"]
17+
18+
[tool.pytest.ini_options]
19+
pythonpath = ["src"]
20+
testpaths = ["tests"]

0 commit comments

Comments
 (0)