Spaces:
Running
Download README.md from igardner/StockAnalysis: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/spaces/igardner/StockAnalysis/resolve/main/README.md
- Command line
-
hf download hf://spaces/igardner/StockAnalysis/README.md
-
curl -L -o README.md https://huggingface.co/spaces/igardner/StockAnalysis/resolve/main/README.md
title: StockAnalysis
emoji: π
colorFrom: pink
colorTo: gray
sdk: gradio
sdk_version: 6.30.0
python_version: '3.13'
app_file: app.py
pinned: false
license: mit
short_description: Structured stock analysis as JSON for agents.
suggested_hardware: cpu-basic
tags:
- stock-analysis
- finance
- agents
- gradio
- huggingface-hub
- inference-providers
- smoke-test
- model-evaluation
models:
- Qwen/Qwen3.8-27B
- Qwen/Qwen3.8-Flash-Next
- meta-models/Muse-Spark-Open-816B-A42B
- reflection/Beam-1.0-501B-A23B
- deepseek-ai/DeepSeek-V4.1-Flash
- meta-llama/Meta-Llama-3-8B-Instruct
- nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4
- moonshotai/Kimi-K3
- zai-org/GLM-5.3-Flash
- meta-llama/Llama-3.1-8B-Instruct
- meta-models/Muse-Spark-Open-816B-A42B-NVFP4
- OBLITERATUS/Qwen3.8-27B-OBLITERATED
- zai-org/GLM-5.3
- meta-models/Muse-Glimmer-30B
- google/gemma-4-31B-it
- Qwen/Qwen3.5-9B
- meta-llama/Llama-3.2-3B-Instruct
- XiaomiMiMo/MiMo-V2.6-Pro-RL
- inclusionAI/Ling-3.1-flash
- Qwen/Qwen3.5-4B
- openai/gpt-oss-20b
- google/gemma-4-26B-A4B-it
- Qwen/Qwen3.6-35B-A3B
- huihui-ai/Huihui-Qwen3.8-27B-abliterated
- openai/gpt-oss-120b
- XiaomiMiMo/MiMo-V2.6-Flash-RL
- Qwen/Qwen2.5-1.5B-Instruct
- Qwen/Qwen3-0.6B
- Qwen/Qwen3-8B
- deepseek-ai/DeepSeek-R1
StockAnalysis
StockAnalysis returns stock-analysis output as JSON for humans and agents.
The main screen collects a ticker, horizon, risk profile, and an optional question. The Space sends that request to a separate stock-analysis project and returns the upstream result as JSON, with the raw response kept beside it. If the upstream project returns text, an optional local model can convert it to JSON without inventing prices, targets, or recommendations.
The original inference smoke test is still here. It now lives in a compact panel fixed to the bottom-right corner of the page, with the live model table, run history, JSON download, and settings.
Agent API
There is no separate REST layer. Agents call the normal Gradio event behind the
Analyze button. The Hub also generates a live agents.md for the Space. The
repository copy at agents.md adds concrete examples.
To use this application (igardner/StockAnalysis: Structured stock analysis as JSON for agents):
API schema: GET https://igardner-stockanalysis.hf.space/gradio_api/info
Call endpoint: POST https://igardner-stockanalysis.hf.space/gradio_api/call/v2/analyze {"ticker": "AAPL", "horizon": "3 months", "risk_profile": "balanced", "question": ""}
Poll result: GET https://igardner-stockanalysis.hf.space/gradio_api/call/analyze/{event_id}
File inputs: none - all inputs are JSON scalars.
Auth: Bearer $HF_TOKEN (https://huggingface.co/settings/tokens)
The endpoint returns three values in order: the JSON result, the raw upstream
response, and a short human-readable status string. The JSON result is the value
agents should use. See agents.md for the full boundary and
promise list, error shapes, retry guidance, and the private smoke-test caveat.
EVENT_ID=$(curl -sS -X POST \
"https://igardner-stockanalysis.hf.space/gradio_api/call/v2/analyze" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $HF_TOKEN" \
-d '{"ticker":"AAPL","horizon":"3 months","risk_profile":"balanced","question":""}' \
| python -c 'import json,sys; print(json.load(sys.stdin)["event_id"])')
curl -sS -N \
"https://igardner-stockanalysis.hf.space/gradio_api/call/analyze/$EVENT_ID" \
-H "Authorization: Bearer $HF_TOKEN"
Always fetch /gradio_api/info before calling. The schema is the runtime
contract, and Gradio can change the generated examples between versions.
Configuration
| Variable | Required | Default | Purpose |
|---|---|---|---|
EXTERNAL_API_URL |
for real results | blank | HTTP endpoint of the external stock-analysis project. |
STOCK_MODEL_ID |
no | blank | Uploaded custom model ID. Sent as model when set. |
LOCAL_PARSER_URL |
no | blank | OpenAI-compatible chat endpoint used to convert text to JSON. |
LOCAL_PARSER_MODEL |
no | llama3.1 |
Model name for the local parser. |
HF_TOKEN |
for smoke test and private calls | Space secret | Token read with os.getenv("HF_TOKEN"). Never committed. |
EXTERNAL_API_URL is blank until the external project is ready. Until it is
set, the app returns a clear not_configured JSON response instead of failing
silently. STOCK_MODEL_ID is also blank until the custom model is uploaded.
Add HF_TOKEN under Settings β Variables and secrets β New secret in the
Space. Secrets are injected as environment variables and are write-only in the
UI. Restart the Space after adding or changing one. The app never writes the
token to disk or into run results.
Custom model
The custom stock-analysis model is not wired in yet. Set STOCK_MODEL_ID to its
Hub model ID (or to whatever identifier the external project expects) when it is
ready. If it is blank, the external request omits the model field.
Local JSON parser
The parser is optional. It is useful when the external project returns text but an agent needs JSON. It calls any OpenAI-compatible chat-completions endpoint and asks the model to preserve the source facts.
With Ollama:
ollama serve
ollama pull llama3.1
export LOCAL_PARSER_URL=http://127.0.0.1:11434/v1
export LOCAL_PARSER_MODEL=llama3.1
The app also accepts a full /chat/completions URL. A base URL ending in /v1
works, and a bare host gets /v1/chat/completions appended.
On Hugging Face Spaces, 127.0.0.1 points at the Space container. Use a
reachable parser endpoint or a Docker Space with a sidecar if you want the local
parser to run next to the app. The normal deployment path is the external
project plus its own JSON output.
External project contract
The adapter in external_api.py sends:
{
"ticker": "AAPL",
"horizon": "3 months",
"risk_profile": "balanced",
"question": ""
}
It adds "model": "<STOCK_MODEL_ID>" when a custom model is configured. It
sends Authorization: Bearer $HF_TOKEN when the secret is present.
Response handling:
- HTTP 2xx with JSON: passed through unchanged.
- HTTP 2xx with text: wrapped as
{"status": "ok", "ticker": "...", "raw": "..."}unless the local parser is configured. - Non-2xx: returned as
{"status": "error", ...}with the HTTP status and a short error message. - No
EXTERNAL_API_URL: returned as{"status": "not_configured", ...}.
The adapter is the only place to change when the external request or response contract changes.
Inference smoke test
The smoke test is still the original liveness check. It queries the Hub for the
most trending conversational models with a live inference provider, sends one
tiny chat completion to each, and records a literal True or False. The
compact bottom-right panel shows the model table, run history, download, and
settings.
The smoke-test events are marked api_visibility="private", so they do not
appear in the public API docs or the Gradio clients. Private does not block
direct HTTP, so it is not a security boundary. The Analyze event is the only
public endpoint.
Settings live at the top of app.py:
MODEL_LIMIT = 30 # how many trending models to test
CONCURRENCY = 3 # parallel requests
MAX_TOKENS = 8 # output tokens per attempt
REQUEST_TIMEOUT_SECONDS = 60 # per-request timeout
RESULTS_DIR = "/data/hf-inference-smoke"
PAGE_SIZE = 100 # Hub discovery page size
MAX_SCAN = 500 # how far past MODEL_LIMIT to scan
AUTO_RUN_ON_START = False # run automatically on page load
Every run is saved to persistent storage, not the repository:
/data/hf-inference-smoke/
βββ latest.json # most recent run (mirror)
βββ runs/<run_id>.json # one file per run
βββ models/latest.json # most recent discovery snapshot
Each run file records the model list, trending scores, live providers, per-attempt status, latency, usage, and errors.
Models used
The list below is the last recorded snapshot, kept so the tested models and the smoke-test history are not lost. Selection is dynamic, so the next run can use a different order. The run JSON always records the exact list it used.
Snapshot: 2026-10-25T20:18:59Z
Last recorded run: 20261025T202058Z-ff6262 at 2026-10-25T20:20:58Z - 21/30 passed in 64.56s.
| # | Model | Result |
|---|---|---|
| 1 | Qwen/Qwen3.8-27B |
True |
| 2 | Qwen/Qwen3.8-Flash-Next |
False |
| 3 | meta-models/Muse-Spark-Open-816B-A42B |
True |
| 4 | reflection/Beam-1.0-501B-A23B |
True |
| 5 | deepseek-ai/DeepSeek-V4.1-Flash |
True |
| 6 | meta-llama/Meta-Llama-3-8B-Instruct |
False |
| 7 | nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4 |
True |
| 8 | moonshotai/Kimi-K3 |
True |
| 9 | zai-org/GLM-5.3-Flash |
True |
| 10 | meta-llama/Llama-3.1-8B-Instruct |
True |
| 11 | meta-models/Muse-Spark-Open-816B-A42B-NVFP4 |
False |
| 12 | OBLITERATUS/Qwen3.8-27B-OBLITERATED |
False |
| 13 | zai-org/GLM-5.3 |
True |
| 14 | meta-models/Muse-Glimmer-30B |
True |
| 15 | google/gemma-4-31B-it |
True |
| 16 | Qwen/Qwen3.5-9B |
True |
| 17 | meta-llama/Llama-3.2-3B-Instruct |
False |
| 18 | XiaomiMiMo/MiMo-V2.6-Pro-RL |
True |
| 19 | inclusionAI/Ling-3.1-flash |
True |
| 20 | Qwen/Qwen3.5-4B |
False |
| 21 | openai/gpt-oss-20b |
True |
| 22 | google/gemma-4-26B-A4B-it |
True |
| 23 | Qwen/Qwen3.6-35B-A3B |
True |
| 24 | huihui-ai/Huihui-Qwen3.8-27B-abliterated |
False |
| 25 | openai/gpt-oss-120b |
True |
| 26 | XiaomiMiMo/MiMo-V2.6-Flash-RL |
True |
| 27 | Qwen/Qwen2.5-1.5B-Instruct |
False |
| 28 | Qwen/Qwen3-0.6B |
False |
| 29 | Qwen/Qwen3-8B |
True |
| 30 | deepseek-ai/DeepSeek-R1 |
True |
How the smoke test works
- Discovery queries the Hub for
conversationalmodels sorted bytrendingScore, scanning past the requested count so models without a live inference provider are skipped. - Routing calls each model through the HF router with
:cheapest. If that fails, it retries once with:fastest. - A model passes only when the router returns HTTP 200 with a non-empty
choicesarray. - Each attempt asks for at most 8 output tokens, so a 30-model run costs well under a cent.
- Every run is saved to
/data/hf-inference-smoke/.
A False result means the model could not produce a valid chat completion
through either routing policy at the time of the run. It is a liveness check, not
a quality benchmark.
Claims and disclaimer
The app is built for structured stock analysis and predictions. It does not promise a specific return, and it does not guarantee that any result will be profitable. Output comes from an external project and may be incomplete or wrong.
This is research tooling, not financial advice. Markets involve risk, including loss of principal. Verify anything before acting on it.
Project layout
app.py # Gradio UI, settings, public /analyze event, smoke corner
external_api.py # external stock-analysis project adapter
local_parser.py # optional OpenAI-compatible text-to-JSON converter
smoke.py # discovery, routing, smoke runner, persistence
agents.md # concrete agent contract and examples
PROGRESS.md # implementation notes, committed as work proceeds
requirements.txt # pinned dependencies
README.md # Space metadata and documentation
Requirements
- Python 3.13 and Gradio 6.30.0 via the README
sdk_version. huggingface_hub==1.16.1andrequests==2.34.2pinned inrequirements.txt.- CPU basic hardware is sufficient. No GPU is required.
HF_TOKENshould be a Space secret for the smoke test and authenticated outbound calls.
Deployment
- Add
HF_TOKENas a Space secret. - Set
EXTERNAL_API_URLwhen the external stock-analysis project is ready. - Set
STOCK_MODEL_IDwhen the custom model is uploaded. - Optionally set
LOCAL_PARSER_URLandLOCAL_PARSER_MODELfor text-to-JSON conversion. - Restart the Space.
After restart, GET /gradio_api/info lists the analyze endpoint. The smoke
test remains available in the bottom-right corner and does not appear in the
public endpoint list.
Maintainer note: dev mode
If the Space is in dev mode, pushed commits do not trigger a rebuild. Do not disable dev mode or run a factory reboot on a live dev container; that tears down the running environment. Use the dev-mode workflow to apply changes, or restart the Space intentionally after the dev session is finished.