research(equity): screener short 'fond/news neg ma prezzo su' (forward, edge non provato)
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>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user