From 42f2baf4ef4de415b6dd4b61e6705e10c80be27f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 15:57:54 +0000 Subject: [PATCH] =?UTF-8?q?Retirer=20la=20s=C3=A9ance=20du=206=20ao=C3=BBt?= =?UTF-8?q?=20:=20c'=C3=A9tait=20celle=20du=207,=20republi=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'onglet Marché affichait 0 % partout. La cause est mon versement de la veille : le 6 août « rattrapé » par rapatriement était la séance du 7 au titre près — 47 clôtures identiques sur 47, et 47 volumes identiques sur 47. Deux séances consécutives ne partagent pas les volumes de toutes les valeurs. La variation valait donc 1900/1900 − 1 pour chacune. Les 47 lignes sont retirées. Les variations reviennent : médiane +0,09 %, de −6,42 % à +7,50 %, et huit valeurs à zéro sur quarante-sept — des titres qui n'ont pas bougé, ce qui est le cas normal. L'archive retrouve son trou au 6 août. C'est le bon état : un trou connu est honnête, une fausse séance ne l'est pas, et `brvm veille` le compte de nouveau parmi les vingt-deux. LE PROJET AVAIT DÉJÀ CE CONTRÔLE, ET JE NE L'AI PAS APPLIQUÉ. `sikafinance.seances_repetees` porte le critère, mesuré sur ce marché : « deux échanges égaux au franc près n'existent pas, c'est la même transaction republiée ». Il valait pour une valeur à la fois ; à l'échelle d'une séance entière, un volume ET un cours communs à toutes les valeurs deux jours de suite sont impossibles. Le versement le vérifie désormais avant d'écrire. Les volumes nuls sont exclus du décompte — une valeur qui n'échange pas deux jours de suite est le cas normal, et les compter ferait crier le contrôle sur les séances creuses. Sous cinq valeurs échangées il s'abstient : la coïncidence redevient possible et il ne prouverait rien. Éprouvé sur la vraie donnée, pas seulement sur des cas fabriqués : rejoué contre l'incrément exact que j'avais versé, il refuse — « 47/47 valeurs au volume ET au cours identiques ». Trois cas s'ajoutent au harnais, dont les deux qui bornent : une séance dont les volumes diffèrent passe, des volumes nuls répétés passent aussi. CE QUE JE NE SAIS TOUJOURS PAS : pourquoi l'API d'historique a rendu la séance du 7 pour une demande du 6. Le 7 août est la fête nationale ivoirienne ; il se peut que l'une des deux dates ne soit pas une séance du tout. Le rapatriement du 6 août reste donc à reprendre, et à examiner avant d'être versé — le contrôle refusera tout seul si la réponse est encore celle-là. 252 tests passent. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Mu7CkW6727S9ZyRbiYyRk9 --- .github/workflows/versement.yml | 38 ++++++++++++++++++++++++++ README.md | 14 ++++++++-- data/cours.csv | 47 --------------------------------- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/.github/workflows/versement.yml b/.github/workflows/versement.yml index d9bd91d..59d3c92 100644 --- a/.github/workflows/versement.yml +++ b/.github/workflows/versement.yml @@ -341,6 +341,44 @@ jobs: refus.append(f"{len(absurdes)} clôtures nulles ou négatives : " + ", ".join(absurdes[:5])) + # LA SÉANCE REPUBLIÉE, ET J'EN AI VERSÉ UNE. Le 6 août 2026 a été + # « rattrapé » par rapatriement, et les 47 lignes obtenues étaient + # la séance du 7 au titre près : mêmes clôtures, mêmes volumes. + # L'app a affiché 0 % partout, et c'est l'utilisateur qui l'a vu. + # + # `sikafinance.seances_repetees` porte déjà le critère, mesuré sur + # ce marché : « deux échanges égaux au franc près n'existent pas, + # c'est la même transaction republiée ». Il valait pour une valeur + # à la fois ; à l'échelle d'une séance entière, un volume commun à + # toutes les valeurs deux jours de suite est impossible. + # + # Les volumes NULS se répètent normalement — une valeur qui + # n'échange pas deux jours de suite — et sont donc exclus du + # décompte, comme dans le contrôle d'origine. + derniers_volumes = {} + for ligne in archive: + champs = ligne.split(",") + if champs[6]: + derniers_volumes[champs[1]] = (champs[6], champs[5]) + par_seance = {} + for ligne in incr: + champs = ligne.split(",") + if champs[6] and float(champs[6]) > 0: + par_seance.setdefault(champs[0], []).append( + (champs[1], champs[6], champs[5])) + for date, valeurs in sorted(par_seance.items()): + # Sous cinq valeurs échangées, la coïncidence redevient + # possible et le contrôle ne prouverait rien. + if len(valeurs) < 5: + continue + repetees = sum(1 for t, v, c in valeurs + if derniers_volumes.get(t) == (v, c)) + if repetees >= 0.9 * len(valeurs): + refus.append( + f"séance {date} : {repetees}/{len(valeurs)} valeurs au " + "volume ET au cours identiques à leur séance " + "précédente — séance republiée, pas une séance neuve") + # LA LIMITE DE ±7,5 % EST DÉCRITE, JAMAIS OPPOSÉE, et ce n'est pas # de la mollesse — c'est ce que le projet a MESURÉ. Sur l'archive, # ce contrôle lève 356 alertes dont l'immense majorité est diff --git a/README.md b/README.md index 0b9349f..a53778d 100644 --- a/README.md +++ b/README.md @@ -454,8 +454,18 @@ de nature. **Ce que le robot vérifie avant d'écrire**, parce que personne ne regardera : en-têtes identiques, aucune ligne déjà présente, aucune séance antérieure à l'archive — ce garde-fou a déjà empêché de -ressusciter 38 séances fantômes — tri préservé, et aucune clôture nulle -ou négative. Un seul contrôle qui tombe annule le versement. +ressusciter 38 séances fantômes — tri préservé, aucune clôture nulle ou +négative, et **aucune séance republiée**. Un seul contrôle qui tombe +annule le versement. + +La séance republiée mérite son nom : le 6 août 2026, rattrapé par +rapatriement, est revenu avec les 47 cours et les 47 volumes du 7 — la +même séance sous une autre date. Versée, elle a mis toutes les variations +de l'onglet **Marché** à 0 %, et c'est un lecteur qui l'a vu. Le critère +vient de `sikafinance.seances_repetees`, mesuré sur ce marché : « deux +échanges égaux au franc près n'existent pas, c'est la même transaction +republiée ». Les volumes nuls, eux, se répètent normalement et ne +comptent pas. **Ce qu'il ne refuse pas : la limite de ±7,5 %.** La tentation est d'en faire un invariant dur ; mesurée sur l'archive, elle lève 356 alertes dont diff --git a/data/cours.csv b/data/cours.csv index e81b214..f2b3b93 100644 --- a/data/cours.csv +++ b/data/cours.csv @@ -113053,53 +113053,6 @@ date,ticker,ouverture,haut,bas,cloture,volume_titres,volume_fcfa 2026-08-05,TTLS,3500.0,,,3630.0,681.0,2472030.0 2026-08-05,UNLC,51055.0,,,54595.0,33.0,1801635.0 2026-08-05,UNXC,1835.0,,,1895.0,5591.0,10594945.0 -2026-08-06,ABJC,3000.0,3010.0,3000.0,3010.0,499.0,1499495.0 -2026-08-06,BICB,7500.0,7595.0,7500.0,7595.0,1303.0,9834392.5 -2026-08-06,BICC,29000.0,29100.0,29000.0,29100.0,148.0,4299400.0 -2026-08-06,BNBC,1915.0,1915.0,1900.0,1900.0,1386.0,2643795.0 -2026-08-06,BOAB,8700.0,8710.0,8700.0,8710.0,4887.0,42541335.0 -2026-08-06,BOABF,7500.0,7500.0,7230.0,7230.0,14488.0,106704120.0 -2026-08-06,BOAC,11600.0,11600.0,11600.0,11600.0,3757.0,43581200.0 -2026-08-06,BOAM,5585.0,5590.0,5585.0,5590.0,849.0,4743787.5 -2026-08-06,BOAN,5075.0,5200.0,5075.0,5200.0,4425.0,22733437.5 -2026-08-06,BOAS,7600.0,7710.0,7600.0,7710.0,1166.0,8925730.0 -2026-08-06,CABC,3600.0,3600.0,3550.0,3550.0,782.0,2795650.0 -2026-08-06,CBIBF,28305.0,28305.0,28305.0,28305.0,523.0,14803515.0 -2026-08-06,CFAC,1690.0,1700.0,1690.0,1700.0,502.0,850890.0 -2026-08-06,CIEC,5000.0,5020.0,5000.0,5020.0,2192.0,10981920.0 -2026-08-06,ECOC,16000.0,16200.0,16000.0,16200.0,562.0,9048200.0 -2026-08-06,ETIT,64.0,65.0,64.0,65.0,428618.0,27645861.0 -2026-08-06,FTSC,1990.0,2000.0,1990.0,2000.0,10905.0,21755475.0 -2026-08-06,LNBB,4300.0,4300.0,4300.0,4300.0,4261.0,18322300.0 -2026-08-06,NEIC,2260.0,2260.0,2115.0,2115.0,878.0,1920625.0 -2026-08-06,NSBC,23550.0,23550.0,23550.0,23550.0,309.0,7276950.0 -2026-08-06,NTLC,16750.0,16750.0,16595.0,16595.0,384.0,6402240.0 -2026-08-06,ONTBF,2940.0,2940.0,2940.0,2940.0,1693.0,4977420.0 -2026-08-06,ORAC,17175.0,17175.0,17175.0,17175.0,773.0,13276275.0 -2026-08-06,ORGT,3140.0,3140.0,3125.0,3125.0,1235.0,3868637.5 -2026-08-06,PALC,8970.0,8980.0,8970.0,8980.0,836.0,7503100.0 -2026-08-06,PRSC,4305.0,4480.0,4305.0,4480.0,464.0,2038120.0 -2026-08-06,SAFC,5000.0,5250.0,5000.0,5250.0,3742.0,19177750.0 -2026-08-06,SCRC,3575.0,3575.0,3550.0,3550.0,4769.0,16989562.5 -2026-08-06,SDCC,11700.0,11900.0,11700.0,11900.0,478.0,5640400.0 -2026-08-06,SDSC,2190.0,2530.0,2190.0,2530.0,63031.0,148753160.0 -2026-08-06,SEMC,1445.0,1445.0,1445.0,1445.0,1026.0,1482570.0 -2026-08-06,SGBC,38000.0,38000.0,38000.0,38000.0,3368.0,127984000.0 -2026-08-06,SHEC,2300.0,2300.0,2290.0,2290.0,2066.0,4741470.0 -2026-08-06,SIBC,9250.0,9300.0,9250.0,9300.0,2688.0,24931200.0 -2026-08-06,SICC,7085.0,7085.0,7085.0,7085.0,9.0,63765.0 -2026-08-06,SIVC,2075.0,2125.0,2075.0,2125.0,4737.0,9947700.0 -2026-08-06,SLBC,37000.0,37000.0,37000.0,37000.0,322.0,11914000.0 -2026-08-06,SMBC,16500.0,16550.0,16500.0,16550.0,628.0,10377700.0 -2026-08-06,SNTS,31500.0,31500.0,31000.0,31000.0,2386.0,74562500.0 -2026-08-06,SOGC,8060.0,8100.0,8060.0,8100.0,771.0,6229680.0 -2026-08-06,SPHC,7650.0,7650.0,7600.0,7600.0,455.0,3469375.0 -2026-08-06,STAC,2700.0,2700.0,2675.0,2675.0,4162.0,11185375.0 -2026-08-06,STBC,24500.0,24980.0,24500.0,24980.0,1907.0,47179180.0 -2026-08-06,TTLC,2980.0,2980.0,2805.0,2805.0,3506.0,10141105.0 -2026-08-06,TTLS,3630.0,3630.0,3500.0,3500.0,1573.0,5607745.0 -2026-08-06,UNLC,57500.0,57500.0,54595.0,54595.0,21.0,1176997.5 -2026-08-06,UNXC,1835.0,1900.0,1835.0,1900.0,5463.0,10202152.5 2026-08-07,ABJC,3000.0,,,3010.0,499.0,1501990.0 2026-08-07,BICB,7500.0,,,7595.0,1303.0,9896285.0 2026-08-07,BICC,29000.0,,,29100.0,148.0,4306800.0