Built at the Damm × Engineering HUB Hackathon.
SmartBuy tells Damm's procurement team when to buy, how much, and why — for barley, aluminium, PET plastic, and energy. It combines live market signals, hedge fund positioning data, Cala's knowledge graph, and Damm's own internal price history into a single, actionable recommendation.
Damm buys tens of millions of euros of raw materials annually. Procurement decisions are made manually, based on intuition, with no systematic view of market signals. Mistimed purchases cost 1–3% per commodity cycle — at Damm's scale, that's hundreds of thousands of euros per year.
- Pulls live futures price data and computes momentum, volatility, and 52-week range signals
- Reads CFTC Commitments of Traders reports — "smart money" hedge fund positioning
- Queries Cala's knowledge graph (supply chain entities + market narrative) and classifies the results into quantified signals using OpenAI
- Runs a price forecast model on Damm's uploaded internal purchase history
- Aggregates all signals into a weighted score → BUY NOW / HEDGE / MONITOR / WAIT
- Outputs a specific, timestamped recommendation: buy X tonnes by [date] via [contract type], estimated Y% cost avoidance vs waiting
Four tabs:
| Tab | What it shows |
|---|---|
| 📊 Signal Analysis | Recommendation card, quantity/timing breakdown, signal dashboard (market + Cala), AI-written rationale |
| 🕸️ Market Drivers | Commodity impact network — what drives this material's price |
| 📂 Dataset & Forecast | Upload internal Damm price data → merge → 12-week barley price forecast |
| 🧪 Cala Entities (Beta) | Raw Cala knowledge graph entities, cluster visualisation |
Seven market signals, plus four Cala knowledge graph signals:
Market signals (from live data)
| Signal | Source | What it captures |
|---|---|---|
| Price Momentum (20d) | yfinance futures | Short-term price acceleration |
| Price Trend (60d) | yfinance futures | Medium-term directional drift |
| 52-Week Range | yfinance futures | Price position vs historical range |
| Volatility | yfinance futures | Annualised 20-day vol — hedge urgency |
| Energy Cost | NG=F natural gas | Smelting/production cost pressure (aluminium) |
| USD/FX | DX-Y.NYB USD index | EUR cost of USD-priced commodities |
| COT — Managed Money | CFTC disaggregated | Hedge fund net positioning |
Cala knowledge graph signals (via Cala API + OpenAI)
| Signal | What it captures |
|---|---|
| Supply Outlook | Production capacity, harvest, major suppliers |
| Demand Pressure | End-user competition, buyer concentration |
| Geopolitical / Weather Risk | Conflict, drought, logistics disruption |
| Market Structure | Consolidation, pricing power, seasonality |
Each signal returns a score from −1.0 (bearish) to +1.0 (bullish). Signals are weighted and aggregated into a single score that maps to the four actions.
Cala is used as a genuine upstream input to the recommendation engine — not as decorative text.
Cala QL queries (structured entity data)
→ companies.industry=malting
→ companies.industry=brewing.location=Europe
Cala Search API (market narrative)
→ "European malting barley supply harvest outlook 2025"
↓
OpenAI gpt-4o-mini
→ classifies Cala results into 4 scored signals
↓
Signal engine (alongside 7 market signals)
→ aggregate score → recommendation
The delta between the market-only score and the Cala-enhanced score is shown in the UI. When Cala data changes the recommendation (e.g. HEDGE → BUY NOW), the reason is surfaced explicitly.
| Endpoint | Method | Purpose |
|---|---|---|
POST /v1/knowledge/query |
Cala QL | Structured entity rows — supply chain players, capacities, market participants |
POST /v1/knowledge/search |
NL | Narrative market analysis with citations |
GET /v1/entities |
Search | Fuzzy entity lookup by name |
POST /v1/entities/{id} |
Fetch | Full entity profile — properties |
GET /v1/entities/{id}/introspection |
Schema | Field/relationship schema for an entity |
barley: companies.industry=malting
companies.industry=brewing.location=Europe
aluminium: companies.industry=aluminium
companies.industry=mining.commodity=bauxite
pet: companies.industry=petrochemicals
companies.industry=packaging
energy: companies.industry=natural_gas
companies.industry=energy.location=Europe
POST /v1/knowledge/queryandPOST /v1/knowledge/searchhave response times of 40–90 seconds (not a timeout — the endpoints are slow; timeout must be set to ≥90s)- Relationship traversal via POST body returns HTTP 500
- Numerical observations (price time series) return HTTP 500
- Entity types available: Company, Product, Plant, Organization, Person — no MacroIndicator or Future types found
When relationship traversal works, the architecture upgrades:
Barley → SOURCED_FROM → Ukraine (30%) → AFFECTED_BY → conflict
→ PURCHASED_BY → Boortmalt → SUPPLIES → AB InBev, Heineken
→ CORRELATED_WITH → Wheat (0.82)
Each relationship chain becomes a direct signal — no LLM middle step needed.
forecast/fit_predict.py — runs on Damm's uploaded internal purchase price history.
Strategy: blend three complementary trend estimates
50% × OLS trend (last 8 weeks) — captures recent momentum
30% × OLS trend (last 26 weeks) — captures seasonal half-year drift
20% × Holt double exponential — data-driven level + trend smoothing
Output: 12-week price forecast. The forecast slope feeds into the quantity advisor as an estimated cost avoidance figure (e.g. "waiting 3 days costs +0.8%").
| Source | Data | Used for |
|---|---|---|
| yfinance | Futures prices (ALI=F, NG=F, ZW=F, CL=F, DX-Y.NYB) | Price momentum, trend, volatility, FX signals |
| CFTC | Commitments of Traders (disaggregated futures) | Managed money net positioning signal |
| Cala.ai | Knowledge graph (entities, Cala QL, search) | Supply chain intelligence signals |
| Damm internal | Historical purchase prices (CSV upload) | Forecast model + internal price context |
| OpenAI gpt-4o | LLM reasoning | Natural language recommendation, historical analogue |
| OpenAI gpt-4o-mini | LLM classification | Cala data → scored signals |
cd smartbuy
pip install -r requirements.txt
cp .env.example .env
# Fill in .env:
# OPENROUTER_API_KEY=sk-... (OpenAI key or OpenRouter key)
# CALA_API_KEY=clsk_...
streamlit run app.pyOPENROUTER_API_KEY=your_openai_or_openrouter_key
CALA_API_KEY=your_cala_key
The app runs without CALA_API_KEY — Cala signals will be skipped and only the 7 market signals will be used.
smartbuy/
├── app.py # Streamlit app — 4 tabs
├── data/
│ ├── cala_client.py # Cala API client (all endpoints)
│ ├── cala_signal_extractor.py # Cala QL + Search → OpenAI → signals
│ ├── cot_data.py # CFTC COT data fetcher
│ └── market_data.py # yfinance price data + signal stats
├── engine/
│ ├── signals.py # Signal definitions + compute_signals()
│ ├── recommender.py # OpenAI-powered recommendation writer
│ ├── quantity_advisor.py # Buy %, hedge %, timing, contract type
│ └── commodity_graph.py # Plotly impact graph + Cala entity cluster
└── forecast/
├── fit_predict.py # Blended OLS + Holt forecast model
└── evaluate.py # Model evaluation utilities
| Commodity | Price proxy | Key signals |
|---|---|---|
| 🌾 Barley | ZW=F (wheat futures) | Harvest outlook, maltster capacity, brewer demand |
| 🔩 Aluminium | ALI=F | Energy costs, smelting production, LME positioning |
| 🧴 PET Plastic | CL=F (crude oil) | Feedstock (PTA/MEG), packaging demand, crude |
| ⚡ Energy | NG=F | Storage levels, LNG supply, TTF forward curve |
Damm dataset is confidential and used solely within the hackathon context.