true engineering specification (PROJECT_SPEC_V2.md). This version adds the missing parts you identified:
- Exact APIs
- Event model
- Storage design
- Execution semantics
- Reasoning-service boundaries
- Formal policy semantics
- Testing plan
- Rollout plan
- Concrete milestone
You can paste this directly as PROJECT_SPEC_V2.md in your repo and use it with Codex.
This specification defines the architecture and implementation requirements for a safe AI-assisted trading system.
The system must:
- separate reasoning from execution
- enforce deterministic risk controls
- support paper trading before live trading
- allow AI-assisted development via AAHP
The following are explicitly out of scope for MVP:
- autonomous live trading
- multi-broker support
- options strategies
- portfolio optimization
- reinforcement learning training pipelines
- large LLM agent orchestration
flowchart TD
A[Market Data / News] --> B[data-service]
B --> C[feature-service]
C --> D[strategy-service]
D --> E[reasoning-service]
D --> F[policy-service]
E --> F
F -->|APPROVE| G[execution-service]
F -->|REJECT| H[audit-log]
G --> I[paper-broker]
G --> J[portfolio-service]
J --> K[dashboard]
Responsibilities:
- ingest market data
- normalize symbols
- timestamp validation
- publish feature inputs
Storage:
- Redis: latest quotes
- Parquet: historical bars
- retention: permanent
Endpoints:
GET /v1/market/quote/{symbol}
GET /v1/market/bar/{symbol}
Events:
market.quote.updated
market.bar.closed
Responsibilities:
- compute indicators
- generate model features
- provide feature snapshots
Endpoints:
GET /v1/features/{symbol}
Output schema:
FeatureSnapshot
Responsibilities:
- generate signal candidates
- deterministic strategies only (MVP)
Endpoints:
POST /v1/signals/generate
Output:
{
"signal_id": "uuid",
"symbol": "AAPL",
"candidate_action": "BUY",
"confidence": 0.71,
"size_pct": 0.02,
"model_version": "v0.1"
}Events:
signal.candidate.created
Purpose:
Provide LLM reasoning but never control execution.
SignalCandidate
MarketContext
PortfolioSnapshot
NewsSummary
ReasoningMemo
Example:
{
"signal_id": "uuid",
"thesis": "Momentum continues after earnings revision",
"counter_thesis": "Macro event tomorrow",
"risk_flags": ["earnings_proximity"],
"recommendation": "ALLOW_WITH_CAUTION"
}Rules:
output must be valid JSON
schema validated with Pydantic
1 invalid JSON → retry with repair prompt
2 failures → reasoning_status = FAILED
policy-service receives reasoning_status
soft timeout: 8 seconds
hard timeout: 15 seconds
max input tokens: 5000
max output tokens: 700
Purpose:
Deterministic risk gate.
POST /v1/policy/evaluate
{
"signal_id": "uuid",
"symbol": "AAPL",
"candidate_action": "BUY",
"confidence": 0.71,
"size_pct": 0.02,
"market_context": {
"data_age_seconds": 12,
"market_open": true,
"event_blackout_active": false
},
"portfolio_context": {
"gross_exposure_pct": 0.25,
"daily_drawdown_pct": 0.01
}
}{
"decision": "APPROVE",
"reasons": [],
"approved_size_pct": 0.01,
"expires_at": "ISO8601"
}market_open == false
data_age_seconds > 30
symbol not in allowlist
daily_drawdown_pct >= 0.03
event_blackout_active == true
liquidity_score < 0.7
confidence < 0.6
reasoning recommendation == ALLOW_WITH_CAUTION
portfolio correlation > 0.35
REJECT > REVIEW > APPROVE
Responsible for broker interaction.
POST /v1/orders
{
"signal_id": "uuid",
"symbol": "AAPL",
"side": "BUY",
"qty": 10,
"order_type": "MARKET"
}Idempotency-Key
same key + same payload → return original response
same key + different payload → HTTP 409
NEW
ACCEPTED
PARTIALLY_FILLED
FILLED
REJECTED
CANCELLED
Transitions:
NEW → ACCEPTED
ACCEPTED → FILLED
ACCEPTED → PARTIAL
ACCEPTED → CANCEL_PENDING
Polling interval:
5 seconds
Reconciliation key:
external_order_id
Source of truth:
broker execution state
Portfolio state reconciled afterward.
Database:
Postgres
Tables:
orders
fills
execution_events
Retention:
permanent
Tables:
policy_evaluations
policy_rule_hits
Retention:
180 days
Tables:
positions
portfolio_snapshots
pnl_history
Storage:
Redis → latest quotes
Parquet → historical bars
Events published via NATS/Kafka.
Topics:
market.quote.updated
signal.candidate.created
policy.decision.made
order.submitted
order.filled
order.rejected
Coverage target:
85% policy-service
85% execution-service
70% reasoning-service
Test cases:
policy rule evaluation
order idempotency
state transitions
contract validation
Flows:
signal → policy → execution
reasoning → policy review
broker rejection
stale data rejection
Deterministic replays:
fixed historical signals
expected policy outcomes
expected order states
Local dev.
fake signals
paper broker
Paper trading.
real market data
5 symbols
no overnight positions
Sandbox broker.
manual approval required
max trade size $100
Small live capital.
max daily loss $50
kill switch enabled
Goal:
Deterministic paper trading core.
Components:
libs/contracts
policy-service
execution-service
paper broker
strategy-service (fake signals)
dashboard
Out of scope:
reasoning-service
live brokers
RL training
Definition of done:
policy evaluation endpoint works
orders endpoint works
fake signal can produce paper trade
order lifecycle visible in dashboard
integration tests pass
Directory:
.ai/handoff/
Structure:
manifest.json
task_briefs/
summaries/
decisions/
prompts/
Purpose:
limit context
enable multi-agent collaboration
preserve architecture decisions
Prompt:
Bootstrap Python monorepo for AI trading stack.
Read only:
docs/architecture.md
PROJECT_SPEC_V2.md
Create:
services/policy-service
services/execution-service
libs/contracts
tests
README.md
docker-compose.yml
Makefile
Requirements:
Python 3.11
FastAPI
Pydantic
Postgres
paper broker adapter
unit tests required
After completion write:
.ai/handoff/summaries/TASK-000-bootstrap.md
Python 3.11
FastAPI
Pydantic
Postgres
Redis
NATS or Kafka
DuckDB
Docker
Prometheus
Grafana
Before live trading:
paper trade for multiple weeks
max 0.5% risk per trade
daily drawdown stop
kill switch
stale data rejection
human override