feat: translate all UI strings to English for international release
Complete French→English translation across all frontend pages and backend services — every label, button, header, empty state, toast, and nav item is now in English. Build verified clean (tsc + vite). No i18n library added; direct string replacement throughout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ def _max_similarity_vs_existing(candidate_kws: List[str], existing: List[Dict])
|
||||
return max((_jaccard(candidate_kws, p.get("keywords") or []) for p in existing), default=0.0)
|
||||
|
||||
|
||||
_EMBED_SIM_THRESHOLD = 0.75 # seuil cosinus pour considérer deux patterns comme doublons
|
||||
_EMBED_SIM_THRESHOLD = 0.75 # cosine threshold to consider two patterns as duplicates
|
||||
|
||||
|
||||
def _is_duplicate_pattern(
|
||||
@@ -59,8 +59,8 @@ def _is_duplicate_pattern(
|
||||
jaccard_threshold: float = 0.30,
|
||||
) -> bool:
|
||||
"""
|
||||
Retourne True si le candidat est trop similaire à un pattern existant.
|
||||
Essaie les embeddings cosinus (Sprint 4.3) avec fallback Jaccard.
|
||||
Returns True if the candidate is too similar to an existing pattern.
|
||||
Tries cosine embeddings (Sprint 4.3) with Jaccard fallback.
|
||||
"""
|
||||
if not existing:
|
||||
return False
|
||||
@@ -77,7 +77,7 @@ def _is_duplicate_pattern(
|
||||
api_key=api_key,
|
||||
candidate_id=candidate.get("id"),
|
||||
)
|
||||
if sim > 0: # embedding a fonctionné
|
||||
if sim > 0: # embedding worked
|
||||
return sim >= _EMBED_SIM_THRESHOLD
|
||||
except Exception as _emb_err:
|
||||
logger.debug(f"[Cycle] Embedding failed, fallback Jaccard: {_emb_err}")
|
||||
@@ -104,13 +104,13 @@ def _run_portfolio_monitor(risk: dict, cycle_id: str) -> dict:
|
||||
n_alerts = len(risk.get("alerts", []))
|
||||
|
||||
system_prompt = (
|
||||
"Tu es un risk manager spécialisé dans les portefeuilles d'options géopolitiques. "
|
||||
"Analyse l'état du portefeuille simulé ci-dessous et génère des recommandations concrètes. "
|
||||
"Réponds en JSON avec ce schéma exact: "
|
||||
'{"assessment": "<2 phrases max sur l\'état global>", '
|
||||
"You are a risk manager specializing in geopolitical options portfolios. "
|
||||
"Analyze the state of the simulated portfolio below and generate concrete recommendations. "
|
||||
"Respond in JSON with this exact schema: "
|
||||
'{"assessment": "<max 2 sentences on the overall state>", '
|
||||
'"actions": [{"priority": "high|medium", "type": "close_trade|rebalance|monitor", '
|
||||
'"trade_id": <int ou null>, "underlying": "<ticker>", "reason": "<raison courte>"}], '
|
||||
'"rebalance_suggestion": "<suggestion concrète de rééquilibrage en 1 phrase>"}'
|
||||
'"trade_id": <int or null>, "underlying": "<ticker>", "reason": "<short reason>"}], '
|
||||
'"rebalance_suggestion": "<concrete rebalancing suggestion in 1 sentence>"}'
|
||||
)
|
||||
user_prompt = context
|
||||
|
||||
|
||||
@@ -99,13 +99,13 @@ def get_portfolio_concentration(open_trades: List[Dict]) -> Dict[str, int]:
|
||||
def build_portfolio_context_block(open_trades: List[Dict], concentration: Dict[str, int]) -> str:
|
||||
"""Build a prompt section describing current portfolio for injection into AI prompts."""
|
||||
if not open_trades:
|
||||
return "\n## PORTEFEUILLE ACTUEL\nAucun trade en cours — portefeuille vide.\n"
|
||||
return "\n## CURRENT PORTFOLIO\nNo open trades — empty portfolio.\n"
|
||||
|
||||
total = len(open_trades)
|
||||
conc_sorted = sorted(concentration.items(), key=lambda x: -x[1])
|
||||
conc_str = " | ".join(f"{cls.upper()}: {n}" for cls, n in conc_sorted)
|
||||
|
||||
lines: List[str] = [f"Total: {total} trade(s) ouvert(s) | Concentration: {conc_str}", ""]
|
||||
lines: List[str] = [f"Total: {total} open trade(s) | Concentration: {conc_str}", ""]
|
||||
|
||||
for t in open_trades:
|
||||
sym = t["underlying"]
|
||||
@@ -118,27 +118,27 @@ def build_portfolio_context_block(open_trades: List[Dict], concentration: Dict[s
|
||||
pat = t.get("pattern_name", "")
|
||||
entry = t.get("entry_price")
|
||||
cur = t.get("current_price")
|
||||
ep_str = f"entrée {entry:.2f} → actuel {cur:.2f}" if entry and cur else ""
|
||||
ep_str = f"entry {entry:.2f} → current {cur:.2f}" if entry and cur else ""
|
||||
|
||||
line = f" • {sym} | {strat} [{cls}] | {held}j tenu / {rem}j restants | J-1: {m1d_str} | J-5: {m5d_str}"
|
||||
line = f" • {sym} | {strat} [{cls}] | {held}d held / {rem}d remaining | D-1: {m1d_str} | D-5: {m5d_str}"
|
||||
if ep_str:
|
||||
line += f" | {ep_str}"
|
||||
if pat:
|
||||
line += f" | thèse: «{pat}»"
|
||||
line += f" | thesis: «{pat}»"
|
||||
lines.append(line)
|
||||
|
||||
# Identify overweight classes
|
||||
overweight = [cls for cls, n in conc_sorted if n >= 3]
|
||||
ow_str = ", ".join(overweight) if overweight else "aucune"
|
||||
ow_str = ", ".join(overweight) if overweight else "none"
|
||||
|
||||
block = (
|
||||
"\n## PORTEFEUILLE ACTUEL — POSITIONS OUVERTES\n"
|
||||
"\n## CURRENT PORTFOLIO — OPEN POSITIONS\n"
|
||||
+ "\n".join(lines)
|
||||
+ f"\n\nClasses surpondérées (≥3 trades): {ow_str}\n"
|
||||
+ "\n⚠️ CONSIGNES IMPÉRATIVES (non négociables):\n"
|
||||
+ "1. NE PAS suggérer un nouveau trade sur un sous-jacent déjà en portefeuille — doublement interdit.\n"
|
||||
+ "2. Signaler explicitement dans 'rationale' si une suggestion CONTREDIT une position ouverte (signal de clôture potentiel).\n"
|
||||
+ "3. Éviter d'alourdir une classe surpondérée (≥3 trades) sauf catalyseur exceptionnel justifié.\n"
|
||||
+ "4. Un signal opposé à une position ouverte = opportunité de SORTIE à documenter, pas d'entrée inversée.\n"
|
||||
+ f"\n\nOverweight classes (≥3 trades): {ow_str}\n"
|
||||
+ "\n⚠️ MANDATORY RULES (non-negotiable):\n"
|
||||
+ "1. DO NOT suggest a new trade on an underlying already in the portfolio — strictly forbidden.\n"
|
||||
+ "2. Explicitly flag in 'rationale' if a suggestion CONTRADICTS an open position (potential exit signal).\n"
|
||||
+ "3. Avoid adding to an overweight class (≥3 trades) unless an exceptional justified catalyst exists.\n"
|
||||
+ "4. A signal opposing an open position = EXIT opportunity to document, not a reverse entry.\n"
|
||||
)
|
||||
return block
|
||||
|
||||
Reference in New Issue
Block a user