Financial Resilience Simulator: Measure Your Ability to Withstand Economic Shocks

Financial Resilience Simulator: Measure Your Ability to Withstand Economic Shocks (Eco3min)
number bidir. ; graphe éditorial (bande d’érosion, * valeurs en bout de courbe, point terracotta unique) ; hiérarchie * résultats ; en-tête filet doré ; .is-three par classe. * – v1.2 : support bilingue (attribut lang, data-lang, table I18N JS). *, */ if ( ! defined( ‘ABSPATH’ ) ) { return; } if ( ! function_exists( ‘eco3_simu_register_assets’ ) ) : /* ========================================================================= * 1. ASSETS (enqueue conditionnel via le shortcode) * ========================================================================= */ add_action( ‘wp_enqueue_scripts’, ‘eco3_simu_register_assets’ ); function eco3_simu_register_assets() { wp_register_style( ‘eco3-simu’, false, array(), ‘1.4’ ); wp_add_inline_style( ‘eco3-simu’, eco3_simu_css() ); wp_register_script( ‘eco3-simu’, ”, array(), ‘1.4’, true ); wp_add_inline_script( ‘eco3-simu’, eco3_simu_js() ); } /* ========================================================================= * 2. SHORTCODE * ========================================================================= */ add_shortcode( ‘eco3min_simulateur’, ‘eco3_simu_shortcode’ ); function eco3_simu_shortcode( $atts ) { $atts = shortcode_atts( array( ‘type’ => ‘interets-composes’, ‘lang’ => ‘fr’, ), $atts, ‘eco3min_simulateur’ ); wp_enqueue_style( ‘eco3-simu’ ); wp_enqueue_script( ‘eco3-simu’ ); $lang = ( strtolower( $atts[‘lang’] ) === ‘en’ ) ? ‘en’ : ‘fr’; return eco3_simu_markup( sanitize_title( $atts[‘type’] ), $lang ); } /* ========================================================================= * 3. MARKUP PAR TYPE + LANGUE * ========================================================================= */ function eco3_simu_markup( $type, $lang ) { switch ( $type ) { case ‘interets-composes’: if ( $lang === ‘en’ ) { return
Simulator · Compound interest

What time does to your capital, and what it doesn’t guarantee

Adjust the assumptions. The tool plots the nominal projection and its real purchasing power: the terracotta band is what inflation removes.

€
€
%
yrs
%
Assumption −1.5%/yr (Dalbar QAIB methodology — contested: other studies find a smaller gap). Describes the observed gap between fund returns and the return actually captured by the average investor, not an individual fate.
Nominal projection Constant return, on a spreadsheet
Real purchasing power

Conditional projection, non-predictive · assumptions entered by the user · constant return, annual compounding, end-of-period contributions · Eco3min — educational tool, neither advice nor a recommendation.

HTML; } return
Simulateur · Intérêts composés

Ce que le temps fait à votre capital: et ce qu’il n’en garantit pas

Modifiez les hypothèses. L’outil trace la projection nominale et son pouvoir d’achat réel : la bande terracotta est ce que l’inflation retire.

€
€
%
ans
%
Hypothèse −1,5 %/an (méthodologie Dalbar QAIB — contestée : d’autres études situent l’écart plus bas). Décrit l’écart observé entre rendement des fonds et rendement réellement capté par l’investisseur moyen, pas une fatalité individuelle.
Projection nominale Rendement constant, sur tableur
Pouvoir d’achat réel

Projection conditionnelle, non prédictive · hypothèses saisies par l’utilisateur · rendement supposé constant, capitalisation annuelle, versements en fin de période · Eco3min — outil pédagogique, ni conseil ni recommandation.

HTML; case ‘capacite-endettement’: if ( $lang === ‘en’ ) { return
Simulator · Debt capacity

The ratio says “yes”: your remaining budget says whether you hold

The debt-to-income ratio (HCSF cap 35%) is necessary but not sufficient. At an equal ratio, it is the remaining budget that separates a robust situation from a fragile one.

€
€
€
%
yrs
ppl
Debt-to-income ratioHCSF cap 35%
Remaining budget / month

Budget-structure observation, non-predictive · assumptions entered by the user · the HCSF 35% cap is a regulatory reference, not an approval threshold · Eco3min — educational tool, neither advice nor a recommendation.

HTML; } return
Simulateur · Capacité d’endettement

Le ratio dit « oui »: le reste à vivre dit si vous tenez

Le taux d’endettement (plafond HCSF 35 %) est nécessaire mais pas suffisant. À ratio égal, c’est le reste à vivre qui sépare une situation robuste d’une situation fragile.

€
€
€
%
ans
pers.
Taux d’endettementPlafond HCSF 35 %
Reste à vivre / mois

Observation de structure budgétaire, non prédictive · hypothèses saisies par l’utilisateur · le plafond HCSF 35 % est un repère réglementaire, pas un seuil d’octroi · Eco3min — outil pédagogique, ni conseil ni recommandation.

HTML; case ‘resilience-financiere’: if ( $lang === ‘en’ ) { return
Simulator · Financial resilience

A positive balance doesn’t tell you how long you’d hold

Adjust your flows and the shock. This tool measures no return — it measures your resistance duration: how many months your safety savings absorb the shock before rupture.

€
€
€
€
%
%
Normal monthly balanceBefore the shock
Resistance duration

Budget-structure stress observation, non-predictive · assumptions entered by the user · the 3–6 month buffer is a commonly cited reference, not a norm · Eco3min — educational tool, neither advice nor a recommendation.

HTML; } return
Simulateur · Résilience financière

Un solde positif ne dit pas combien de temps vous tenez

Modifiez vos flux et le choc. L’outil ne mesure pas un rendement — il mesure votre durée de résistance : combien de mois votre épargne de sécurité absorbe le choc avant rupture.

€
€
€
€
%
%
Solde mensuel normalAvant le choc
Durée de résistance

Observation de structure budgétaire sous choc, non prédictive · hypothèses saisies par l’utilisateur · le matelas de 3–6 mois est une référence courante, pas une norme · Eco3min — outil pédagogique, ni conseil ni recommandation.

HTML; default: return ‘
Type de simulateur inconnu : ' . esc_html( $type ) . '.
‘; } } /* ========================================================================= * 4. CSS * ========================================================================= */ function eco3_simu_css() { return =v) return steps[k]*mag; } return 10*mag; } //, i18n, var I18N = { fr: { loc:’fr-FR’, symAfter:true, yr:function(n){ return n+’ an’+(n>1?’s’:”); }, leg:{nominal:’Nominal’, behav:’Après écart comportemental’, real:’Pouvoir du2019achat réel’, erode:’Érodé par lu2019inflation’}, delta:function(p){ return ‘u2212’+p+’ % vs nominal’; }, breakdown:function(o){ return ‘Total versé sur ‘+o.yrs+’ ans : ‘+o.versed+’‘ +’ · Intérêts générés (nominal) : ‘+o.interets+’‘ +’ · Pouvoir du2019achat érodé par lu2019inflation : u2212’+o.erode+’‘ +(o.dalbar?’ · Écart comportemental : u2212’+o.behav+’‘:”); }, cap:{ leg:{enc:’Crédits en cours’, neu:’Nouvelle mensualité’, rav:’Reste à vivre’}, marker:’Plafond HCSF 35 %’, perso:function(v){ return v+’ /personne’; }, breakdown:function(o){ return ‘Revenus : ‘+o.revenus+’/mois · Mensualités totales : ‘+o.charges+’ (taux du2019endettement ‘+o.ratio+’ %) · Reste à vivre : ‘+o.reste+’/mois (‘+o.perso+’)’; } }, res:{ leg:{normal:’Normal’, shock:’Sous choc’, ref:’Repère 3–6 mois’}, month:function(n){ return n+’ mois’; }, infinite:’∞’, runwayUnit:function(v){ return v+’ mois’; }, delta:function(s){ return ‘sous choc : ‘+s+’/mois’; }, breakdown:function(o){ return ‘Régime normal : solde ‘+o.solde+’/mois · Sous choc (revenus −’+o.cr+’ %, charges +’+o.cc+’ %) : ‘+o.soldechoc+’/mois‘+(o.rupture?(‘ · votre épargne (‘+o.ep+’) couvre ‘+o.runway+’‘):(‘ · aucune rupture sous ce choc’)); } } }, en: { loc:’en-IE’, symAfter:false, yr:function(n){ return n+’ yr’+(n>1?’s’:”); }, leg:{nominal:’Nominal’, behav:’After behavior gap’, real:’Real purchasing power’, erode:’Eroded by inflation’}, delta:function(p){ return ‘u2212’+p+’% vs nominal’; }, breakdown:function(o){ return ‘Total contributed over ‘+o.yrs+’ yrs: ‘+o.versed+’‘ +’ · Interest generated (nominal): ‘+o.interets+’‘ +’ · Purchasing power eroded by inflation: u2212’+o.erode+’‘ +(o.dalbar?’ · Behavior gap: u2212’+o.behav+’‘:”); }, cap:{ leg:{enc:’Existing loans’, neu:’New payment’, rav:’Remaining budget’}, marker:’HCSF cap 35%’, perso:function(v){ return v+’ /person’; }, breakdown:function(o){ return ‘Income: ‘+o.revenus+’/mo · Total payments: ‘+o.charges+’ (debt-to-income ‘+o.ratio+’%) · Remaining budget: ‘+o.reste+’/mo (‘+o.perso+’)’; } }, res:{ leg:{normal:’Normal’, shock:’Under shock’, ref:’3–6-month reference’}, month:function(n){ return n+’ mo’; }, infinite:’∞’, runwayUnit:function(v){ return v+’ months’; }, delta:function(s){ return ‘under shock: ‘+s+’/mo’; }, breakdown:function(o){ return ‘Normal regime: balance ‘+o.solde+’/mo · Under shock (income −’+o.cr+’%, costs +’+o.cc+’%): ‘+o.soldechoc+’/mo‘+(o.rupture?(‘ · your savings (‘+o.ep+’) cover ‘+o.runway+’‘):(‘ · no rupture under this shock’)); } } } }; //, module : intérêts composés function initInteretsComposes(root){ var lang = (root.getAttribute(‘data-lang’) === ‘en’) ? ‘en’ : ‘fr’; var T = I18N[lang]; var EUR = new Intl.NumberFormat(T.loc,{style:’currency’,currency:’EUR’,maximumFractionDigits:0}); function fmt(v){ return EUR.format(Math.max(0, Math.round(v))); } function shortEur(v){ v = Math.max(0, v); if(lang === ‘en’){ if(v>=1e6) return ‘€’+(v/1e6).toFixed(v>=1e7?0:1)+’M’; if(v>=1e3) return ‘€’+Math.round(v/1e3)+’k’; return ‘€’+Math.round(v); } if(v>=1e6) return (v/1e6).toFixed(v>=1e7?0:1).replace(‘.’,’,’)+’ M€’; if(v>=1e3) return Math.round(v/1e3)+’ k€’; return Math.round(v)+’ €’; } var binds = {}; root.querySelectorAll(‘[data-bind]’).forEach(function(el){ var k = el.getAttribute(‘data-bind’); (binds[k] = binds[k] || []).push(el); }); var outs = {}; root.querySelectorAll(‘[data-out]’).forEach(function(el){ outs[el.getAttribute(‘data-out’)] = el; }); var behavCard = root.querySelector(‘[data-card=”behavioral”]’); var resultsBox = root.querySelector(‘[data-results]’); var svg = root.querySelector(‘[data-chart]’); var legend = root.querySelector(‘[data-legend]’); function readVal(k, def){ var els = binds[k]; if(!els) return def; var src = els.filter(function(e){return e.type===’number’;})[0] || els[0]; return num(src, def); } function syncFrom(el, k){ var els = binds[k]; if(!els) return; var n = els.filter(function(e){return e.type===’number’;})[0]; var r = els.filter(function(e){return e.type===’range’;})[0]; if(!n || !r) return; if(el.type === ‘range’){ n.value = r.value; } else { r.value = Math.min(parseFloat(r.max), Math.max(parseFloat(r.min), num(n, parseFloat(r.value)))); } } function compute(){ var P = Math.max(0, readVal(‘capital’, 0)); var m = Math.max(0, readVal(‘mensuel’, 0)); var r = Math.max(0, readVal(‘taux’, 0)); var yrs = Math.max(1, Math.min(60, Math.round(readVal(‘duree’, 1)))); var infl = Math.max(0, readVal(‘inflation’, 0)); var dalbar = !!(binds.dalbar && binds.dalbar[0].checked); var rEff = dalbar ? Math.max(0, r – 1.5) : r; var nominal = fv(P, m, r, yrs); var behavioral = fv(P, m, rEff, yrs); var real = deflate(behavioral, infl, yrs); var versed = P + m*Math.round(yrs*12); outs.nominal.textContent = fmt(nominal); if (outs.behavioral) outs.behavioral.textContent = fmt(behavioral); outs.real.textContent = fmt(real); var erodePct = nominal>0 ? Math.round((1 – real/nominal)*100) : 0; if (outs.delta) outs.delta.textContent = erodePct>0 ? T.delta(erodePct) : ”; if (behavCard) behavCard.hidden = !dalbar; if (resultsBox) resultsBox.classList.toggle(‘is-three’, dalbar); outs.breakdown.innerHTML = T.breakdown({ yrs: yrs, versed: fmt(versed), interets: fmt(nominal – versed), erode: fmt(nominal – deflate(nominal, infl, yrs)), behav: fmt(nominal – behavioral), dalbar: dalbar }); drawChart({P:P,m:m,r:r,rEff:rEff,yrs:yrs,infl:infl,dalbar:dalbar}); } function drawChart(p){ var W=720,H=360, padL=62,padR=104,padT=20,padB=34; var x0=padL, x1=W-padR, y0=H-padB, y1=padT; var nom=[], rea=[], beh=[]; for(var y=0; ymaxV) maxV=d[1]; }); }); maxV = niceMax(maxV); function X(yr){ return x0 + (yr/p.yrs)*(x1-x0); } function Y(v){ return y0 – (v/maxV)*(y0-y1); } function pts(a){ return a.map(function(d){ return X(d[0]).toFixed(1)+’ ‘+Y(d[1]).toFixed(1); }); } function path(a){ return a.map(function(d,i){ return (i?’L’:’M’)+X(d[0]).toFixed(1)+’ ‘+Y(d[1]).toFixed(1); }).join(‘ ‘); } var s=”, ticks=4; for(var t=0;t‘; s+=’‘+shortEur(val)+’‘; } [0, Math.round(p.yrs/2), p.yrs].forEach(function(yr){ s+=’‘+T.yr(yr)+’‘; }); s+=’‘; s+=’‘; if(p.dalbar) s+=’‘; s+=’‘; var nv=nom[nom.length-1][1], rv=rea[rea.length-1][1], ex=X(p.yrs), ny=Y(nv), ry=Y(rv); s+=’‘; s+=’‘; var lift = (Math.abs(ny-ry)‘+shortEur(nv)+’‘; s+=’‘+shortEur(rv)+’‘; svg.innerHTML=s; var L=T.leg; var leg=’‘+L.nominal+’‘; if(p.dalbar) leg+=’‘+L.behav+’‘; leg+=’‘+L.real+’‘; leg+=’‘+L.erode+’‘; legend.innerHTML=leg; } root.addEventListener(‘input’, function(e){ var el = e.target; var b = el.getAttribute && el.getAttribute(‘data-bind’); if(b && el.type !== ‘checkbox’) syncFrom(el, b); compute(); }); root.addEventListener(‘change’, compute); compute(); } //, module : capacité d’endettement (multi-sorties, composition) function initCapaciteEndettement(root){ var lang = (root.getAttribute(‘data-lang’) === ‘en’) ? ‘en’ : ‘fr’; var T = I18N[lang].cap, loc = I18N[lang].loc; var EUR = new Intl.NumberFormat(loc,{style:’currency’,currency:’EUR’,maximumFractionDigits:0}); function fmt(v){ return EUR.format(Math.round(v)); } function pct(v){ return (Math.round(v*10)/10).toLocaleString(loc); } function ratioStr(v){ return lang===’en’ ? pct(v)+’%’ : pct(v)+’ %’; } var binds = {}; root.querySelectorAll(‘[data-bind]’).forEach(function(el){ var k = el.getAttribute(‘data-bind’); (binds[k] = binds[k] || []).push(el); }); var outs = {}; root.querySelectorAll(‘[data-out]’).forEach(function(el){ outs[el.getAttribute(‘data-out’)] = el; }); var barWrap = root.querySelector(‘[data-bar]’); var legend = root.querySelector(‘[data-legend]’); var realCard = root.querySelector(‘.eco3-simu__stat–real’); function readVal(k, def){ var els = binds[k]; if(!els) return def; var src = els.filter(function(e){return e.type===’number’;})[0] || els[0]; var v = parseFloat(src.value); return isFinite(v) ? v : def; } function syncFrom(el, k){ var els = binds[k]; if(!els) return; var n = els.filter(function(e){return e.type===’number’;})[0]; var r = els.filter(function(e){return e.type===’range’;})[0]; if(!n || !r) return; if(el.type === ‘range’){ n.value = r.value; } else { var v=parseFloat(n.value); v=isFinite(v)?v:parseFloat(r.value); r.value = Math.min(parseFloat(r.max), Math.max(parseFloat(r.min), v)); } } function mensualite(montant, tauxAnnuel, annees){ var i = tauxAnnuel/100/12, n = Math.round(annees*12); if(n0 ? charges/revenus*100 : 0; var reste = revenus – charges; var perso = reste/pers; outs.ratio.textContent = ratioStr(ratio); outs.reste.textContent = fmt(reste); if(outs.resteperso) outs.resteperso.textContent = T.perso(fmt(perso)); if(realCard) realCard.classList.toggle(‘is-neg’, reste0 ? revenus : 1; var wEnc = Math.max(0, encours)/base*100; var wNew = Math.max(0, mNew)/base*100; var wRav = Math.max(0, reste)/base*100; var totalCredit = wEnc + wNew; if(totalCredit > 100){ var sc=100/totalCredit; wEnc*=sc; wNew*=sc; wRav=0; } var seg=”; if(wEnc>0) seg+=’
‘; if(wNew>0) seg+=’
‘; if(wRav>0) seg+=’
‘; barWrap.innerHTML = ‘
‘+seg+’
‘ + ‘
‘ + ‘
‘+T.marker+’
‘; var L=T.leg; legend.innerHTML = ‘‘+L.enc+’‘ + ‘‘+L.neu+’‘ + ‘‘+L.rav+’‘; } root.addEventListener(‘input’, function(e){ var el = e.target; var b = el.getAttribute && el.getAttribute(‘data-bind’); if(b) syncFrom(el, b); compute(); }); compute(); } // ——– module : résilience financière (trajectoire, normal vs choc) function initResilienceFinanciere(root){ var lang = (root.getAttribute(‘data-lang’) === ‘en’) ? ‘en’ : ‘fr’; var T = I18N[lang].res, loc = I18N[lang].loc; var EUR = new Intl.NumberFormat(loc,{style:’currency’,currency:’EUR’,maximumFractionDigits:0}); function fmt(v){ return EUR.format(Math.round(Math.abs(v))); } function sfmt(v){ return (v>=0?’+’:’u2212′)+fmt(v); } function shortEur(v){ v=Math.max(0,v); if(v>=1e6) return (lang===’en’?’u20ac’:”)+(v/1e6).toFixed(1)+(lang===’en’?’M’:’ Mu20ac’); if(v>=1e3) return (lang===’en’?’u20ac’:”)+Math.round(v/1e3)+(lang===’en’?’k’:’ ku20ac’); return (lang===’en’?’u20ac’:”)+Math.round(v)+(lang===’en’?”:’ u20ac’); } var binds={}; root.querySelectorAll(‘[data-bind]’).forEach(function(el){var k=el.getAttribute(‘data-bind’);(binds[k]=binds[k]||[]).push(el);}); var outs={}; root.querySelectorAll(‘[data-out]’).forEach(function(el){outs[el.getAttribute(‘data-out’)]=el;}); var svg=root.querySelector(‘[data-chart]’), legend=root.querySelector(‘[data-legend]’); var realCard=root.querySelector(‘.eco3-simu__stat–real’); function readVal(k,def){ var els=binds[k]; if(!els) return def; var s=els.filter(function(e){return e.type===’number’;})[0]||els[0]; var v=parseFloat(s.value); return isFinite(v)?v:def; } function syncFrom(el,k){ var els=binds[k]; if(!els) return; var n=els.filter(function(e){return e.type===’number’;})[0],r=els.filter(function(e){return e.type===’range’;})[0]; if(!n||!r) return; if(el.type===’range’){n.value=r.value;} else {var v=parseFloat(n.value); v=isFinite(v)?v:parseFloat(r.value); r.value=Math.min(parseFloat(r.max),Math.max(parseFloat(r.min),v));} } function niceMax(v){ if(v=v) return st[k]*mag; } return 10*mag; } function compute(){ var revenus=Math.max(0,readVal(‘revenus’,0)), charges=Math.max(0,readVal(‘charges’,0)), dettes=Math.max(0,readVal(‘dettes’,0)), epargne=Math.max(0,readVal(‘epargne’,0)); var cr=Math.max(0,readVal(‘chocrev’,0)), cc=Math.max(0,readVal(‘choccharges’,0)); var soldeNormal = revenus – charges – dettes; var revChoc = revenus*(1-cr/100), chChoc = charges*(1+cc/100); var soldeChoc = revChoc – chChoc – dettes; var deficit = -soldeChoc; var rupture = deficit > 0; var runway = rupture ? epargne/deficit : Infinity; outs.solde.textContent = sfmt(soldeNormal); outs.runway.textContent = rupture ? T.runwayUnit(Math.round(runway*10)/10) : T.infinite; if(outs.soldechoc) outs.soldechoc.textContent = T.delta(sfmt(soldeChoc)); if(realCard) realCard.classList.toggle(‘is-neg’, rupture && runway maxV)maxV=d[1];});}); maxV=niceMax(maxV||1000); function X(m){ return x0+(m/horizon)*(x1-x0); } function Y(v){ return y0-(v/maxV)*(y0-y1); } function path(a){ return a.map(function(d,i){return (i?’L’:’M’)+X(d[0]).toFixed(1)+’ ‘+Y(d[1]).toFixed(1);}).join(‘ ‘); } var s=”, ticks=4; for(var t=0;t‘+shortEur(val)+’‘; } [0, Math.round(horizon/2), horizon].forEach(function(m){ s+=’‘+T.month(m)+’‘; }); if(6‘; s+=’‘+T.leg.ref+’‘; } s+=’‘; s+=’‘; if(p.rupture && p.runway‘; s+=’‘+T.runwayUnit(Math.round(p.runway*10)/10)+’‘; } svg.innerHTML=s; legend.innerHTML=’‘+T.leg.normal+’‘ +’‘+T.leg.shock+’‘ +’‘+T.leg.ref+’‘; } root.addEventListener(‘input’, function(e){ var el=e.target; var b=el.getAttribute&&el.getAttribute(‘data-bind’); if(b) syncFrom(el,b); compute(); }); compute(); } function boot(){ document.querySelectorAll(‘.eco3-simu[data-eco3-simu]’).forEach(function(root){ if(root.dataset.e3sInit) return; root.dataset.e3sInit=’1′; switch(root.getAttribute(‘data-eco3-simu’)){ case ‘interets-composes’: initInteretsComposes(root); break; case ‘capacite-endettement’: initCapaciteEndettement(root); break; case ‘resilience-financiere’: initResilienceFinanciere(root); break; // futurs types : ajouter un case + un init dédié } }); } if(document.readyState===’loading’) document.addEventListener(‘DOMContentLoaded’, boot); else boot(); })(); ECO3JS; } endif;

Last updated — 25 September 2026

Disclaimer – Financial and Legal Information: The analyses, commentary, and content published on eco3min.fr are provided for informational and educational purposes only. They do not constitute investment advice or a solicitation to buy or sell financial instruments. Past performance is not indicative of future results. All investment decisions involve risk and are the sole responsibility of the reader. Data, charts and calculated series are provided as is and may contain errors or lag behind their source: check any figure against the primary source cited before relying on it. Content describing laws, regulations or contracts reflects the law as of its publication date and does not constitute legal or tax advice; applying it to a specific situation requires a qualified legal or tax professional.