1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta charset="UTF-8"> 6 <!-- Provide `global` for node-style browser deps (e.g. react-zlib-js) without 7 rewriting it at build time (that corrupted core-js's ../internals/global-this). --> 8 <link rel="icon" href="/favicon.png"> 9 10 11 <!-- Warm up connections to third-party origins used early in page load so the 12 TLS/DNS handshake overlaps with HTML parsing instead of blocking later. --> 13 <!-- Keep this list short: Chrome warns past four preconnects, and each one 14 spends a connection slot that the critical requests could use. Only the 15 origins hit before first paint stay here (the geo lookup, jQuery and the 16 hero font); everything else is a cheap dns-prefetch below. --> 17 <link rel="preconnect" href="https://api.aaafx.com" crossorigin> 18 19 <link rel="preconnect" href="https://ajax.googleapis.com" crossorigin> 20 <!-- Web fonts (Unbounded/Inter) are served from fonts.gstatic.com; preconnect so 21 the font used by the hero heading (an LCP candidate) arrives sooner. --> 22 23 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 24 <!-- Preload the latin Unbounded face used by the headings above the fold. It is 25 declared with font-display: swap, so arriving late made the headings reflow 26 after first paint (~0.11 CLS on mobile, the tab-box shrinking from 147px to 27 119px). Discovering it here rather than after the stylesheet parses gets it 28 in before paint. Only this face is preloaded: adding Inter/Roboto too cost 29 ~200 ms of LCP competing for bandwidth without improving CLS further. 30 This URL must stay in sync with the @font-face rules in 31 public/assets/sass/font/_unbounded.scss; if it drifts the preload is merely 32 unused, never wrong. --> 33 34 <link rel="preload" as="font" type="font/woff2" crossorigin 35 href="https://fonts.gstatic.com/s/unbounded/v6/Yq6W-LOTXCb04q32xlpwu8Zf.woff2"> 36 <!-- Preload the home hero image (the mobile LCP element). It lives in the lazy 37 home route chunk, so without this the browser only discovers it after the 38 JS boots and mounts. fetchpriority=high pulls it ahead of lower-priority 39 requests. Matches the <img> in src/views/home/index.vue. --> 40 41 <link rel="preload" as="image" href="/assets/image/hero/bg-1.svg" fetchpriority="high"> 42 <link rel="dns-prefetch" href="https://cdn.jsdelivr.net"> 43 <link rel="dns-prefetch" href="https://cdnjs.cloudflare.com"> 44 <link rel="dns-prefetch" href="https://www.googletagmanager.com"> 45 <link rel="dns-prefetch" href="https://cdn.livechatinc.com"> 46 <link rel="dns-prefetch" href="https://metatraderweb.app"> 47 <!-- jQuery and slick MUST stay in <head>, above the app's module script. 48 Vite injects
48<script type="module" src="/assets/index-*.js"> at the end of 49 <head>, and `defer` scripts and module scripts execute in DOCUMENT order. 50 While these lived at the bottom of <body> they ran *after* the app booted, 51 so every component's mounted() hook that calls $(...) â header, footer, 52 home, mt4/mt5, crypto, account-types, act-trader, and every $().slick() 53 carousel init â threw "ReferenceError: $ is not defined". 54 `defer` still keeps them off the parser-blocking path. --> 55
55<script defer src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
55 56
56<script defer type="text/javascript" src="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick.min.js"></script>
56 57 <!-- No maximum-scale: capping it at 1 blocks pinch-zoom, which fails the 58 accessibility audit and locks out anyone who needs to magnify the page. --> 59 <meta name="viewport" content="width=device-width, initial-scale=1"> 60 <title>Trade Forex at zero commission - AAAFx</title> 61 <meta name="description" 62 content="Trade forex, commodities, stocks, indices, cyrptos with the most transparent broker with raw spreads from 0.0 pips and zero commission."> 63 <meta name="keywords" 64 content="Forex, AAAFx, forex broker, trade forex, trade cryptos, tight spreads,mt4, foreign exchange, open account, forex broker, forex trade, "> 65 66 <meta property="og:title" content="AAAFx"> 67 <meta property="og:description" content="AAAFx"> 68 <meta property="og:image" content="/global/assets/image/logo_black.svg"> 69 <meta property="og:url" content="https://aaafx.com/"> 70 71 <meta name="twitter:title" content="AAAFx"> 72 <meta name="twitter:description" content="AAAFx"> 73 <meta name="twitter:url" content="/assets/image/logo_black.svg"> 74 <meta name="twitter:card" content="AAAFx"> 75 76 <!-- Slick carousel CSS loaded non-render-blocking (carousels are below the 77 fold). The print->all media swap lets the browser paint first, then 78 apply these styles; <noscript> keeps it working without JS. --> 79 <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick.css" 80 media="print" onload="this.media='all'" /> 81 <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick-theme.css" 82 media="print" onload="this.media='all'" /> 83 <noscript> 84 <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick.css" /> 85 <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/gh/kenwheeler/[email protected]/slick/slick-theme.css" /> 86 </noscript> 87
87<script type="module" crossorigin src="/assets/index-010e327e.js"></script>
87 88 <link rel="modulepreload" crossorigin href="/assets/vue-vendor-bd567a24.js"> 89 <link rel="modulepreload" crossorigin href="/assets/bootstrap-71154e5e.js"> 90 <link rel="stylesheet" href="/assets/index-27fc820b.css"> 91 <link rel="modulepreload" crossorigin href="/assets/index-1af62f8c.js"> 92 <link rel="modulepreload" crossorigin href="/assets/discover-more-c1731183.js"> 93 <link rel="modulepreload" crossorigin href="/assets/trading-account-types-1ee00cfb.js"> 94 <link rel="modulepreload" crossorigin href="/assets/bg-1-27a91485.js"> 95 <link rel="modulepreload" crossorigin href="/assets/fast_execution-3a3f49ab.js"> 96 <link rel="modulepreload" crossorigin href="/assets/micro_Lots-bca66e08.js"> 97 <link rel="modulepreload" crossorigin href="/assets/act-trader-c9f89d53.js"> 98</head> 99 100<body> 101 <div id="app"></div> 102 <!-- Google Tag Manager (noscript) -->
vendor: 70 bytes, lines 102-103
102 103 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
103GTM-PN8CGG6
vendor: 91 bytes, lines 103-105
103" height="0" width="0" 104 style="display:none;visibility:hidden"></iframe></noscript> 105
105<!-- End Google Tag Manager (noscript) --> 106 107 <!-- =================================================================== 108 Third-party tags: GTM, Google Ads, GA4, Teads, LiveChat, MetaTrader 109 widget and Criteo. 110 111 These all used to load during initial page load and were by far the 112 biggest drag on mobile. Measured against the same build, blocking them 113 took Total Blocking Time from ~1000 ms to 114 ms and LCP from 5.0 s to 114 2.6 s. TBT is what drives the INP failure in the field data, so instead 115 of loading up front, each tag now: 116 117 1. installs its queueing stub immediately (cheap, no network), so any 118 early gtag()/dataLayer.push()/LiveChatWidget call is still captured 119 and replayed once the real library arrives; and 120 2. fetches its script only once the page has loaded and the main thread 121 is idle, with a hard backstop so tags always fire on a quiet page. 122 123 Deferring means analytics pageviews land a few seconds later than before, 124 which can shift attribution slightly. That is deliberate. 125 =================================================================== --> 126
126<script> 127 (function () { 128 // ---- queues installed now, so nothing early is dropped ---- 129 window.dataLayer = window.dataLayer || []; 130 function gtag() { window.dataLayer.push(arguments); } 131 window.gtag = window.gtag || gtag; 132 gtag('js', new Date()); 133 gtag('config', 'AW-10955579630'); 134 gtag('config', 'G-M4SV51FR6C'); 135 136 // LiveChat: build the queueing stub, but hold back init() -- that is the 137 // call that downloads tracking.js. 138 var lcInit; 139 window.__lc = window.__lc || {}; 140 window.__lc.license = 19246054; 141 window.__lc.chat_between_groups = false; 142 (function (n, t, c) { 143 function i(n) { return e._h ? e._h.apply(null, n) : e._q.push(n) } 144 var e = { 145 _q: [], _h: null, _v: '2.0', 146 on: function () { i(['on', c.call(arguments)]) }, 147 once: function () { i(['once', c.call(arguments)]) }, 148 off: function () { i(['off', c.call(arguments)]) }, 149 get: function () { 150 if (!e._h) throw new Error("[LiveChatWidget] You can't use getters before load."); 151 return i(['get', c.call(arguments)]); 152 }, 153 call: function () { i(['call', c.call(arguments)]) }, 154 init: function () { 155 var s = t.createElement('script'); 156 s.async = !0; s.type = 'text/javascript'; 157 s.src = 'https://cdn.livechatinc.com/tracking.js'; 158 t.head.appendChild(s); 159 } 160 }; 161 lcInit = e.init; 162 n.LiveChatWidget = n.LiveChatWidget || e; 163 }(window, document, [].slice)); 164 165 window.criteo_q = window.criteo_q || []; 166 167 function load(src) { 168 var s = document.createElement('script'); 169 s.async = true; s.src = src; 170 document.head.appendChild(s); 171 } 172 173 // The geo block below calls loadScripts() for Criteo, but it was never 174 // defined anywhere -- the call always threw (swallowed by its try/catch), 175 // so Criteo retargeting never actually loaded. This is that missing loader. 176 window.loadScripts = function (opts) { 177 var s = document.createElement('script'); 178 s.src = opts.url; 179 var attr = (opts && opts.attr) || {}; 180 for (var k in attr) { 181 if (Object.prototype.hasOwnProperty.call(attr, k)) s.setAttribute(k, attr[k]); 182 } 183 document.head.appendChild(s); 184 }; 185 186 // Lets async code (e.g. the geo lookup) hand work to the deferred phase. 187 var queued = [], fired = false; 188 window.__deferTag = function (fn) { fired ? fn() : queued.push(fn); }; 189 190 // Deliberately NOT triggered by user interaction. The GTM container alone 191 // costs ~785 ms of main-thread time on a mid-range phone, so firing it from 192 // a pointerdown/scroll handler would land all of that work on the user's 193 // first tap -- precisely the contention that makes INP fail. Idle time after 194 // load is early enough to be done before most first interactions, without 195 // ever competing with one. 196 function fire() { 197 if (fired) return; 198 fired = true; 199 window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); 200 load('https://www.googletagmanager.com/gtm.js?id=GTM-PN8CGG6'); 201 // The GTM container already loads gtag.js for AW-10955579630 and 202 // G-M4SV51FR6C itself (a trace shows both fetched a second time with 203 // GTM's &cx=c>m=... parameters). Loading them here as well downloaded 204 // ~360 kB of duplicate script and executed it twice. The gtag() config 205 // calls queued above still apply â GTM's own gtag.js reads the same 206 // dataLayer. If a tag ever stops reporting, re-add the line for that ID. 207 load('https://p.teads.tv/teads-fellow.js'); 208 // metatraderweb's widget.js only defines window.MetaTraderWebTerminal(), 209 // and nothing on the site ever calls it -- the platform pages link to the 210 // web terminal with a plain <a href> (static_vars.platformLinks.mt4/mt5.web) 211 // rather than embedding the iframe. So this bought nothing and cost a 212 // request that 404s in Chrome, putting a red error in every visitor's 213 // console. It 200s to curl, which is why it looks fine from a shell. 214 // The dns-prefetch below is deliberately kept: those <a href> links do 215 // go to metatraderweb.app, so resolving it early still helps the click. 216 217 // LiveChat mounts #chat-widget-container as its full 392x
217714 panel and 218 // only collapses it to the 84x84 launcher bubble ~2 s later. The 219 // container is position:fixed so it never moves *our* content, but 220 // Chrome still scores the widget's own 630 px jump as a layout shift -- 221 // 0.22 CLS on the runs where that first panel is briefly visible. It is 222 // intermittent, so it does not move the p75 CrUX number, but it is a 223 // real shift and the largest one left on the page. 224 // visibility:hidden elements are excluded from layout-shift scoring, so 225 // holding the widget invisible until it has settled makes the collapse 226 // free. The launcher still appears long before anyone reaches for it. 227 // Note the reveal is driven entirely by timers set here and the 228 // stylesheet never hides the widget by itself, so no failure path can 229 // leave chat unreachable -- the worst case is a late bubble. 230 var lcHide = document.createElement('style'); 231 lcHide.textContent = '#chat-widget-container{visibility:hidden!important}'; 232 document.head.appendChild(lcHide); 233 var lcReveal = function () { if (lcHide.parentNode) lcHide.parentNode.removeChild(lcHide); }; 234 // Reveal as soon as the container is launcher-sized, i.e. the panel has 235 // collapsed. A height of 0 means it is mounted but not laid out yet, so 236 // that does not count as settled. 237 var lcPoll = setInterval(function () { 238 var c = document.getElementById('chat-widget-container'); 239 var h = c ? c.getBoundingClientRect().height : 0; 240 if (h > 40 && h < 200) { clearInterval(lcPoll); lcReveal(); } 241 }, 250); 242 setTimeout(function () { clearInterval(lcPoll); lcReveal(); }, 5000); 243 lcInit(); 244 while (queued.length) { 245 try { queued.shift()(); } catch (e) { console.log(e); } 246 } 247 } 248 249 function schedule() { 250 if ('requestIdleCallback' in window) requestIdleCallback(fire, { timeout: 3000 }); 251 else setTimeout(fire, 2000); 252 } 253 if (document.readyState === 'complete') schedule(); 254 else window.addEventListener('load', schedule, { once: true }); 255 setTimeout(fire, 8000); // backstop: never leave tags unfired 256 })(); 257 </script>
257 258 259 <!--md5 tags--> 260 261 <noscript> 262 <a href="https://www.livechatinc.com/chat-with/19246054/" rel="nofollow">Chat with us</a>, 263 powered by <a href="https://www.livechatinc.com/?welcome" rel="noopener nofollow" target="_blank">LiveChat</a> 264 </noscript> 265 <noscript> 266 <a href="https://www.livechat.com/chat-with/13534029/" rel="nofollow"> 267 Chat with us</a>, powered by <a href="https://www.livechat.com/?welcome" rel="noopener nofollow" 268 target="_blank">LiveChat</a> 269 </noscript> 270
270<script type="text/javascript"> 271 // let p_countries = {'AE':104591,'DE':104621,'DK':104622,'ES':104623,'ID':104624,'IT':103069,'KE':104625,'MA':104626,'MY':104627,'NG':104628,'NL':104629,'PH':104630,'PL':103068,'PT':104631,'SA':104632,'SE':104633,'SG':104634,'TH':104635,'ZA':104636}; 272 let p_countries = { 'AE': 104591, 'DE': 104621, 'DK': 104622, 'ES': 104623, 'ID': 104624, 'IT': 103069, 'KE': 104625, 'MA': 104626, 'MY': 104627, 'NG': 104628, 'NL': 104629, 'PH': 104630, 'PL': 103068, 'PT': 104631, 'SA': 104632, 'SE': 104633, 'SG': 109525, 'TH': 109526, 'ZA': 104636, 'CA': 109527 }; 273 const european_countries_list = { 274 "AT": '"Austria"', 275 "BE": '"Belgium"', 276 "BG": '"Bulgaria"', 277 "CY": '"Cyprus"', 278 "CZ": '"Czechia"', 279 "DE": '"Germany"', 280 "DK": '"Denmark"', 281 "EE": '"Estonia"', 282 "ES": '"Spain"', 283 "FI": '"Finland"', 284 "FR": '"France"', 285 "GR": '"Greece"', 286 "HR": '"Croatia"', 287 "HU": '"Hungary"', 288 "IE": '"Ireland"', 289 "IT": '"Italy"', 290 "LT": '"Lithuania"', 291 "LU": '"Luxembourg"', 292 "LV": '"Latvia"', 293 "MT": '"Malta"', 294 "NL": '"Netherlands"', 295 "PL": '"Poland"', 296 "PT": '"Portugal"', 297 "RO": '"Romania"', 298 "SE": '"Sweden"', 299 "SI": '"Slovenia"', 300 "SK": '"Slovakia"', 301 "CH": '"Switzerland"' 302 } 303 fetch('https://appapi.aaafx.com/get/geo').then(response => { 304 if (response.ok) { 305 return response.text(); 306 } else { 307 throw new Error('HTTP Error ' + response.status); 308 } 309 }).then(country => { 310 if (country != '') { 311 try { 312 localStorage.setItem("ip_country", country); 313 let lang = ['AR', 'ES', 'TR', 'VI', 'PT', 'TH', 'ZH', 'ID'] 314 if (lang.includes(country)) { 315 localStorage.setItem('lang', country.toLowerCase()); 316 } 317 window.criteo_q = window.criteo_q || []; 318 if (p_countries[country]) { 319 // Handed to the deferred tag phase so Criteo does not compete with 320 // first paint. Runs immediately if that phase has already fired. 321 window.__deferTag(function () { 322 loadScripts({ 'url': ('//dynamic.criteo.com/js/ld/ld.js?a=' + p_countries[country]), 'attr': { 'async': "true" } }, p_countries[country]); 323 }); 324 } 325 } catch (e) { console.log(e); } 326 let pathname = window.location.pathname; 327 if (!european_countries_list[country] && pathname.split("/").length > 1 && pathname.split("/")[1] != 'global') { 328 if (pathname == "/") { 329 pathname = ''; 330 } 331 const { port, origin, search } = window.location; 332 let str = '/global' + pathname + search; 333 window.location = str; 334 } 335 336 if (window.location.search.includes("refLink")) { 337 var date = new Date(); 338 date.setMonth(date.getMonth() + 12); 339 let link = window.location.search.split("=")[1] 340 document.cookie = "IBREFERLINK=" + link + ";expires=" + date + ";domain=.aaafx.com"; 341 342 let decrpyLinRm = window.atob(link.toString()); 343 let decrpyLinRm1 = decrpyLinRm.split('||') 344 //cons(decrpyLinRm,"---$$$$$-------",decrpyLinRm1) 345 document.cookie = "refLink=" + window.btoa(decrpyLinRm1[0]) + ";expires=" + date + ";domain=.aaafx.com"; 346 document.cookie = "refRm=" + window.btoa(decrpyLinRm1[1]) + ";expires=" + date + ";domain=.aaafx.com"; 347 } 348 } 349 }).catch(function (error) { console.log(error); }); 350 </script>
350 351 352 <!-- jQuery and slick are loaded in <head> so they execute before this module; 353 see the comment there. --> 354 355 <!-- The MetaTrader web widget is now loaded in the deferred tag phase above. --> 356 <!-- Font Awesome loaded non-render-blocking via the print->all media swap. --> 357 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" 358 media="print" onload="this.media='all'" /> 359 <noscript> 360 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" /> 361 </noscript> 362 <!-- public/assets/js/myscript.js is NOT loaded: it starts with 363 `import $ from "jquery"`, which is a syntax error in a classic script, so 364 the browser threw on every page load and none of the file ever ran (its 365 slider setup is duplicated in the components' mounted() hooks anyway). 366 If it is ever needed again, drop the import and re-add the tag. --> 367 <!--
367<script src="https://prodstorage.azureedge.net/Widgets/lib/@1.0.0/widget-core.js"></script>
367 368
368<script> 369 AcuityWidgets.globals({ 370 apikey: '7dcdb63a-17e3-46b6-97b6-e296e141404d', 371 locale: 'en-GB' 372 }); 373 </script>
373 374 375
375<script> 376 !function(f,b,e,v,n,t,s) 377 {if(f.fbq)return;n=f.fbq=function(){n.callMethod? 378 n.callMethod.apply(n,arguments):n.queue.push(arguments)}; 379 if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0'; 380 n.queue=[];t=b.createElement(e);t.async=!0; 381 t.src=v;s=b.getElementsByTagName(e)[0]; 382 s.parentNode.insertBefore(t,s)}(window, document,'script', 383 'https://connect.facebook.net/en_US/fbevents.js'); 384 fbq('init', '{682763263534928}'); 385 fbq('track', 'PageView'); 386 </script>
386 387 <noscript> 388 <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id={your-pixel-id-goes-here}&ev=PageView&noscript=1"/> 389 </noscript> 390
390<script type="text/javascript"> 391 (function(a,e,f,g,b,c,d){a[b]||(a.FintezaCoreObject=b,a[b]=a[b]||function(){(a[b].q=a[b].q||[]).push(arguments)},a[b].l=1*new Date,c=e.createElement(f),d=e.getElementsByTagName(f)[0],c.async=!0,c.defer=!0,c.src=g,d&&d.parentNode&&d.parentNode.insertBefore(c,d))}) 392 (window,document,"script","https://aaafx.com/fz/core.js","fz"); 393 fz("register","website",{id:"xsenvhfblhketmdxnnchygsavgwcsblzhl",trackHash:true,trackLinks:true,timeOnPage:true}); 394 </script>
394 --> 395
396<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"ecd6eede56a8458eb2ba953c4301864a","r":1,"spa":2}' crossorigin="anonymous"></script>
396 397</body> 398 399</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.