GMI puts AI to work on real stock data in two ways. First, machine learning (ML.NET) is baked into the stock statistics — it forecasts each company’s revenue, debt, cash flow, and equity, flags anomalies, and rolls everything into a letter-grade health score. Second, Claude lets you ask plain-language questions and get answers about your uploaded report files and the AI report dashboard. This page shows how both are built.
The big picture
A report starts deterministic and ends conversational. Gmi.ReportEngine.Console
orchestrates jobs (four concurrent) and spawns Gmi.ReportGeneratorAlpha.Console per
report. That generator pulls fundamentals from Financial Modeling Prep, runs the ML.NET
forecasting layer, and writes Excel and JSON outputs to Azure Blob Storage. Only afterward,
on demand, does Claude enter — analyzing the documents a user chooses, under a hard cost cap.
flowchart TD
ENGINE["Gmi.ReportEngine.Console -- orchestrator, 4 concurrent"] --> ALPHA["Gmi.ReportGeneratorAlpha.Console -- one per report"]
ALPHA -->|"13 calls per symbol, 3000 per min token bucket"| FMP["Financial Modeling Prep API"]
FMP --> ALPHA
ALPHA --> ML["ML.NET Forecasting Service"]
ML --> OUT["ml-forecasts.xlsx plus JSON companions"]
ALPHA --> BLOB["Azure Blob Storage"]
OUT --> BLOB
BLOB --> NOTIFY["Email and SMS notify user"]
BLOB -.->|"on demand, user selected files"| CLAUDE["Claude analysis layer"]
The ML.NET engine — the deterministic grade
The grade is not an LLM opinion. ML.NET (Microsoft.ML 5.0 with
Microsoft.ML.TimeSeries, seeded at 0 for reproducibility) drives every number that
has to be repeatable. Singular Spectrum Analysis (SSA) forecasts revenue, debt,
cash flow, and shareholder equity four quarters out with 95% confidence intervals; if SSA can’t
train on the available history, it falls back to linear regression (1.96 × standard error).
A separate SDCA multiclass classifier turns daily prices plus RSI, MACD, and
volatility into a Bullish / Neutral / Bearish sentiment signal, and SSA spike detection flags
anomalies in debt-to-equity. Those feed a composite 0–100 health score and the familiar
A+ to F rating.
flowchart TD
Q["Quarterly financials"] --> SSA["SSA forecast -- 4 quarters, 95 percent CI"]
SSA -->|"if training fails"| LR["Linear regression fallback -- 1.96 x std error"]
SSA --> F["Revenue, Debt, Cash Flow, Equity forecasts"]
LR --> F
P["Daily prices plus RSI, MACD, volatility"] --> SDCA["SDCA multiclass classifier"]
SDCA --> SENT["Bullish, Neutral or Bearish"]
DE["Debt-to-equity ratios"] --> SPIKE["SSA spike anomaly detection"]
F --> HS["Composite health score, 0 to 100"]
SENT --> HS
SPIKE --> HS
HS --> RATING["Green above 70, Yellow 40 to 70, Red below 40"]
The Claude layer — language and analysis
Where judgment and language help, Claude takes over. A user right-clicks any file from a finished
report — PDF, DOCX, XLSX, or an image (handled by the vision model) — and asks for
analysis. Cost transparency is the whole point: the API returns a token estimate first, the job is
capped at $45, and nothing runs until the user approves the spend. The background
Gmi.ClaudeAnalysis.Console then works the queue: polling every 15 seconds, a batch of
5, three concurrent calls behind a SemaphoreSlim, three retries with exponential
backoff (5s, 15s, 45s), and graceful 429 handling that honors Anthropic’s
Retry-After. Each model can be chosen per job — Haiku for cost-optimized passes,
Sonnet as the analysis default, Opus for the deepest reasoning — and results are saved as
DOCX and HTML alongside the original file.
sequenceDiagram
participant U as User
participant Web as Gmi.ClientWeb.Blazor
participant Api as Gmi.Api.AzureFunctions
participant Db as MySQL job queue
participant Svc as Gmi.ClaudeAnalysis.Console
participant Cl as Anthropic API
participant Blob as Azure Blob Storage
U->>Web: Select report files to analyze
Web->>Api: POST claude-analysis estimate
Api-->>Web: Token and cost estimate, 45 dollar cap
U->>Web: Approve spend
Web->>Api: POST claude-analysis submit
Api->>Db: Create job plus per-file requests
loop Poll 15s, batch of 5, 3 concurrent
Svc->>Db: Claim pending requests
Svc->>Cl: Analyze file, max 8192 tokens
Cl-->>Svc: Structured analysis
Svc->>Blob: Save DOCX and HTML
end
Note over Svc,Cl: 3 retries with backoff 5s 15s 45s, and 429 responses honor Retry-After
Svc->>Db: Record input and output tokens and cost
Svc-->>U: Notify complete
The same estimate-approve-meter pattern powers the conversational “Ask Claude” flow below: a token cost and the chosen model are shown up front, the job runs server-side, and Claude returns a structured result grounded in the report’s numbers.
Cost, safety, and billing
Every Claude call is metered server-side. Token usage is written to a ledger
(ClaudeAnalysisJob and ClaudeAnalysisRequest track input tokens, output
tokens, and actual cost), priced with a fixed markup, and reconciled against payment by
Gmi.BillingEngine.Console and Stripe webhooks guarded by idempotency keys so a retry
never double-charges. On the safety side, a strict system prompt keeps Claude in bounds — it
is explicitly not an investment advisor, never gives personalized buy/sell recommendations, and
appends a compliance disclaimer to every response. The deterministic ML.NET grade, not the model,
remains the source of truth.
flowchart TD
EST["Server-side token estimate"] --> CAP["Enforce 45 dollar per-job cap"]
CAP --> CALL["Metered Claude calls"]
CALL --> LEDGER["Usage ledger -- input tokens, output tokens, actual cost"]
LEDGER --> BILL["Gmi.BillingEngine.Console"]
BILL --> STRIPE["Stripe"]
STRIPE -->|"webhook with idempotency key"| RECON["Reconcile payment and usage -- no double charge"]
The components behind it
Gmi.ReportEngine.Console
Job orchestrator; claims and runs report jobs four at a time.
Gmi.ReportGeneratorAlpha.Console
Per-report worker: FMP download, ML.NET forecasting, Excel/JSON output.
Gmi.ClaudeAnalysis.Console
Queue-driven Claude file analysis with batching, retries, and the $45 cap.
Gmi.ReportComparison.Console
Claude-written narrative diff of two report runs, rendered to DOCX.
Gmi.BillingEngine.Console
Queue-driven billing that reconciles usage with Stripe, idempotently.
Gmi.Api.AzureFunctions
The .NET-isolated REST surface, including the estimate and submit endpoints.
How we’d apply this to your systems
This is the pattern we bring to client work: deterministic code where repeatability matters, AI where language and judgment help, and a metering-and-approval layer so AI spend is always visible and bounded. ML.NET owns the grade; Claude explains it. Neither one is asked to do the other’s job — and that separation is exactly what makes the system safe to put in front of paying users and auditors alike.
Putting AI into a product or platform?
Leopard Data architects AI/ML integrations that ship to production — with the cost controls and governance to match.





