Skip to content

wolfwdavid/agentic-market-analyzer

Repository files navigation

Agentic Market Analyzer

A custom ReAct (Reasoning + Acting) agent for market microstructure analysis, built from scratch without LangChain, LlamaIndex, or any agent framework. The system uses a Qwen3-8B language model to reason about financial data, dispatch analytical tools, and synthesize findings into actionable insights.


Architecture

                       +-------------------+
                       |    Gradio UI      |
                       |  (app.py / ui/)   |
                       +--------+----------+
                                |
                                v
                    +-----------+-----------+
                    |  AgentOrchestrator    |
                    |  (ReAct Loop)         |
                    |                       |
                    |  1. Build prompt      |
                    |  2. Call model        |
                    |  3. Parse output      |
                    |  4. Dispatch tool     |
                    |  5. Loop or answer    |
                    +-----------+-----------+
                                |
              +-----------------+-----------------+
              |                                   |
    +---------v---------+               +---------v---------+
    |   ModelEngine     |               |   ToolRegistry    |
    |   (Qwen3-8B)      |               |                   |
    +-------------------+               |  fetch_market_data|
                                        |  compute_tech_ind |
                                        |  analyze_vol_prof |
                                        |  run_monte_carlo  |
                                        |  fetch_econ_data  |
                                        |  correlate_assets |
                                        |  generate_report  |
                                        +---------+---------+
                                                  |
                                        +---------v---------+
                                        |   Data Layer      |
                                        |                   |
                                        |  TieredCache      |
                                        |  RateLimiter      |
                                        |  ProviderRegistry |
                                        |  (yfinance, AV,   |
                                        |   FRED)           |
                                        +-------------------+

How the ReAct Agent Works

The agent follows a Thought-Action-Observation loop, limited to 8 iterations:

User: "Is AAPL overbought?"

Iteration 1:
  Thought:  I need RSI and Bollinger Bands for AAPL.
  Action:   compute_technical_indicators(ticker="AAPL", indicators=["rsi", "bollinger"])
  Observation: {"rsi": {"current": 72.3, "interpretation": "Overbought"}, ...}

Iteration 2:
  Thought:  RSI is 72.3 (above 70) and price is near the upper Bollinger Band.
            I have enough data to answer.
  Answer:   AAPL appears overbought. The 14-period RSI reads 72.3 (above the 70
            threshold) and the price is touching the upper Bollinger Band...

Key design decisions:

  • One tool call per iteration. This keeps the reasoning chain legible and makes error recovery straightforward.
  • Malformed output correction. If the model produces unparseable output, the orchestrator injects a correction message and lets the model retry.
  • Timeout enforcement. Each tool call has a configurable timeout (default 10 seconds) to prevent hangs from slow APIs.

Why No LangChain

This project intentionally avoids agent frameworks to demonstrate:

  1. Transparency. Every line of the ReAct loop is visible and debuggable. There is no "magic" behind an AgentExecutor -- the loop is 80 lines of straightforward Python in agent/orchestrator.py.

  2. Control. The prompt, parsing, and error recovery are all hand-tuned for the Qwen3 model family. Generic frameworks apply one-size-fits-all prompting that often produces worse results with smaller models.

  3. Minimal dependencies. The agent layer depends only on the standard library and the model/tools it already needs. No framework lock-in.

  4. Educational value. Reading this codebase should teach you exactly how tool-calling agents work, from prompt construction through output parsing to observation injection.


Tools

Tool Description Key Output
fetch_market_data OHLCV data via yfinance (Alpha Vantage fallback) Dates, OHLCV arrays, metadata
compute_technical_indicators RSI, MACD, Bollinger, VWAP, SMA, EMA, ATR Current values + interpretations
analyze_volume_profile Volume-at-price histogram POC, Value Area, HVN list
run_monte_carlo GBM price simulation (vectorized numpy) Percentile paths, VaR, expected range
fetch_economic_data FRED macro data (fed funds, CPI, unemployment, ...) Time series + trend analysis
correlate_assets Correlation matrix, rolling correlation, beta Matrix, rolling series, betas
generate_report Compile all results into structured Markdown Formatted report

Data Layer

Two-Tier Cache

  • Memory tier: Python dict for sub-millisecond lookups.
  • SQLite tier: Persistent storage that survives restarts.
  • TTL-based expiration with stale-data fallback when upstream sources fail.
  • Thread-safe via threading.Lock.

Rate Limiter

Token-bucket algorithm with per-API configuration:

API Rate Burst Cap
yfinance 2/s 5 2000/hr
Alpha Vantage 5/min 1 25/day
FRED 2/s 5 --

Provider Registry

Automatic failover chain: primary provider -> secondary -> stale cache -> error.


Installation

git clone <repo-url>
cd agentic-market-analyzer
pip install -r requirements.txt

Copy the environment template and add your API keys:

cp .env.example .env
# Edit .env with your keys

Running

Demo Mode (no GPU required)

python app.py

Uses mock model responses so you can explore the UI and tool integrations without a GPU.

Live Mode (GPU required)

python app.py --live

Loads Qwen3-8B-AWQ for real inference. Requires a CUDA-capable GPU with at least 8 GB VRAM.

Options

--port PORT    Server port (default: 7860)
--share        Create a public Gradio share link
--debug        Enable debug logging

Deployment on Hugging Face Spaces (ZeroGPU)

  1. Create a new Space with the Gradio SDK and ZeroGPU hardware.
  2. Push this repository to the Space.
  3. Set secrets in the Space settings:
    • FRED_API_KEY
    • ALPHA_VANTAGE_API_KEY
    • HF_TOKEN
  4. The app will load with demo_mode=False and use @spaces.GPU for on-demand GPU allocation.

Running Tests

pytest tests/ -v

The test suite covers:

  • Technical indicators: RSI bounds, MACD sign in trends, Bollinger band ordering, VWAP range, SMA/EMA convergence, ATR non-negativity.
  • Monte Carlo: Path start price, percentile ordering, median convergence, variance scaling, reproducibility.
  • Volume Profile: POC correctness, Value Area coverage, HVN threshold.
  • Agent Parser: XML tool calls, bare JSON, Action fallback, malformed JSON repair, answer detection, edge cases.
  • Orchestrator: Single tool call + answer, direct answer, max iteration handling, malformed output correction, tool failure reporting.
  • Cache: Set/get, TTL expiration, stale fallback, persistence, pruning, concurrent thread safety.
  • Rate Limiter: Burst capacity, depletion blocking, token recovery.

Error Handling Philosophy

  1. Fail gracefully. Every tool returns a ToolResult with explicit success/failure status. The agent sees errors as observations and can adapt its strategy.

  2. Retry with backoff. Network calls use exponential backoff (max 3 retries) before failing. This handles transient errors without overwhelming APIs.

  3. Stale over nothing. The cache will serve expired data when the upstream is unreachable. Slightly old market data is more useful than an error.

  4. Structured errors. Error messages always include context (which provider failed, what was attempted, what the user should try).


Project Structure

agentic-market-analyzer/
  app.py                          Gradio entry point
  requirements.txt                Pinned dependencies
  .env.example                    Environment variable template
  agent/
    orchestrator.py               ReAct loop engine
    events.py                     AgentEvent dataclasses
    parser.py                     Parse model output into tool calls
    prompt_builder.py             System prompt + tool schema formatting
    conversation.py               Message history management
  model/
    engine.py                     Model inference (Qwen3-8B or fallback)
    config.py                     Model settings
  tools/
    base.py                       BaseTool ABC, ToolResult, ToolRegistry
    market_data.py                fetch_market_data
    technical_indicators.py       compute_technical_indicators
    volume_profile.py             analyze_volume_profile
    monte_carlo.py                run_monte_carlo (GBM simulation)
    economic_data.py              fetch_economic_data (FRED)
    correlation.py                correlate_assets
    report.py                     generate_report
  data/
    cache.py                      Two-tier cache (memory + SQLite)
    rate_limiter.py               Token bucket rate limiter
    providers/
      base.py                     BaseDataProvider ABC
      yfinance_provider.py        yfinance wrapper
      alphavantage_provider.py    Alpha Vantage wrapper
      fred_provider.py            FRED wrapper
      registry.py                 Provider registry with failover
  ui/
    components.py                 Gradio UI builders
    formatters.py                 Format agent events for display
    charts.py                     Matplotlib chart generators
  tests/
    conftest.py                   Shared fixtures
    test_tools/                   Indicator, Monte Carlo, volume profile tests
    test_agent/                   Parser and orchestrator tests
    test_data/                    Cache and rate limiter tests

License

MIT

Releases

No releases published

Packages

 
 
 

Contributors

Languages