mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 18:14:42 +02:00
Permet de suivre plusieurs comptes (courtiers/banques) côte à côte, avec vue par compte et agrégée. Ajoute le PEA BNP Paribas à côté de Revolut. - portfolio.yaml : nouveau format `accounts` (name/type/cash/positions par compte). Compat ascendante : ancien format (cash/positions à la racine) lu comme compte unique « Principal » - data.py : load_accounts(), base_currency(), portfolio_tickers() (union dédupliquée) - tracker.py : build_account / build_all (par compte + agrégat global) ; build() reste la vue globale (digest/report) ; report() multi-comptes - scanner/technical/alerts : helpers « portfolio » → union de tous les comptes - dashboard /api/portfolio : renvoie accounts[] (positions+agg par compte) + global - dashboard front : onglets de comptes (Tous / Revolut / BNP Paribas), KPI + positions + secteurs qui suivent le périmètre sélectionné, badge de type par ligne en vue Tous - BNP Paribas (PEA) saisi depuis la capture : AI/PAEEM/DCAM/PCEU/PSP5/BNP/ENGI/TTE .PA (tickers Yahoo vérifiés, cours conformes à la capture) Vérifié : totaux conformes à la capture BNP (6 395,49 € ; P&L -39 €), global 22 444 € ; bascule d'onglet OK au navigateur (Playwright) ; compile + JS + CLI report OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
78 lines
2.3 KiB
Python
78 lines
2.3 KiB
Python
"""Analyse technique : agrège les indicateurs en un état lisible par titre."""
|
|
from __future__ import annotations
|
|
|
|
import pandas as pd
|
|
|
|
from . import data, indicators as ind
|
|
|
|
|
|
def analyze(symbol: str, period: str = "1y") -> dict:
|
|
df = data.history(symbol, period=period)
|
|
close = df["Close"]
|
|
last = close.iloc[-1]
|
|
|
|
rsi = ind.rsi(close).iloc[-1]
|
|
macd = ind.macd(close)
|
|
macd_hist = macd["hist"].iloc[-1]
|
|
macd_cross = (
|
|
"haussier" if macd["macd"].iloc[-1] > macd["signal"].iloc[-1] else "baissier"
|
|
)
|
|
sma50 = ind.sma(close, 50).iloc[-1]
|
|
sma200 = ind.sma(close, 200).iloc[-1] if len(close) >= 200 else float("nan")
|
|
bb = ind.bollinger(close).iloc[-1]
|
|
atr = ind.atr(df).iloc[-1]
|
|
|
|
signals = []
|
|
if rsi >= 70:
|
|
signals.append("RSI en surachat (>70)")
|
|
elif rsi <= 30:
|
|
signals.append("RSI en survente (<30)")
|
|
if last > sma50:
|
|
signals.append("au-dessus MM50")
|
|
else:
|
|
signals.append("sous MM50")
|
|
if not pd.isna(sma200):
|
|
signals.append("tendance LT haussière" if last > sma200 else "tendance LT baissière")
|
|
signals.append(f"MACD {macd_cross}")
|
|
if last >= bb["upper"]:
|
|
signals.append("borne haute Bollinger")
|
|
elif last <= bb["lower"]:
|
|
signals.append("borne basse Bollinger")
|
|
|
|
return {
|
|
"ticker": symbol,
|
|
"cours": round(last, 2),
|
|
"RSI14": round(rsi, 1),
|
|
"MM50": round(sma50, 2),
|
|
"MM200": round(sma200, 2) if not pd.isna(sma200) else None,
|
|
"MACD_hist": round(macd_hist, 3),
|
|
"MACD": macd_cross,
|
|
"ATR14": round(atr, 2),
|
|
"vol_%": round(100 * atr / last, 2),
|
|
"signaux": signals,
|
|
}
|
|
|
|
|
|
def scan(symbols: list[str], period: str = "1y") -> pd.DataFrame:
|
|
rows = []
|
|
for s in symbols:
|
|
try:
|
|
a = analyze(s, period)
|
|
a["signaux"] = ", ".join(a["signaux"])
|
|
rows.append(a)
|
|
except Exception as e:
|
|
rows.append({"ticker": s, "signaux": f"erreur: {e}"})
|
|
return pd.DataFrame(rows)
|
|
|
|
|
|
def scan_portfolio(period: str = "1y") -> pd.DataFrame:
|
|
return scan(data.portfolio_tickers(), period)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
syms = sys.argv[1:]
|
|
df = scan(syms) if syms else scan_portfolio()
|
|
pd.set_option("display.max_colwidth", None, "display.width", 200)
|
|
print(df.to_string(index=False))
|