File size: 1,615 Bytes
41ba604
a4d1e62
 
41ba604
 
 
a4d1e62
41ba604
 
 
 
a4d1e62
 
41ba604
a4d1e62
41ba604
 
 
a4d1e62
41ba604
a4d1e62
 
 
 
41ba604
a4d1e62
 
 
 
 
41ba604
 
 
a4d1e62
 
 
 
 
 
 
41ba604
a4d1e62
 
 
 
41ba604
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"""
Light-weight predictor that fuses price momentum with model-based
financial sentiment.

Usage:
    from finance.quick_predict import predict_signal
    signal = predict_signal("AAPL", "Earnings were upbeat...")
    # -> "Buy", "Hold" or "Sell"
"""
from datetime import date, timedelta

import pandas as pd
import yfinance as yf

from finance.processor import get_classifier, _label_to_score


def _yesterday_pct(ticker: str) -> float:
    """Return the most recent day's % change."""
    today = date.today()
    data = yf.download(
        ticker, start=today - timedelta(days=5), end=today, progress=False
    )
    if len(data) < 2:
        return 0.0
    # Recent yfinance returns MultiIndex columns even for a single ticker.
    if isinstance(data.columns, pd.MultiIndex):
        data.columns = data.columns.get_level_values(0)
    close = float(data["Close"].iloc[-1])
    prev = float(data["Close"].iloc[-2])
    return (close - prev) / prev * 100.0


def _sentiment_score(text: str) -> float:
    """Signed sentiment score in [-1, 1] weighted by model confidence."""
    classifier = get_classifier()
    result = classifier(text, truncation=True, max_length=128)[0]
    return _label_to_score(result["label"]) * float(result["score"])


def predict_signal(ticker: str, news_text: str) -> str:
    """Buy / Hold / Sell based on momentum + sentiment."""
    pct = _yesterday_pct(ticker)
    emo = _sentiment_score(news_text)
    blended = 0.6 * pct + 40 * emo  # scale sentiment to ~equal weight

    if blended > 2.0:
        return "Buy"
    if blended < -2.0:
        return "Sell"
    return "Hold"