a158d0e2ae
Goal chiarito: short su titoli con fondamentali/notizie negativi ma prezzo in salita. Gate dati (v2.0.0): NON backtestabile -> fondamentali da rete = snapshot correnti, applicarli a prezzi passati = look-ahead. Come la term-structure: solo screener forward. Costruito screener da dati di rete tokenless: fondamentali strutturati Yahoo (quoteSummary via crumb: recommendationMean/surprise/revGrowth/sell-skew) + sentiment headline + momentum 1m/3m. SHORT cand = (fond/news neg) AND prezzo su. Run live oggi: nessun candidato (mercato in flessione ampia -> gamba 'prezzo su' non scatta). Intuizione chiave: shortare la FORZA combatte momentum + PEAD; il rialzo 'malgrado' brutte notizie spesso prezza info che i fondamentali trailing non hanno -> e' la versione contrarian/rischiosa dell'anomalia. La versione pulita = short su fondamentali deboli quando il prezzo CONFERMA (scende), non quando diverge. Eseguibilita': borrow/squeeze/perdita illimitata/PDT 5k/IB instabile/00 -> non deployabile. - scripts/research/eq_fundnews_short.py (+ forward log data/raw/fundnews_short_screen.parquet) - tests/test_eq_fundnews_short.py (scoring puro offline) - docs/diary/2026-06-26-equity-fundnews-short.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.5 KiB
Python
41 lines
1.5 KiB
Python
"""Lock 2026-06-26 (fond/news short screener): scoring puro testabile offline. La strategia NON e'
|
|
backtestabile (fondamentali snapshot = look-ahead) -> deliverable = screener forward + verdetto.
|
|
Diario docs/diary/2026-06-26-equity-fundnews-short.md."""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
|
|
|
from eq_fundnews_short import fund_neg_score, headline_sentiment, UNIVERSE # type: ignore
|
|
|
|
|
|
def test_fund_neg_high_for_bad_company():
|
|
"""recMean verso sell + surprise negative + ricavi in calo + analisti a sell -> score alto."""
|
|
s = fund_neg_score(rec_mean=4.5, surp=[-0.10, -0.05], rev_g=-0.08, sell_skew=0.5)
|
|
assert s > 0.7
|
|
|
|
|
|
def test_fund_neg_low_for_healthy_company():
|
|
"""recMean buy + surprise positive + ricavi su + nessun sell -> score basso."""
|
|
s = fund_neg_score(rec_mean=1.5, surp=[0.10, 0.06], rev_g=0.20, sell_skew=0.0)
|
|
assert s < 0.2
|
|
|
|
|
|
def test_fund_neg_none_when_no_data():
|
|
assert fund_neg_score(None, None, None, None) is None
|
|
|
|
|
|
def test_headline_sentiment_directions():
|
|
neg, n_, p_ = headline_sentiment(["Stock plunges as company warns and cuts guidance"])
|
|
pos, _, _ = headline_sentiment(["Shares surge as earnings beat, analysts upgrade"])
|
|
neutral, _, _ = headline_sentiment(["Company holds annual shareholder meeting"])
|
|
assert neg > 0.6 and pos < 0.4 and neutral == 0.0
|
|
|
|
|
|
def test_universe_nonempty():
|
|
assert len(UNIVERSE) >= 10
|