import {calculateLeverage,regulatorCap} from './leverage-engine.mjs'; const $=id=>document.getElementById(id),currencies=['USD','EUR','GBP','JPY','AUD','CAD','CHF','NZD','SEK','NOK','DKK','INR','SGD','HKD','ZAR','PLN','CZK','HUF','MXN']; for(const id of ['acct','base','quote'])for(const c of currencies)$(id).add(new Option(c,c)); $('acct').value='USD';$('base').value='EUR';$('quote').value='USD'; for(const base of currencies)for(const quote of currencies)if(base!==quote)$('symbol').add(new Option(base+quote,base+'/'+quote)); $('symbol').value='EUR/USD'; function regulation(){const [base,quote]=$('symbol').value.split('/');$('base').value=base;$('quote').value=quote;const reg=$('regulator').value,country=$('country').value,limit=regulatorCap(reg,base,quote);$('regulator-cap').textContent=limit===null?'No regulator cap applied to this estimate.':'Retail forex leverage cap for '+base+quote+': 1:'+limit; let message='';if(country==='uk'&®!=='fca')message='Heads up: You selected United Kingdom with a different framework. UK retail CFD accounts generally use FCA rules. Confirm your broker’s legal entity before keeping this selection.';else if(country==='nl'&®!=='afm'&®!=='esma')message='Heads up: You selected Netherlands. Check the AFM or applicable EU retail framework for your broker’s entity.';else if(country==='eu'&®==='afm')message='Heads up: AFM is the Netherlands regulator and follows the EU retail CFD framework. Keep AFM if it applies to your broker, or select the broader ESMA framework.';else if(country==='eu'&&(reg==='fca'||reg==='custom'))message='Heads up: You selected European Union with a different framework. Confirm the applicable national EU rules and broker entity. Custom — No Cap disables caps in this estimate only.';else if(country==='other'&®!=='custom')message='Heads up: This framework may differ from your country’s rules. Confirm your broker entity and account classification.';$('regulator-guidance').textContent=message;$('regulator-guidance').hidden=!message;return limit;} let revision=0;const cache=new Map(); async function exchange(from,to){if(from===to)return {value:1,date:null};const key=from+to;if(cache.has(key))return cache.get(key);const response=await fetch('https://api.frankfurter.dev/v1/latest?base='+from+'&symbols='+to,{signal:AbortSignal.timeout(10000)});if(!response.ok)throw Error('Reference rates are unavailable. Please try again.');const data=await response.json();if(!(data.rates?.[to]>0))throw Error('This conversion rate is unavailable.');const result={value:data.rates[to],date:data.date};cache.set(key,result);return result;} function clear(){ $('lev-margin').textContent='—';$('lev-free').textContent='';$('lev-breakdown').replaceChildren();$('lev-percent').textContent='—';$('lev-meter').value=0;$('lev-insight').textContent='Check your inputs to calculate the margin estimate.';} async function update(){const version=++revision;const balance=Number($('balance').value),risk=Number($('risk').value),stop=Number($('stop').value),leverage=Number($('leverage').value),brokerCap=$('cap').value===''?null:Number($('cap').value),legalCap=regulation(),cap=brokerCap===null?legalCap:legalCap===null?brokerCap:Math.min(brokerCap,legalCap);clear();$('lev-status').textContent='Updating reference rates…';try{document.querySelectorAll('#leverage-form input').forEach(input=>input.setAttribute('aria-invalid',String(!input.validity.valid)));if(!$('leverage-form').checkValidity())throw Error('Check the highlighted inputs. All required values must be positive.');const [b,q]=await Promise.all([exchange($('base').value,$('acct').value),exchange($('quote').value,$('acct').value)]);if(version!==revision)return;const r=calculateLeverage({balance,risk,stop,leverage,cap,mode:$('risk-mode').value,baseRate:b.value,quoteRate:q.value,pip:$('quote').value==='JPY'?.01:.0001});const money=n=>new Intl.NumberFormat('en-US',{style:'currency',currency:$('acct').value,currencyDisplay:'code'}).format(n);const num=n=>new Intl.NumberFormat('en-US',{maximumFractionDigits:4}).format(n);$('lev-margin').textContent=money(r.margin);$('lev-free').textContent='Free margin after open: '+money(r.free);const rows=[['Position size',num(r.lots)+' lots'],['Notional exposure',money(r.notional)],['Planned stop-loss risk',money(r.cashRisk)],['Pip value / lot',money(r.pipLot)],['Effective leverage','1:'+r.effective]];for(const [label,value]of rows){const row=document.createElement('div');row.className='mini';const k=document.createElement('span');k.className='mk';k.textContent=label;const v=document.createElement('strong');v.className='mv';v.textContent=value;row.append(k,v);$('lev-breakdown').append(row);}$('lev-meter').value=Math.min(100,r.percent);$('lev-percent').textContent=r.percent.toFixed(1)+'%';$('lev-insight').textContent=r.free<0?'This position requires more margin than the available balance. Reduce position risk or size.':r.percent>=50?'This position uses at least half your balance as margin. Leave room for adverse price moves and other positions.':'Keep sufficient free margin for price movement. Changing leverage does not change your planned stop-loss risk.';const date=b.date||q.date;$('lev-status').textContent=(date?'Reference rates dated '+date:'No currency conversion required')+(cap!==null&&leverage>cap?' · Leverage cap applied.':'.');}catch(error){if(version!==revision)return;clear();$('lev-status').textContent=error.message;}} $('leverage-form').addEventListener('submit',e=>e.preventDefault());$('leverage-form').addEventListener('input',update);$('leverage-form').addEventListener('change',update);update();