# XAUUSD News Sentiment Model See [`WORKFLOW_AND_USAGE.md`](WORKFLOW_AND_USAGE.md) for the complete integration, LLM fallback, deployment, and retraining workflow. This project trains a scikit-learn model to answer one specific question: > Does this news imply bullish, neutral, or bearish sentiment for XAUUSD? - `bullish`: supportive for gold versus the US dollar, so XAUUSD tends upward - `bearish`: negative for gold versus the US dollar, so XAUUSD tends downward - `neutral`: no clear directional implication or conflicting signals Neutral is retained because forcing ambiguous news into a bullish or bearish class creates false signals. The model uses headline and description word/character TF-IDF features with logistic regression. It also derives transparent domain features for explicit gold movement, US dollar direction, Treasury-yield direction, and hawkish/dovish policy language. Its numeric score is in `[-1, 1]`: ```text P(XAUUSD bullish) - P(XAUUSD bearish) ``` ## Training Labels Labels are DeepSeek's (`deepseek-v4-flash`) XAU score for each headline, not human or trader labels. The general article `score` is never used. For each row the trainer takes the first of: 1. the `xau_score` column (stored for every LLM-scored headline since 2026-09-18), 2. `score_by_currency_json["XAU"]` (older rows that mention gold), 3. `--extra-labels` JSONL keyed by headline hash (for example `eval/results/backfill_labels.jsonl`, DeepSeek labels for older macro headlines). By default only `score_source = 'llm'` rows are used, and test-feed rows (`feed.example`, `good.example`) are skipped. `--scope gold` limits training to rows with a JSON `XAU` key, which was the behaviour before 2026-09-22. The included model (trained 2026-09-22, `--scope all`) used 2,620 deduplicated headlines from Sep 8–22 (1,064 `xau_score`, 640 JSON, 933 backfill): - 909 bearish - 893 neutral - 818 bullish ## Setup ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install -r requirements.txt ``` ## Train ```bash python news_sentiment.py train --db newsdata.db \ --extra-labels eval/results/backfill_labels.jsonl ``` This creates: - `models/xauusd_sentiment.joblib`: fitted model and metadata - `models/xauusd_sentiment.metrics.json`: training and evaluation report ## Evaluation The included model's built-in holdouts: - Stratified random holdout: 69.3% accuracy and 0.689 macro-F1 - Newest chronological holdout (Sep 21–22): 66.6% accuracy and 0.659 macro-F1 These are lower than the 2026-09-17 model's 73.0% / 69.7%. That is because the test sets now include macro headlines (Fed, dollar, yields), which are harder. The fair comparison is on the same unseen rows: on Sep 18–22 the 09-17 model agreed with DeepSeek on 45.9% of headlines (36.2% on macro). This recipe, retrained daily, reached 62.8% (58.8% on macro). On the independent 78-headline golden set it scores 47/78; the 09-17 model scored 39/78 and DeepSeek 78/78. See `eval/RESULTS_2026-09-22.md`. The data still covers only 15 days of a single market regime. More labelled history across different regimes is the most valuable next improvement. ## Score News ```bash python news_sentiment.py score \ --headline "Gold gains as the dollar weakens" ``` Example output from the included model: ```json { "target": "XAUUSD", "label": "bullish", "score": 0.94748, "confidence": 0.971834, "probabilities": { "bearish": 0.024354, "neutral": 0.003812, "bullish": 0.971834 } } ``` For batch scoring, pass newline-delimited JSON. Each object can contain `headline` (or `title`/`text`) and an optional `description`: ```bash python news_sentiment.py score --input articles.jsonl python news_sentiment.py score --input - < articles.jsonl ``` The command writes JSONL to standard output and adds a `sentiment` object to each input record. ## Python API ```python from news_sentiment import load_model, score_news model = load_model("models/xauusd_sentiment.joblib") result = score_news( model, "Gold slides as Treasury yields rise", "A stronger US dollar adds pressure to bullion.", ) print(result) ``` This model estimates XAUUSD sentiment expressed by news. It does not predict the size or timing of a future price move and is not trading advice.