Institutional-grade, high-throughput financial sentiment analysis engine built in Rust.
3.0K
tierpulse is an institutional-grade, high-throughput financial sentiment analysis engine built in Rust. It utilizes a three-tier "Intelligence Failover" strategy with High-Scale Batching to provide ultra-reliable sentiment analysis for trading bots and financial applications.
GitHub repo: https://github.com/kabudu/tierpulse
ort)Create a .env file or set environment variables. All variables prefixed with TP_ are used to configure the service.
| Variable | Default | Description |
|---|---|---|
PORT | 8080 | Server listening port. |
TP_TIINGO_KEY | Required | Primary news provider API key. |
TP_FINNHUB_KEY | null | Tertiary news provider key (final fallback tier). |
TP_MARKETAUX_KEY | null | Secondary news provider key (batched fallback tier). |
TP_GROK_KEY | null | xAI API key (utilizes grok-4-1-fast-reasoning). |
TP_DEEPSEEK_KEY | null | DeepSeek API key (utilizes deepseek-chat). |
TP_PRIMARY_LLM | grok | Primary LLM engine (grok or deepseek). |
TP_REDIS_URL | null | Redis URL for distributed caching (e.g., redis://localhost:6379). |
TP_CACHE_TTL | 300 | In-memory/Redis cache expiration in seconds. |
TP_AUTH_MODE | none | Authentication mode: none, api_key, or jwt. |
TP_AUTH_API_KEYS | null | Required for api_key mode. Format: tenantA:keyA,tenantB:keyB. |
TP_JWT_SECRET | null | Required for jwt mode (HS256 signing secret). |
TP_JWT_ISSUER | null | Optional JWT issuer (iss) validation. |
TP_RATE_LIMIT | 100 | Per-tenant tokens per minute (tenant-scoped limiter). |
TP_GLOBAL_RATE_LIMIT | 1000 | Global protection guard tokens per minute (service-wide limiter). |
TP_EGRESS_ALLOWLIST | "" | Optional comma-separated extra HTTPS hosts allowed for outbound egress (Layer 1). |
TP_PROVIDER_CALL_BUDGET_PER_REQUEST | 6 | Max outbound news-provider calls allowed per /analyze request before news-tier budget is considered exhausted. |
TP_ONNX_THREADS | 2 | CPU thread allocation for the ort session. |
TP_MODEL_PATH | model.onnx | Path to the INT8 quantized ONNX model. |
TP_LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR). |
This service supports both via env configuration:
TP_AUTH_MODE=api_key + TP_AUTH_API_KEYS=tenantA:keyA,tenantB:keyBTP_AUTH_MODE=jwt + TP_JWT_SECRET=... (+ optional TP_JWT_ISSUER=...)When auth is enabled, tenant identity is extracted at request time and enforced through tenant-scoped rate limiting.
token, api_token, api_key, authorization, and x-api-key.Authorization and x-api-key are marked as sensitive in the HTTP layer to prevent accidental exposure in request logs.Tiingo -> MarketAux -> Finnhub) or when TP_PROVIDER_CALL_BUDGET_PER_REQUEST is exhausted.api.tiingo.com, finnhub.io, api.marketaux.com, api.x.ai, api.deepseek.com).TP_EGRESS_ALLOWLIST can extend the allowed host set (for controlled environment-specific endpoints) without code changes.GET /metrics returns Prometheus-style metrics text for:
request_duration_ms (quantiles: p50/p95/p99)cache_hit_ratioprovider_error_rate{provider,status_class}fallback_transition_count{from,to}tier_exhaustion_rateGET /health/live is a liveness probe (process is up).GET /health/ready is a readiness probe with per-tier status, degradation reason, and breaker state.GET /health remains mapped to readiness for backward compatibility.Readiness response shape (example):
{
"status": "degraded",
"tiers": {
"tier_1_local_onnx": {
"status": "operational",
"degradation_reason": null,
"breaker_state": "not_configured"
},
"tier_2_news": {
"status": "operational",
"degradation_reason": "primary_news_provider_unavailable_using_fallback_capacity",
"breaker_state": "not_configured",
"providers": {
"tiingo": { "status": "degraded", "breaker_state": "not_configured" },
"marketaux": {
"status": "configured",
"breaker_state": "not_configured"
},
"finnhub": {
"status": "not_configured",
"breaker_state": "not_configured"
}
}
},
"tier_3_llm": {
"status": "degraded",
"degradation_reason": "no_llm_provider_configured",
"breaker_state": "not_configured",
"providers": {
"grok": {
"status": "not_configured",
"breaker_state": "not_configured"
},
"deepseek": {
"status": "not_configured",
"breaker_state": "not_configured"
}
}
}
}
}
Provider auth transport summary (verified against provider docs):
token and Authorization: Token <api_token> header for REST.token and X-Finnhub-Token: <api_key> header for GET requests.api_token as a query parameter for REST requests.docker pull boxedcode/tierpulse:latest
docker run -p 8080:8080 --env-file .env boxedcode/tierpulse:latest
POST /api/v1/analyzeAnalyze sentiment for a list of symbols with automatic batching and multi-tier failover.
Request validation (enforced):
symbols: required, 1 to 50 itemssymbols[].ticker: required, trimmed length 1 to 16symbols[].name: required, trimmed length 1 to 120symbols[].ticker: must be unique within the request (case-insensitive)lookback_hours: 1 to 168max_articles_per_symbol: 1 to 20All error responses use a standardized envelope:
code: machine-readable error codemessage: human-readable summaryretry_after_seconds: retry guidance when applicable (null if not applicable)request_id: correlation ID for support and tracingdetails: array of structured detail objects (may be empty)Error Codes
| Code | HTTP Status | Meaning | Recovery Guidance |
|---|---|---|---|
INVALID_REQUEST | 400 | Request payload failed schema/semantic validation. | Fix request fields based on details[] and retry. |
UNAUTHORIZED | 401 | Missing/invalid API key or JWT credentials. | Provide valid auth headers (x-api-key or Authorization: Bearer <jwt>) and retry. |
TENANT_RATE_LIMITED | 429 | Tenant-scoped rate limiter was exceeded. | Back off for retry_after_seconds, then retry with jitter; reduce tenant request burst. |
GLOBAL_RATE_LIMITED | 429 | Global protection guard was exceeded. | Back off for retry_after_seconds, then retry with jitter; reduce overall traffic/load. |
INTELLIGENCE_EXHAUSTION | 503 | All provider/LLM tiers are unavailable or exhausted for this request path. | Retry after retry_after_seconds; verify provider key health/quota and upstream reachability. |
Headers (when auth is enabled):
x-api-key: <tenant key>Authorization: Bearer <jwt> (tenant identity from tid or sub claim)Request:
{
"symbols": [
{ "ticker": "AAPL", "name": "Apple Inc." },
{ "ticker": "TSLA", "name": "Tesla, Inc." },
{ "ticker": "BTC", "name": "Bitcoin" }
],
"lookback_hours": 24,
"max_articles_per_symbol": 5
}
Response:
{
"request_id": "tp_550e8400-e29b-41d4-a716-446655440000",
"results": [
{
"symbol": "AAPL",
"sentiment_score": 0.82,
"label": "bullish",
"confidence": 0.94,
"source_tier": "tier_1_local_onnx",
"news_provider": "batch_news",
"article_count": 5,
"reasoning": null
},
{
"symbol": "BTC",
"sentiment_score": -0.45,
"label": "bearish",
"confidence": 0.88,
"source_tier": "tier_3_llm",
"news_provider": null,
"article_count": 0,
"reasoning": "Recent regulatory tightening in EU leading to cautious sentiment."
}
],
"execution_time_ms": 450
}
Validation Error Response (400)
{
"code": "INVALID_REQUEST",
"message": "Request validation failed.",
"retry_after_seconds": null,
"request_id": "tp_550e8400e29b41d4a716446655440000",
"details": [
{
"code": "INVALID_SYMBOL_COUNT",
"field": "symbols",
"message": "symbols must contain between 1 and 50 items"
},
{
"code": "INVALID_LOOKBACK_HOURS",
"field": "lookback_hours",
"message": "lookback_hours must be between 1 and 168"
}
]
}
Tenant Rate Limit Response (429)
{
"code": "TENANT_RATE_LIMITED",
"message": "Tenant 'tenantA' exceeded request budget.",
"retry_after_seconds": 1,
"request_id": "tp_550e8400e29b41d4a716446655440000",
"details": [
{
"field": "tenant_id",
"message": "tenantA"
}
]
}
Intelligence Exhaustion Response (503)
{
"code": "INTELLIGENCE_EXHAUSTION",
"message": "All upstream providers are currently rate-limited or unreachable.",
"retry_after_seconds": 300,
"request_id": "tp_550e8400e29b41d4a716446655440000",
"details": [
{
"tier_1": "exhausted",
"tier_2": "exhausted",
"tier_3_llm": "cooldown_active"
}
]
}
The system utilizes a multi-stage pipeline:
finbert to INT8 ONNX..github/workflows/ci.yml runs cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all-targets --all-features, and cargo audit.tests/provider_failover_llm_schema_tests.rs verifies provider failover ordering parity and strict LLM schema parsing behavior.tests/analyze_validation_http_tests.rs validates typed error envelope parity (400/401/429/503) and operational endpoint exposure.openapi/openapi.v1.yaml is the source-of-truth versioned API contract for public endpoints.tests/openapi_contract_tests.rs validates core OpenAPI invariants (version, required paths, response envelope refs, and ErrorEnvelope required fields) and is executed by the dedicated CI contract-test job.Content type
Image
Digest
sha256:d07fd68da…
Size
410.2 MB
Last updated
4 months ago
docker pull boxedcode/tierpulse