QUARK · SIGNAL API DOCS
Dashboard →

Signal API Documentation

Full reference for the Quark signal feed. Endpoint list, response schemas, rate limits, and — most importantly — how to translate the regime score / crash probability / defensive blend into trades in your own portfolio.

Contents

Authentication & rate limits

Base URLhttps://api.quarkresearch.cc
Auth headerx-api-key: qk_...
Auth query?api_key=qk_... (for cURL convenience; prefer header)
Rate limit60 requests / minute / key
Content-Typeapplication/json (CSV endpoint returns text/csv)
Signal refreshDaily post-close; intraday if regime flips or crash-prob crosses threshold
TLSRequired. HTTP requests are rejected.

Endpoints

GET/api/signals/current

Top-level regime + tail risk + allocation summary. The most common call.

GET/api/signals/current/full

Unabridged signal — adds risk_params (τ_decoh, quantum_halflife, grassmann_distance) and rotation state. Use this when you want to replicate the full decision layer.

GET/api/signals/history?days=N

Historical signal observations, JSON. Max days=730.

GET/api/signals/history.csv?days=N

Same data, CSV. Columns: timestamp_utc, regime, regime_score, crash_prob_1d, crash_prob_5d, crash_prob_21d, equity_pct, defensive_pct, snr. Stable — safe to pipe into pandas/R.

GET/api/signals/usage

Your account info: tier, key last-4, webhook URL, rate limit, list of endpoints available to your tier.

GET/api/alpha/attribution

Per-signal decomposition of the library's α over the attribution window. 18 tracked signals; returns contribution in dollars, percent, and normalized share.

POST/api/webhook/register

Body: {"url": "https://your-server/webhook"}. One webhook per key. Replaces any existing registration.

POST/api/webhook/test

Fires a synthetic payload to your registered URL. Useful for verifying your receiver signature check before a real regime change lands.

POST/api/webhook/unregister

Removes webhook registration.

POST/api/billing/portal

Returns Stripe customer portal URL for self-service subscription management.

Full response schema

Every numeric field is a float in [0, 1] unless otherwise noted. All fields carry a fallback: bool flag — when true, the section is showing a classical/default estimate because the quantum pipeline failed to produce a non-fallback value this cycle.

regime section

FieldTypeRangeMeaning
regime_scorefloat0.0 – 1.0Continuous. 0 = RISK_ON, 0.5 = NEUTRAL, 1.0 = CRISIS.
regime_labelstringDiscrete label: RISK_ON, NEUTRAL, RISK_OFF, CRISIS.
landscape_positionfloat0.0 – 1.0Where in the potential landscape the market sits. 0 = normal attractor, 1 = crisis attractor.
barrier_depletionfloat0.0 – 1.0Fraction of regime-defense barrier already consumed. Values above 0.7 mean a regime transition is imminent.
decoherence_stressfloat0.0 – 1.0Factor-correlation instability. 0 = factor structure stable; 1 = correlations reorganizing.
tda_stressfloat0.0 – 1.0Topological crash signature from persistent homology of correlation trajectories.
past_saddlebooltrue when the system has already crossed the saddle point between regimes — a transition is locked in.
crash_prob_1dfloat0.0 – 1.01-day crash probability from this section (duplicate of tail_risk.crash_prob_1d).

tail_risk section

FieldTypeRangeMeaning
crash_prob_1dfloat0.0 – 1.0Probability of a ≥3σ drawdown in the next trading day.
crash_prob_5dfloat0.0 – 1.0Same, 5-day horizon.
crash_prob_21dfloat0.0 – 1.0Same, 21-day (~1 month) horizon. The workhorse tail-risk number.
instanton_var_10dfloat0.0+Non-perturbative 10-day VaR as a decimal fraction of portfolio NAV (e.g. 0.0695 = 6.95%).
instanton_actionfloat0.0+Barrier action integral — the "height" of the crash barrier. Higher = further from a crash.

signal_quality section

FieldTypeRangeMeaning
avg_snrfloat0.0+Mean |trend velocity| / realized vol across the universe. High = trends are clean; low = noise.
momentum_weightfloat0.0 – 1.0Recommended weight on momentum factor given current SNR.
quality_weightfloat0.0 – 1.0Recommended weight on quality factor.
volume_weightfloat0.0 – 1.0Recommended weight on volume/liquidity factor.
cyclicality_weightfloat0.0 – 1.0Recommended weight on cyclicality factor. (Weights sum to 1.0.)
n_assetsintNumber of assets with a valid SNR this cycle.

allocation section

FieldTypeRangeMeaning
per_asset_caps{ticker: float}0.0 – 1.0Max weight we would hold in each ticker given the current landscape. Use as an upper clamp on your own weights.
uniform_capfloat0.0 – 1.0Fallback per-asset cap when a ticker isn't in per_asset_caps.
defensive_blendfloat0.0 – 1.0Suggested shift toward defensive posture. 0 = no shift (all-equity), 1 = fully defensive (bonds / gold / cash).
barrier_normalizedfloat0.0 – 1.0Barrier height / full action. 1 = fresh barrier, 0 = depleted.

risk_params section (full endpoint only)

FieldTypeRangeMeaning
quantum_halflifefloatdaysHow fast your covariance/signal estimates should adapt. Short = fast adaptation needed; long = regime stable.
tau_decohfloatdaysDecoherence timescale — days until factor structure is expected to shift.
grassmann_distancefloat0.0 – π/2Angular distance between today's principal subspace and τ days ago. Large = factors rotating.
quantum_shrinkage_boostfloat≥ 1.0Covariance shrinkage multiplier recommended given Grassmannian distance.

rotation section (full endpoint only)

FieldTypeMeaning
grassmannian_distancefloatFactor-rotation magnitude. Same number as in risk_params.
should_invalidate_cachebooltrue when cached signals / weights should be recomputed — eigenspace has rotated enough to invalidate them.
force_short_halflifebooltrue when you should temporarily halve your signal smoothing window.

fund_summary section

FieldTypeMeaning
return_pctfloatOur live ensemble portfolio's cumulative return (%), for benchmarking.
regimestringRegime label we're currently trading under.
healthstringGREEN / AMBER / RED — operational health of the live system.

How to apply the signal to your portfolio

The signal is deliberately decoupled from any specific trade list — it's a set of continuous knobs you translate into your own allocation logic. Below are the three patterns we see licensees use successfully. Pick one that matches your existing workflow.

Pattern 1 — Defensive overlay on a static allocation

Simplest. You already have a strategic allocation (e.g. 60/40). Use defensive_blend to scale up/down your equity weight when the signal says conditions are deteriorating.

target_equity_pct = base_equity_pct × (1 − signal.allocation.defensive_blend)
target_bond_pct   = 1 − target_equity_pct

# Example:
# base 60/40, defensive_blend = 0.25 →
#   target_equity = 0.60 × 0.75 = 45%
#   target_bond   = 55%
Rebalance trigger: only rebalance when the absolute change in defensive_blend exceeds 0.10, or when rotation.should_invalidate_cache is true. Prevents thrash.

Pattern 2 — Tail-risk gated sizing

For strategies that size positions as Kelly fractions or vol-targeted. Scale each position's size by the complement of the 21-day crash probability, so sizes shrink as crash risk rises.

crash_21 = signal.tail_risk.crash_prob_21d
sizing_multiplier = 1 − min(2 × crash_21, 0.80)   # cap de-risking at 80%

for asset in portfolio:
    target_size[asset] = base_size[asset] × sizing_multiplier

The 2× factor is a convention — we use it internally. It means a 21d crash probability of 0.40 drives sizing to 20% of baseline; anything higher clamps at 20%.

Pattern 3 — Full quant replication

For licensees running their own quant book. Use the /full endpoint and consume the per-asset caps, factor weights, and Grassmannian state directly.

from scipy.optimize import minimize

def step(signal):
    caps = signal.allocation.per_asset_caps  # ticker → max weight

    # Halflife → covariance EWMA decay
    lam = 0.5 ** (1 / signal.risk_params.quantum_halflife)
    cov = ewma_covariance(returns, lam) × signal.risk_params.quantum_shrinkage_boost

    # Factor tilt from Nelson weights
    factor_tilt = {
        'momentum':    signal.signal_quality.momentum_weight,
        'quality':     signal.signal_quality.quality_weight,
        'volume':      signal.signal_quality.volume_weight,
        'cyclicality': signal.signal_quality.cyclicality_weight,
    }

    # Solve mean-variance with caps as upper bound on each w_i
    weights = mean_variance_with_caps(
        expected_returns=alpha_from_factors(factor_tilt),
        covariance=cov,
        upper_bounds=caps,
        defensive_blend=signal.allocation.defensive_blend,
    )
    return weights

# Only re-solve on a regime change or rotation flag
if signal.rotation.should_invalidate_cache or regime_changed:
    weights = step(signal)

Field-by-field heuristics

FieldHeuristic
regime_scoreBlend your risk-on / risk-off target allocation with this as the mixing weight. Score 0.0 = full risk-on book; 1.0 = full crisis book.
crash_prob_21d > 0.15Hedge threshold. Start buying puts or reducing beta. Our own system buys hedges when this crosses 0.15 upward.
barrier_depletion > 0.70Regime transition imminent. Expect the regime label to flip within days. Do not add risk.
past_saddle == trueTransition is already locked in. Stop trying to fade the move.
avg_snr < 0.10Market is in chop. Reduce turnover, widen rebalance bands.
avg_snr > 0.30Trends are clean. Lengthen holding periods; don't over-trade.
grassmann_distance > 0.30Factors are rotating. Invalidate any cached factor-based signals and recompute.
defensive_blend == 1.0System is recommending a fully defensive posture. Match your policy — typically bonds + gold + cash.
tda_stress > 0.50Correlation topology is stressed — diversification is breaking. Consider dollar-neutral pairs instead of long-only.
fallback == true on any sectionTreat that section as a classical fallback; don't stake conviction on it that cycle.
The signal is not a trade list. You still need a universe, an execution layer, and a risk limit framework. The signal tells you the environment; you translate it to trades.

Worked examples

Example A — "Check once a day, adjust 60/40"

import requests, json

KEY = 'qk_live_...'
BASE = 'https://api.quarkresearch.cc'

def daily_allocation(base_equity=0.60):
    sig = requests.get(f'{BASE}/api/signals/current',
                       headers={'x-api-key': KEY}, timeout=10).json()
    defensive = sig['allocation']['defensive_blend']
    crash_21  = sig['tail_risk']['crash_prob_21d']

    eq_pct = base_equity * (1 - defensive)

    # Extra de-risk if crash probability elevated
    if crash_21 > 0.15:
        eq_pct *= 0.75
    if crash_21 > 0.30:
        eq_pct *= 0.50

    bond_pct = 1 - eq_pct
    return {'equity': eq_pct, 'bonds': bond_pct, 'signal': sig}

alloc = daily_allocation()
print(f"Target equity: {alloc['equity']:.1%}, bonds: {alloc['bonds']:.1%}")

Example B — "Push to Google Sheets via webhook"

# Register webhook (one-time)
curl -X POST -H "x-api-key: $KEY" \
  -H "content-type: application/json" \
  -d '{"url":"https://script.google.com/macros/s/YOUR_DEPLOY/exec"}' \
  https://api.quarkresearch.cc/api/webhook/register

# Test delivery
curl -X POST -H "x-api-key: $KEY" \
  https://api.quarkresearch.cc/api/webhook/test

Your Apps Script receives the same JSON as /api/signals/current. Write it into a sheet, pivot against your broker's positions, done.

Example C — "Size a momentum sleeve with crash gate"

def momentum_sleeve_sizing(base_gross_exposure=1.50):
    sig = requests.get(f'{BASE}/api/signals/current',
                       headers={'x-api-key': KEY}).json()

    # Sizing multiplier from crash probability
    crash_21 = sig['tail_risk']['crash_prob_21d']
    mult = 1 - min(2 * crash_21, 0.80)

    # Adjust momentum weight based on SNR
    mom_w = sig['signal_quality']['momentum_weight']

    target_gross = base_gross_exposure * mult * (mom_w / 0.25)  # 0.25 = long-run avg
    return target_gross

Webhook reference

Register a URL to receive push notifications when the regime changes, crash probability crosses 15%, or rotation.should_invalidate_cache fires.

Payload

Identical to /api/signals/current response, with one extra field at the top:

{
  "event": "regime_change" | "crash_threshold_crossed" | "rotation_invalidate" | "test",
  "regime": {...},
  "tail_risk": {...},
  ...
}

Signature verification

Every webhook request carries an X-Quark-Signature header with an HMAC-SHA256 of the raw body, keyed on your API key. Verify in your receiver:

import hmac, hashlib

def verify(raw_body: bytes, signature: str, api_key: str) -> bool:
    expected = hmac.new(
        api_key.encode('utf-8'),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
Reject unsigned requests. A webhook endpoint is public by definition; without signature verification anyone can spoof regime-change events to trigger your rebalance logic.

Retry policy

Webhooks that return a non-2xx status or time out after 5 seconds will be retried with exponential backoff: 30s, 2m, 10m, 1h, 6h, 24h. After that the delivery is dropped; the signal itself is still available via GET on /api/signals/current.

Errors & status codes

CodeMeaningWhat to do
401Missing or invalid API keyCheck header name is exactly x-api-key. Key starts with qk_.
403Tier doesn't include this endpointUpgrade, or check endpoint-available list at /api/signals/usage.
404Unknown endpointConfirm base URL is api.quarkresearch.cc, not quarkresearch.cc.
429Rate limit exceededBack off; default limit is 60 req/min/key. Retry after Retry-After seconds.
500Server error on our sideRetry with exponential backoff. If persistent, email hello@quarkresearch.cc.
503Signal pipeline temporarily downRetry after 60s. Fall back to previous cached signal.

fallback: true semantics

When any section returns fallback: true, that section's values are from a classical/default estimator rather than the quantum pipeline. Two options:

Changelog

DateChange
2026-04-20Added /api/signals/current/full; added test-ping button on dashboard; signal log dedupe (no more duplicate CSV rows).
2026-04-19Dashboard UI redesigned; tier labels rendered as "Automated Plus" instead of raw IDs; Manage button wired to Stripe portal.
2026-04-18Alpha attribution endpoint live (/api/alpha/attribution).
2026-04-15Endpoints moved behind api.quarkresearch.cc Cloudflare tunnel.