mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 04:44:43 +02:00
* feat(finlab): console Claude Code finance in-cluster + toolkit d'analyse Intègre finlab (ex-projet Projets/Finance) au lab comme une console Claude Code web spécialisée finance — l'esprit OpenAlice, mais c'est le vrai Claude Code sur l'abonnement (login persisté, pas d'API facturée), agentique, avec la boîte à outils finlab (Yahoo Finance) branchée en MCP. - tools/finlab/ : source finlab rapatriée + Dockerfile (Python 3.12 + Node + claude-code + ttyd) + persona workspace/CLAUDE.md + branchement MCP + entrypoint (seed du workspace no-clobber sur le PVC) - .github/workflows/build-finlab.yml : build GHCR funk-finlab + bump manifest (main) - k8s/apps/finlab/ : Deployment/Service/PVC/IngressRoute (finance.lab.local) + Middleware basicAuth (shell web protégé) ; PVC = HOME (login) + workspace - k8s/apps-of-apps/apps/finlab.yaml : Application ArgoCD - .mcp.json (racine) : outils finlab dans les sessions Claude Code du lab - admin/ia/finlab.md + READMEs + CLAUDE.md : doc + enregistrement Analyse/aide à la décision uniquement — aucun ordre réel (paper trading Alpaca fictif seul exécutable). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(finlab): ttyd absent des dépôts bookworm → binaire statique GitHub Le build amont échouait (`E: Package 'ttyd' has no installation candidate`) : ttyd n'est pas packagé dans Debian bookworm. On récupère le binaire statique (musl, pin TTYD_VERSION=1.7.7) depuis les releases GitHub. Build complet validé en local (podman) : ttyd 1.7.7, claude-code 2.1.195, import finlab + seed OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
"""CLI unifiée : python -m finlab.cli <commande>."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
|
|
import pandas as pd
|
|
|
|
|
|
def main() -> None:
|
|
pd.set_option("display.max_columns", None, "display.max_colwidth", None, "display.width", 240)
|
|
|
|
parser = argparse.ArgumentParser(prog="finlab", description="Boîte à outils d'analyse de marché")
|
|
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
|
|
sub.add_parser("portfolio", help="Suivi du portefeuille (valeur, P&L, secteurs)")
|
|
|
|
t = sub.add_parser("tech", help="Analyse technique")
|
|
t.add_argument("tickers", nargs="*", help="vide = tout le portefeuille")
|
|
t.add_argument("--period", default="1y")
|
|
|
|
f = sub.add_parser("fundamentals", help="Ratios fondamentaux")
|
|
f.add_argument("tickers", nargs="*", help="vide = tout le portefeuille")
|
|
|
|
s = sub.add_parser("scan", help="Scanner d'opportunités sur un thème")
|
|
s.add_argument("theme", nargs="?", default="all", help="thème de watchlists.yaml, 'all' ou 'portfolio'")
|
|
s.add_argument("--target", type=float, default=5.0, help="cible de perf hebdo en %%")
|
|
s.add_argument("--bullish", action="store_true", help="ne montrer que les setups haussiers")
|
|
|
|
d = sub.add_parser("digest", help="Digest compact (portefeuille + opportunités + alertes)")
|
|
d.add_argument("theme", nargs="?", default="all")
|
|
d.add_argument("--target", type=float, default=5.0)
|
|
|
|
a = sub.add_parser("alerts", help="Alertes déclenchées du jour")
|
|
a.add_argument("watch", nargs="?", default=None, help="thème, 'all' ou 'portfolio'")
|
|
|
|
p = sub.add_parser("plan", help="Plan de trade chiffré (entrée/stop/objectif/taille)")
|
|
p.add_argument("tickers", nargs="+")
|
|
p.add_argument("--capital", type=float, default=1427.0)
|
|
|
|
c = sub.add_parser("cache", help="Gestion du cache disque")
|
|
c.add_argument("action", choices=["clear", "info"], default="info", nargs="?")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.cmd == "portfolio":
|
|
from . import tracker
|
|
print(tracker.report())
|
|
|
|
elif args.cmd == "tech":
|
|
from . import technical
|
|
df = technical.scan(args.tickers, args.period) if args.tickers else technical.scan_portfolio(args.period)
|
|
print(df.to_string(index=False))
|
|
|
|
elif args.cmd == "fundamentals":
|
|
from . import fundamental
|
|
df = fundamental.compare(args.tickers) if args.tickers else fundamental.compare_portfolio()
|
|
print(df.to_string(index=False))
|
|
|
|
elif args.cmd == "scan":
|
|
from . import scanner
|
|
if args.theme == "portfolio":
|
|
df = scanner.scan_portfolio(args.target)
|
|
else:
|
|
df = scanner.scan_theme(args.theme, args.target, bullish_only=args.bullish)
|
|
print(df.to_string(index=False))
|
|
|
|
elif args.cmd == "digest":
|
|
from . import digest
|
|
path = digest.write(args.theme, args.target)
|
|
print(path.read_text(encoding="utf-8"))
|
|
print(f"\n[écrit dans {path}]")
|
|
|
|
elif args.cmd == "alerts":
|
|
from . import alerts
|
|
print(alerts.render(alerts.run(args.watch)))
|
|
|
|
elif args.cmd == "plan":
|
|
from . import plan
|
|
for tk in args.tickers:
|
|
try:
|
|
print(plan.render(plan.plan(tk, args.capital)))
|
|
except Exception as e:
|
|
print(f"{tk}: erreur {e}")
|
|
print()
|
|
|
|
elif args.cmd == "cache":
|
|
from . import data
|
|
if args.action == "clear":
|
|
n = data.clear_cache()
|
|
print(f"Cache vidé : {n} fichier(s).")
|
|
else:
|
|
files = list(data.CACHE_DIR.glob("*.pkl")) if data.CACHE_DIR.exists() else []
|
|
print(f"{len(files)} fichier(s) en cache dans {data.CACHE_DIR}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|