Raxx · internal docs

internal · gated

ETF NAV Discount Strategy — Model Card

Strategy ID: etf-nav-discount Version: 0.1 (research) Status: research — not ready for feature-dev Date: 2026-05-12 UTC

This card describes what feature-developer needs to know to productize this strategy. It is premature to file implementation cards until: (a) backtest has been run on real data, (b) Kristerpher makes the headline-vs-adjacent call per issue #479, (c) business-legal-researcher has scoped the regulatory question.


What It Does

Evaluates a weekly trigger: if ticker trades at a discount of threshold_pct or more below its published NAV, submit a market buy order for qty shares in the user's paper (or live, post-opt-in) Alpaca account.

The strategy is authored by the user in natural language, parsed by Claude into a validated DSL struct, confirmed by the user before activation, then executed by a background scheduler.


Input Schema

Strategy execution trigger (internal, not user-facing)

{
  "strategy_id": "string (uuid4)",
  "ticker": "string",
  "threshold_pct": "number (negative float, e.g. -0.50)",
  "qty": "integer",
  "order_type": "string (market | limit)",
  "paper_only": "boolean",
  "max_notional_usd": "number"
}

Condition evaluation inputs (fetched at evaluation time)

{
  "market_price": "number (USD)",
  "nav_per_share": "number (USD)",
  "nav_date": "string (ISO 8601 date)",
  "eval_datetime_utc": "string (ISO 8601 datetime)"
}

Output Schema

Condition evaluation result

{
  "eval_id": "string (uuid4)",
  "condition_met": "boolean",
  "discount_pct": "number",
  "data_quality_flags": ["array of strings"],
  "execution_id": "string (nullable)"
}

Order submission result (if condition met)

{
  "execution_id": "string (uuid4)",
  "order_status": "string (pending | filled | cancelled | error)",
  "alpaca_order_id": "string (nullable)",
  "fill_price": "number (nullable)",
  "qty_filled": "integer (nullable)"
}

Data Dependencies

Data Source Field Update Frequency Licensing
Alpaca /v2/stocks/{symbol}/quotes/latest market_price real-time Alpaca Basic (free for funded accounts)
Fund family sites (SPDR, iShares, Vanguard) nav_per_share end-of-day Free for non-commercial; commercial license TBD — flag to BLR
ETF.com (fallback) nav_per_share end-of-day TBD — flag to BLR
Alpaca /v2/account pattern_day_trader flag live Same as above

Compute + Storage Budget (per user)

At MVP (3 strategies per user, weekly schedule):

Operation Frequency Cost
Claude parse call (strategy creation) 1× per strategy ~$0.0001 (Haiku, 500 tokens)
NAV fetch (fund site HTTP GET) 1× per week per strategy Negligible bandwidth
Alpaca quote fetch 1× per week per strategy Alpaca API call, within free tier
ConditionEvaluation DB row 52× per year per strategy ~1 KB per row; 156 KB/user/year
ExecutionRecord DB row ~0-10× per year per strategy ~500 B per row

At 1,000 users: ~156 MB of evaluation data per year. Negligible at Postgres scale.


Expected Latency


Failure Modes (Summary)

See failure-modes.md for full documentation. Key production risks:

  1. Stale NAV — abort + alert if NAV is > nav_lag_days old
  2. NAV source offline — multi-source fallback; abort if all fail
  3. Signal fires only in stress — expected behavior; UI must communicate clearly
  4. Over-accumulation without exit — position cap is the critical safety rail
  5. PDT rule violation — check Alpaca account flag before every execution
  6. Regulatory (advice framing) — Claude-generated UI copy must be reviewed by counsel

What to Monitor in Production

Signal Threshold Alert
NAV source failure rate > 10% of weekly fetches ops@ alert
Condition evaluation abort rate > 20% of evaluations ops@ alert
Order rejection rate > 5% of submitted orders ops@ alert + user notification
Strategy accumulation per user > max_notional_usd auto-pause + user notification
Claude parse failure rate > 15% of parse attempts ops@ alert
PDT block rate Any occurrence User notification

Open Questions Before Feature-Dev

These must be resolved before implementation cards are filed:

  1. OQ-1 (hard blocker — regulatory): business-legal-researcher must scope the investment-adviser registration question for automated execution of user-authored strategies. Does the LLM-as-parser framing hold? What disclosures are required?

  2. OQ-2 (hard blocker — data): commercial licensing for NAV data (fund sites, ETF.com). BLR must confirm before scraper ships to production.

  3. OQ-3 (backtest blocker): backtest has not been run on real historical data. The strategy cannot be promoted to backtested status without running reference.py against actual Alpaca historical bars + NAV data. Assign to a feature-developer with Alpaca sandbox access.

  4. OQ-4 (product decision): Kristerpher's headline-vs-adjacent call per issue #479. This shapes whether this strategy is the product's primary surface or an add-on.

  5. OQ-5 (UX): human-confirm flow design. Does every trigger require confirm, or only the first per strategy per live-trading activation? Product decision.


Disclaimer

This model card describes a research-stage strategy. No backtest has been run on real data. All performance claims are hypotheses, not validated results. This document does not constitute investment advice. Productization requires regulatory clearance, validated backtest results, and explicit operator decisions on the items in the Open Questions section above.