diff --git a/repair_challenge/carmignac_repair.py b/repair_challenge/carmignac_repair.py index 6fab02f..60df85d 100644 --- a/repair_challenge/carmignac_repair.py +++ b/repair_challenge/carmignac_repair.py @@ -15,17 +15,17 @@ import s3fs # ───────────────────────────────────────────── # PARAMÈTRES # ───────────────────────────────────────────── -ALPHA = 0.03 # tolérance réconciliation : 3% du stock à t -MIN_AUM_EUR = 5e6 # seuil filtrage étape 1 — 0 pour les heads de test +ALPHA = 0.03 # tolérance réconciliation : 5% du stock à t +MIN_AUM_EUR = 0 # seuil filtrage étape 1 — 0 pour les heads de test, 5e6 en prod MIN_JACCARD = 0.3 # seuil minimal similarité portefeuille pour chirurgie -SCORE_DROP_THRESHOLD = 0.1 # si score chute de >10% → candidat chirurgie +SCORE_DROP_THRESHOLD = 0.1 # si score chute de >10% → candidat chirurgie EXCLUDE_REGISTRAR = ["Off Distribution", "Private Clients"] # ───────────────────────────────────────────── # 1. CHARGEMENT # ───────────────────────────────────────────── -def load_data(aum_path, flows_path): +def load_data(): fs = s3fs.S3FileSystem( client_kwargs={'endpoint_url': 'https://'+'minio-simple.lab.groupe-genes.fr'}, key = os.environ["AWS_ACCESS_KEY_ID"], @@ -501,8 +501,11 @@ def run_surgery_pass(scores_history, errors_history, panel, monthly_flows, # ── ISIN du compte courant à t_curr (pour pré-filtre) ── isin_curr = reg_isin_at_date.get(reg_curr, {}).get(t_curr, set()) - # ── Candidats disponibles (non déjà mappés) ── - available = all_regs_in_panel - set(mapping_inv.keys()) - {reg_curr} + # ── Candidats disponibles ── + # On exclut les codes déjà mappés à un autre compte, + # mais reg_curr lui-même est un candidat valide (self-mapping : + # le compte existait déjà sous ce code à t-1, dormant ou partiel). + available = (all_regs_in_panel - set(mapping_inv.keys())) | {reg_curr} best_candidate = None best_score_after = score_prev_no_surgery # baseline = pas de chirurgie @@ -549,9 +552,12 @@ def run_surgery_pass(scores_history, errors_history, panel, monthly_flows, f"score {score_curr:.4f}→{best_score_after:.4f})") # Mise à jour mapping - if reg_curr in mapping_inv: - del mapping_inv[reg_curr] - mapping_inv[best_candidate] = reg_orig + # Si self-mapping (best_candidate == reg_curr), on ne touche pas + # mapping_inv car le code ne change pas — on met juste à jour le score. + if best_candidate != reg_curr: + if reg_curr in mapping_inv: + del mapping_inv[reg_curr] + mapping_inv[best_candidate] = reg_orig new_mapping[reg_orig] = best_candidate new_scores[reg_orig] = best_score_after else: @@ -617,13 +623,13 @@ def export_results(scores_history, mapping_history, surgery_log, all_months, out # ───────────────────────────────────────────── # 8. PIPELINE PRINCIPAL # ───────────────────────────────────────────── -def run_pipeline(aum_path, flows_path): +def run_pipeline(): print("=" * 60) print("CARMIGNAC — Pipeline de réparation des Registrar IDs") print("=" * 60) # Chargement - aum, flows = load_data(aum_path, flows_path) + aum, flows = load_data() # Étape 1 — Univers de référence aum_ref, weights, universe, t_ref = build_reference_universe(aum) @@ -673,7 +679,4 @@ def run_pipeline(aum_path, flows_path): if __name__ == "__main__": - df_scores, df_mapping, surgery_log, scores_history, mapping_history = run_pipeline( - "s3://projet-bdc-data/carmignac/AUM ENSAE V2 -20251105.csv", - "s3://projet-bdc-data/carmignac/Flows ENSAE V2 -20251105.csv" - ) + df_scores, df_mapping, surgery_log, scores_history, mapping_history = run_pipeline()