Uname:Linux antigravity-cli 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64

Base Dir : /var/www/moonbloom

User : wp-moonbloom


403WebShell
403Webshell
Server IP : 85.155.190.233  /  Your IP : 216.73.216.103
Web Server : nginx/1.24.0
System : Linux antigravity-cli 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64
User : wp-moonbloom ( 1001)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /proc/2398464/root/opt/Gemini_Swarm_Core/scripts/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /proc/2398464/root/opt/Gemini_Swarm_Core/scripts/fetch_portfolio_data.py
#!/usr/bin/env python3
"""
Fetches real-time portfolio quotes from Yahoo Finance and prints a Markdown
snapshot block to stdout. Python3 stdlib only (no third-party deps).

Usage:
    python3 fetch_portfolio_data.py

Reads ../portfolio.json (relative to this script's directory).

Output: a Markdown block with a portfolio summary table, meant to be
injected verbatim into the market-scout prompts / report.

On global failure (FX dead AND all quotes dead) prints "(котировки недоступны)"
and exits 0 (never fails the pipeline).
"""
import datetime
import json
import os
import sys
import time
import urllib.error
import urllib.request

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PORTFOLIO_PATH = os.path.join(SCRIPT_DIR, "..", "portfolio.json")

YAHOO_URL = "https://query1.finance.yahoo.com/v8/finance/chart/{symbol}?interval=1d&range=7d"
FX_URL = "https://api.frankfurter.dev/v1/latest?base=USD&symbols=EUR"
ALPHA_VANTAGE_API_KEY = os.environ.get("ALPHA_VANTAGE_API_KEY", "JDJ4CE5RVBGSGDSS")
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
TIMEOUT = 3
DELAY_BETWEEN_CALLS = 0.1


def _get_json(url: str):
    req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
    with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
        return json.loads(resp.read().decode("utf-8"))


def fetch_fx_rate() -> float:
    """USD -> EUR rate. Raises on failure."""
    data = _get_json(FX_URL)
    return float(data["rates"]["EUR"])


def fetch_quote(symbol: str):
    """
    Returns dict: {price, week_change_pct, fifty_two_high, fifty_two_low}
    Raises on failure (caller should catch per-ticker).
    """
    url = YAHOO_URL.format(symbol=symbol)
    data = _get_json(url)

    result = data["chart"]["result"][0]
    meta = result["meta"]
    price = meta.get("regularMarketPrice")
    if price is None:
        raise ValueError(f"no regularMarketPrice for {symbol}")

    fifty_two_high = meta.get("fiftyTwoWeekHigh")
    fifty_two_low = meta.get("fiftyTwoWeekLow")

    week_change_pct = None
    try:
        closes = result["indicators"]["quote"][0]["close"]
        valid_closes = [c for c in closes if c is not None]
        if len(valid_closes) >= 2:
            first_close = valid_closes[0]
            last_close = valid_closes[-1]
            if first_close:
                week_change_pct = (last_close - first_close) / first_close * 100.0
    except (KeyError, IndexError, TypeError, ZeroDivisionError):
        week_change_pct = None

    return {
        "price": float(price),
        "week_change_pct": week_change_pct,
        "fifty_two_high": fifty_two_high,
        "fifty_two_low": fifty_two_low,
    }


def fmt_money_eur(value: float) -> str:
    return f"€{value:,.0f}"


def fmt_pct(value: float) -> str:
    arrow = "▲" if value >= 0 else "▼"
    sign = "+" if value >= 0 else ""
    return f"{arrow} {sign}{value:.1f}%"


def fmt_signed_pct(value: float) -> str:
    sign = "+" if value >= 0 else ""
    return f"{sign}{value:.1f}%"


def load_portfolio():
    with open(PORTFOLIO_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def main():
    try:
        portfolio = load_portfolio()
    except Exception as e:
        print(f"(котировки недоступны)")
        print(f"# fetch_portfolio_data: failed to load portfolio.json: {e}", file=sys.stderr)
        return 0

    positions = portfolio.get("positions", [])

    fx_rate = None
    try:
        fx_rate = fetch_fx_rate()
    except Exception as e:
        print(f"# fetch_portfolio_data: FX fetch failed: {e}", file=sys.stderr)

    rows = []
    any_quote_ok = False

    for i, pos in enumerate(positions):
        ticker = pos["ticker"]
        yahoo_symbol = pos["yahoo"]
        shares = pos["shares"]
        is_eur = pos.get("eur", False)

        if i > 0:
            time.sleep(DELAY_BETWEEN_CALLS)

        try:
            quote = fetch_quote(yahoo_symbol)
            price = quote["price"]
            week_change_pct = quote["week_change_pct"]

            if is_eur:
                position_eur = price * shares
            else:
                if fx_rate is None:
                    raise ValueError("FX rate unavailable, cannot convert to EUR")
                position_eur = price * shares * fx_rate

            any_quote_ok = True
            rows.append({
                "ticker": ticker,
                "price": price,
                "is_eur": is_eur,
                "week_change_pct": week_change_pct,
                "position_eur": position_eur,
                "ok": True,
            })
        except Exception as e:
            print(f"# fetch_portfolio_data: quote failed for {ticker} ({yahoo_symbol}): {e}", file=sys.stderr)
            rows.append({"ticker": ticker, "ok": False})

    if not any_quote_ok or fx_rate is None:
        print("(котировки недоступны)")
        return 0

    total_eur = sum(r["position_eur"] for r in rows if r["ok"])

    # Weighted portfolio week change (only over positions with a known week change).
    weighted_sum = 0.0
    weighted_value = 0.0
    for r in rows:
        if r["ok"] and r.get("week_change_pct") is not None:
            weighted_sum += r["week_change_pct"] * r["position_eur"]
            weighted_value += r["position_eur"]
    portfolio_week_change = (weighted_sum / weighted_value) if weighted_value > 0 else 0.0

    # Sort rows by position value desc (failed rows go last).
    ok_rows = [r for r in rows if r["ok"]]
    failed_rows = [r for r in rows if not r["ok"]]
    ok_rows.sort(key=lambda r: r["position_eur"], reverse=True)

    # Top gainer / loser by week %.
    with_change = [r for r in ok_rows if r.get("week_change_pct") is not None]
    top_gainer = max(with_change, key=lambda r: r["week_change_pct"]) if with_change else None
    top_loser = min(with_change, key=lambda r: r["week_change_pct"]) if with_change else None

    header_arrow = "▲" if portfolio_week_change >= 0 else "▼"
    header_sign = "+" if portfolio_week_change >= 0 else ""

    lines = []
    lines.append(
        f"## 💼 Портфель: {fmt_money_eur(total_eur)} "
        f"({header_arrow} {header_sign}{portfolio_week_change:.1f}% за неделю)"
    )
    lines.append("")
    lines.append("| Тикер | Цена | Δ неделя | Позиция € | Доля % |")
    lines.append("|-------|------|----------|-----------|--------|")

    for r in ok_rows:
        price_str = f"€{r['price']:.2f}" if r["is_eur"] else f"${r['price']:.2f}"
        change_str = fmt_pct(r["week_change_pct"]) if r["week_change_pct"] is not None else "н/д"
        position_str = fmt_money_eur(r["position_eur"])
        share_pct = (r["position_eur"] / total_eur * 100.0) if total_eur > 0 else 0.0
        share_str = f"{share_pct:.1f}%"
        lines.append(f"| {r['ticker']} | {price_str} | {change_str} | {position_str} | {share_str} |")

    for r in failed_rows:
        lines.append(f"| {r['ticker']} | н/д | н/д | н/д | н/д |")

    gainer_str = f"{top_gainer['ticker']} {fmt_signed_pct(top_gainer['week_change_pct'])}" if top_gainer else "н/д"
    loser_str = f"{top_loser['ticker']} {fmt_signed_pct(top_loser['week_change_pct'])}" if top_loser else "н/д"
    today = datetime.date.today().isoformat()

    lines.append(
        f"Топ-гейнер: {gainer_str} · Топ-лузер: {loser_str} · "
        f"USD/EUR: {fx_rate:.4f} · Данные: Yahoo Finance, {today}"
    )

    print("\n".join(lines))
    return 0


if __name__ == "__main__":
    sys.exit(main())

Youez - 2016 - github.com/yon3zu
LinuXploit