@import url(‘https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Syne:wght@400;500;600;700&display=swap’); #sai-wrap *{box-sizing:border-box;margin:0;padding:0;} #sai-wrap{ –bg:#0a0e1a;–surface:#111827;–card:#161d2e;–border:#1e2d45; –text:#e8edf5;–muted:#6b7a99;–accent:#3b82f6;–green:#10b981; –red:#ef4444;–amber:#f59e0b; –font:’Syne’,sans-serif;–mono:’DM Mono’,monospace; background:var(–bg);color:var(–text);font-family:var(–font); min-height:100vh;padding:0; } #sai-wrap .sai-hdr{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(–surface);border-bottom:1px solid var(–border);position:sticky;top:0;z-index:100;flex-wrap:wrap;} #sai-wrap .sai-logo{font-size:16px;font-weight:700;letter-spacing:-.5px;color:var(–text);white-space:nowrap;} #sai-wrap .sai-logo span{color:var(–accent);} #sai-wrap .sai-srch{position:relative;flex:1;min-width:200px;max-width:480px;} #sai-wrap .sai-srch input{width:100%;background:var(–card);border:1px solid var(–border);border-radius:8px;padding:8px 12px 8px 32px;color:var(–text);font-family:var(–font);font-size:13px;outline:none;transition:border .2s;} #sai-wrap .sai-srch input:focus{border-color:var(–accent);} #sai-wrap .sai-srch input::placeholder{color:var(–muted);} #sai-wrap .si{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(–muted);font-size:14px;pointer-events:none;} #sai-wrap .sai-dd{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(–surface);border:1px solid var(–border);border-radius:8px;z-index:500;display:none;overflow:hidden;box-shadow:0 8px 28px rgba(0,0,0,.55);max-height:300px;overflow-y:auto;} #sai-wrap .sai-dd.open{display:block;} #sai-wrap .ddi{padding:9px 13px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(–border);font-size:12px;} #sai-wrap .ddi:last-child{border-bottom:none;} #sai-wrap .ddi:hover{background:var(–card);} #sai-wrap .ddi .ds{font-weight:700;color:var(–accent);font-size:13px;margin-right:8px;} #sai-wrap .ddi .dn{color:var(–muted);} #sai-wrap .ddi .de{font-size:10px;color:var(–muted);background:var(–card);padding:2px 6px;border-radius:4px;border:1px solid var(–border);} #sai-wrap .cur-sw{display:flex;gap:3px;background:var(–card);border-radius:8px;padding:3px;border:1px solid var(–border);} #sai-wrap .cur-btn{padding:5px 12px;border-radius:6px;font-size:11px;font-weight:700;cursor:pointer;border:none;font-family:var(–font);background:transparent;color:var(–muted);transition:all .15s;} #sai-wrap .cur-btn.on{background:var(–accent);color:#fff;} #sai-wrap .wbtn{padding:6px 13px;border-radius:7px;font-size:11px;cursor:pointer;font-family:var(–font);font-weight:600;border:1px solid rgba(59,130,246,.3);background:rgba(59,130,246,.1);color:#3b82f6;transition:all .15s;} #sai-wrap .wbtn.added{border-color:rgba(16,185,129,.3);background:rgba(16,185,129,.1);color:#10b981;} #sai-wrap .sai-body{display:flex;min-height:calc(100vh – 52px);} #sai-wrap .sai-sb{width:190px;background:var(–surface);border-right:1px solid var(–border);overflow-y:auto;flex-shrink:0;} #sai-wrap .sai-content{flex:1;overflow-y:auto;padding:16px 18px;} #sai-wrap .slbl{padding:10px 12px 4px;font-size:9px;letter-spacing:1.5px;color:var(–muted);text-transform:uppercase;font-weight:600;} #sai-wrap .wit{padding:9px 12px;cursor:pointer;border-bottom:1px solid var(–border);transition:background .12s;} #sai-wrap .wit:hover,#sai-wrap .wit.active{background:var(–card);} #sai-wrap .wit .wr{display:flex;justify-content:space-between;} #sai-wrap .wit .ws{font-size:12px;font-weight:700;} #sai-wrap .wit .wn{font-size:10px;color:var(–muted);margin-top:1px;} #sai-wrap .wit .wp{font-family:var(–mono);font-size:11px;text-align:right;} #sai-wrap .wit .wc{font-family:var(–mono);font-size:10px;} #sai-wrap .up{color:#10b981;}#sai-wrap .dn{color:#ef4444;} #sai-wrap .hero{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px;flex-wrap:wrap;gap:10px;} #sai-wrap .hsym{font-size:24px;font-weight:700;letter-spacing:-1px;} #sai-wrap .hnm{font-size:11px;color:var(–muted);margin-top:2px;} #sai-wrap .hprice{text-align:right;} #sai-wrap .hpbig{font-size:24px;font-weight:700;font-family:var(–mono);} #sai-wrap .hpchg{font-size:12px;font-family:var(–mono);margin-top:2px;} #sai-wrap .hlive{font-size:10px;color:var(–muted);margin-top:3px;} #sai-wrap .ldot{width:6px;height:6px;border-radius:50%;background:#10b981;display:inline-block;animation:lp 2s infinite;margin-right:4px;} @keyframes lp{0%,100%{opacity:1}50%{opacity:.25}} #sai-wrap .sigbox{background:var(–card);border:1px solid var(–border);border-radius:12px;padding:13px 15px;margin-bottom:14px;display:flex;gap:14px;align-items:flex-start;} #sai-wrap .sbadge{padding:7px 16px;border-radius:8px;font-size:13px;font-weight:700;letter-spacing:1px;white-space:nowrap;} #sai-wrap .sbuy{background:rgba(16,185,129,.15);color:#10b981;border:1px solid rgba(16,185,129,.3);} #sai-wrap .ssell{background:rgba(239,68,68,.15);color:#ef4444;border:1px solid rgba(239,68,68,.3);} #sai-wrap .shold{background:rgba(245,158,11,.15);color:#f59e0b;border:1px solid rgba(245,158,11,.3);} #sai-wrap .sload{background:rgba(107,122,153,.1);color:var(–muted);border:1px solid var(–border);} #sai-wrap .sigtxt{flex:1;font-size:11px;color:var(–muted);line-height:1.6;} #sai-wrap .cbwrap{margin-top:7px;} #sai-wrap .cblbl{font-size:9px;color:var(–muted);margin-bottom:3px;} #sai-wrap .cbbar{height:3px;background:var(–border);border-radius:2px;width:88px;} #sai-wrap .cbfill{height:100%;border-radius:2px;transition:width .6s;} #sai-wrap .mets{display:grid;grid-template-columns:repeat(4,1fr);gap:9px;margin-bottom:14px;} #sai-wrap .mc{background:var(–card);border:1px solid var(–border);border-radius:10px;padding:10px 12px;} #sai-wrap .ml{font-size:9px;letter-spacing:1px;color:var(–muted);text-transform:uppercase;margin-bottom:4px;} #sai-wrap .mv{font-size:14px;font-weight:700;font-family:var(–mono);} #sai-wrap .chcard{background:var(–card);border:1px solid var(–border);border-radius:12px;padding:13px 15px;margin-bottom:14px;} #sai-wrap .chtop{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;} #sai-wrap .chtit{font-size:11px;color:var(–muted);} #sai-wrap .tfs{display:flex;gap:3px;} #sai-wrap .tf{padding:3px 9px;border-radius:6px;font-size:10px;cursor:pointer;background:transparent;border:1px solid var(–border);color:var(–muted);font-family:var(–font);transition:all .12s;} #sai-wrap .tf.on{background:var(–accent);color:#fff;border-color:var(–accent);} #sai-wrap .chwrap{position:relative;height:175px;} #sai-wrap .sec{font-size:9px;letter-spacing:1.5px;color:var(–muted);text-transform:uppercase;margin-bottom:9px;font-weight:600;} #sai-wrap .predgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-bottom:14px;} #sai-wrap .pcard{background:var(–card);border:1px solid var(–border);border-radius:10px;padding:10px 12px;} #sai-wrap .plbl{font-size:9px;letter-spacing:1px;color:var(–muted);text-transform:uppercase;margin-bottom:4px;} #sai-wrap .pprice{font-size:13px;font-weight:700;font-family:var(–mono);} #sai-wrap .pdelta{font-size:10px;font-family:var(–mono);margin-top:2px;} #sai-wrap .pbar{height:3px;background:var(–border);border-radius:2px;margin-top:7px;} #sai-wrap .pbfill{height:100%;border-radius:2px;} #sai-wrap .indgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-bottom:20px;} #sai-wrap .icard{background:var(–card);border:1px solid var(–border);border-radius:10px;padding:10px 12px;} #sai-wrap .iname{font-size:10px;color:var(–muted);margin-bottom:3px;} #sai-wrap .ival{font-size:13px;font-weight:700;font-family:var(–mono);} #sai-wrap .ipill{font-size:9px;margin-top:4px;padding:2px 7px;border-radius:4px;display:inline-block;} #sai-wrap .pbull{background:rgba(16,185,129,.15);color:#10b981;} #sai-wrap .pbear{background:rgba(239,68,68,.15);color:#ef4444;} #sai-wrap .pneu{background:rgba(107,122,153,.12);color:var(–muted);} #sai-wrap .sk{background:linear-gradient(90deg,var(–card) 25%,var(–border) 50%,var(–card) 75%);background-size:200% 100%;animation:sk 1.4s infinite;border-radius:6px;} @keyframes sk{0%{background-position:200% 0}100%{background-position:-200% 0}} #sai-wrap .rbtn{padding:5px 11px;border-radius:7px;font-size:11px;cursor:pointer;font-family:var(–font);border:1px solid var(–border);background:transparent;color:var(–muted);margin-left:8px;} #sai-wrap .cadnote{font-size:9px;color:var(–muted);margin-top:2px;} #sai-wrap .disc{font-size:10px;color:var(–muted);padding:8px 18px;background:var(–surface);border-top:1px solid var(–border);text-align:center;} #sai-wrap .splash{display:flex;align-items:center;justify-content:center;height:65vh;flex-direction:column;gap:12px;} #sai-wrap .splash-ico{font-size:36px;} #sai-wrap .splash-title{font-size:18px;font-weight:600;} #sai-wrap .splash-sub{font-size:12px;color:var(–muted);text-align:center;max-width:380px;line-height:1.7;} #sai-wrap .splash-btns{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:10px;} #sai-wrap .splash-btn{padding:7px 16px;background:var(–card);border:1px solid var(–border);color:var(–muted);border-radius:6px;cursor:pointer;font-size:12px;font-family:var(–font);transition:all .15s;} #sai-wrap .splash-btn:hover{border-color:var(–accent);color:var(–accent);}
Watchlist
📈
AI-Powered Stock Analysis
Search any stock worldwide. Live prices, AI buy/sell/hold signals, price predictions from today through next year — in USD and CAD.
⚠ For informational purposes only. Not financial advice. AI predictions are estimates only. Always consult a qualified financial advisor.
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js const sai = (function(){ let AV = localStorage.getItem(‘spp_av’) || ”; let CL = localStorage.getItem(‘spp_cl’) || ”; let CUR = ‘USD’, CADR = 1.37; let WL = JSON.parse(localStorage.getItem(‘spp_wl’) || ‘[“AAPL”,”NVDA”,”TSLA”,”SHOP.TO”,”RY.TO”]’); let SYM = null, PC = null, CD = null; const TICKS = [ {s:’AAPL’,n:’Apple Inc.’,e:’NASDAQ’},{s:’NVDA’,n:’NVIDIA Corporation’,e:’NASDAQ’},{s:’MSFT’,n:’Microsoft Corporation’,e:’NASDAQ’}, {s:’GOOGL’,n:’Alphabet Inc.’,e:’NASDAQ’},{s:’AMZN’,n:’Amazon.com Inc.’,e:’NASDAQ’},{s:’META’,n:’Meta Platforms’,e:’NASDAQ’}, {s:’TSLA’,n:’Tesla Inc.’,e:’NASDAQ’},{s:’AVGO’,n:’Broadcom Inc.’,e:’NASDAQ’},{s:’LLY’,n:’Eli Lilly’,e:’NYSE’}, {s:’JPM’,n:’JPMorgan Chase’,e:’NYSE’},{s:’V’,n:’Visa Inc.’,e:’NYSE’},{s:’XOM’,n:’Exxon Mobil’,e:’NYSE’}, {s:’UNH’,n:’UnitedHealth Group’,e:’NYSE’},{s:’MA’,n:’Mastercard’,e:’NYSE’},{s:’HD’,n:’The Home Depot’,e:’NYSE’}, {s:’PG’,n:’Procter & Gamble’,e:’NYSE’},{s:’JNJ’,n:’Johnson & Johnson’,e:’NYSE’},{s:’COST’,n:’Costco Wholesale’,e:’NASDAQ’}, {s:’ABBV’,n:’AbbVie Inc.’,e:’NYSE’},{s:’BAC’,n:’Bank of America’,e:’NYSE’},{s:’WMT’,n:’Walmart Inc.’,e:’NYSE’}, {s:’NFLX’,n:’Netflix Inc.’,e:’NASDAQ’},{s:’ORCL’,n:’Oracle Corporation’,e:’NYSE’},{s:’CRM’,n:’Salesforce’,e:’NYSE’}, {s:’AMD’,n:’Advanced Micro Devices’,e:’NASDAQ’},{s:’ADBE’,n:’Adobe Inc.’,e:’NASDAQ’},{s:’INTC’,n:’Intel Corporation’,e:’NASDAQ’}, {s:’CSCO’,n:’Cisco Systems’,e:’NASDAQ’},{s:’PEP’,n:’PepsiCo Inc.’,e:’NASDAQ’},{s:’DIS’,n:’Walt Disney’,e:’NYSE’}, {s:’KO’,n:’Coca-Cola’,e:’NYSE’},{s:’NKE’,n:’Nike Inc.’,e:’NYSE’},{s:’GS’,n:’Goldman Sachs’,e:’NYSE’}, {s:’MS’,n:’Morgan Stanley’,e:’NYSE’},{s:’BRK-B’,n:’Berkshire Hathaway B’,e:’NYSE’},{s:’PYPL’,n:’PayPal’,e:’NASDAQ’}, {s:’T’,n:’AT&T Inc.’,e:’NYSE’},{s:’VZ’,n:’Verizon’,e:’NYSE’},{s:’SHOP’,n:’Shopify (USD)’,e:’NYSE’}, {s:’QQQ’,n:’Invesco QQQ ETF’,e:’NASDAQ’},{s:’SPY’,n:’SPDR S&P 500 ETF’,e:’NYSE’},{s:’IWM’,n:’iShares Russell 2000′,e:’NYSE’}, {s:’GLD’,n:’SPDR Gold Shares’,e:’NYSE’},{s:’VTI’,n:’Vanguard Total Market’,e:’NYSE’},{s:’TLT’,n:’iShares 20Y Treasury’,e:’NASDAQ’}, {s:’ASML’,n:’ASML Holding’,e:’NASDAQ’},{s:’TSM’,n:’Taiwan Semiconductor’,e:’NYSE’},{s:’NVO’,n:’Novo Nordisk’,e:’NYSE’}, {s:’SAP’,n:’SAP SE’,e:’NYSE’},{s:’TM’,n:’Toyota Motor’,e:’NYSE’},{s:’SONY’,n:’Sony Group’,e:’NYSE’}, {s:’BABA’,n:’Alibaba Group’,e:’NYSE’},{s:’HSBC’,n:’HSBC Holdings’,e:’NYSE’}, {s:’SHOP.TO’,n:’Shopify Inc.’,e:’TSX’},{s:’RY.TO’,n:’Royal Bank of Canada’,e:’TSX’},{s:’TD.TO’,n:’TD Bank’,e:’TSX’}, {s:’BNS.TO’,n:’Bank of Nova Scotia’,e:’TSX’},{s:’BMO.TO’,n:’Bank of Montreal’,e:’TSX’},{s:’ENB.TO’,n:’Enbridge Inc.’,e:’TSX’}, {s:’CNR.TO’,n:’Canadian National Railway’,e:’TSX’},{s:’CP.TO’,n:’Canadian Pacific’,e:’TSX’},{s:’CNQ.TO’,n:’Canadian Natural Resources’,e:’TSX’}, {s:’ABX.TO’,n:’Barrick Gold’,e:’TSX’},{s:’MFC.TO’,n:’Manulife Financial’,e:’TSX’},{s:’SU.TO’,n:’Suncor Energy’,e:’TSX’}, {s:’TRI.TO’,n:’Thomson Reuters’,e:’TSX’},{s:’ATD.TO’,n:’Alimentation Couche-Tard’,e:’TSX’},{s:’L.TO’,n:’Loblaw Companies’,e:’TSX’}, {s:’DOL.TO’,n:’Dollarama Inc.’,e:’TSX’},{s:’WPM.TO’,n:’Wheaton Precious Metals’,e:’TSX’},{s:’AEM.TO’,n:’Agnico Eagle Mines’,e:’TSX’}, {s:’CCO.TO’,n:’Cameco Corporation’,e:’TSX’},{s:’SLF.TO’,n:’Sun Life Financial’,e:’TSX’},{s:’FFH.TO’,n:’Fairfax Financial’,e:’TSX’}, ]; function fx(p){ return CUR===’CAD’ ? +(p*CADR).toFixed(2) : +p.toFixed(2); } function fp(p){ return ‘$’+fx(p).toFixed(2)+(CUR===’CAD’?’ CAD’:’ USD’); } function fv(n){ if(!n)return’—’;if(n>1e9)return(n/1e9).toFixed(1)+’B’;if(n>1e6)return(n/1e6).toFixed(1)+’M’;if(n>1e3)return(n/1e3).toFixed(1)+’K’;return”+n; } async function fetchRate(){ if(!AV) return; try{ const r = await fetch(‘https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=USD&to_currency=CAD&apikey=’+AV); const d = await r.json(); const rate = parseFloat(d[‘Realtime Currency Exchange Rate’]?.[‘5. Exchange Rate’]); if(rate && !isNaN(rate)) CADR = rate; }catch(e){} } async function fetchQ(sym){ if(!AV) return null; try{ const r = await fetch(‘https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=’+sym+’&apikey=’+AV); const d = await r.json(); const q = d[‘Global Quote’]; if(!q||!q[’05. price’]) return null; return{ name: sym, ex: sym.endsWith(‘.TO’)?’TSX’:’US’, price: +parseFloat(q[’05. price’]).toFixed(2), chg: +parseFloat(q[’09. change’]).toFixed(2), pct: +parseFloat(q[’10. change percent’]).toFixed(2), open: +parseFloat(q[’02. open’]).toFixed(2), high: +parseFloat(q[’03. high’]).toFixed(2), low: +parseFloat(q[’04. low’]).toFixed(2), vol: fv(parseInt(q[’06. volume’])), high52: +parseFloat(q[’03. high’]).toFixed(2), low52: +parseFloat(q[’04. low’]).toFixed(2) }; }catch(e){ return null; } } async function fetchHist(sym){ if(!AV) return null; try{ const r = await fetch(‘https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=’+sym+’&outputsize=compact&apikey=’+AV); const d = await r.json(); const ts = d[‘Time Series (Daily)’]; if(!ts) return null; const e = Object.entries(ts).slice(0,100).reverse(); return{ prices: e.map(x=>+parseFloat(x[1][‘4. close’]).toFixed(2)), labels: e.map(x=>x[0]) }; }catch(e){ return null; } } async function fetchRSI(sym){ if(!AV) return null; try{ const r = await fetch(‘https://www.alphavantage.co/query?function=RSI&symbol=’+sym+’&interval=daily&time_period=14&series_type=close&apikey=’+AV); const d = await r.json(); const v = d[‘Technical Analysis: RSI’]; if(!v) return null; return parseFloat(Object.values(v)[0][‘RSI’]).toFixed(1); }catch(e){ return null; } } async function fetchMACD(sym){ if(!AV) return null; try{ const r = await fetch(‘https://www.alphavantage.co/query?function=MACD&symbol=’+sym+’&interval=daily&series_type=close&apikey=’+AV); const d = await r.json(); const v = d[‘Technical Analysis: MACD’]; if(!v) return null; const e = Object.values(v)[0]; return{ macd: parseFloat(e[‘MACD’]).toFixed(3), signal: parseFloat(e[‘MACD_Signal’]).toFixed(3) }; }catch(e){ return null; } } async function getAI(sym, q, rsi, macd, hist){ if(CL){ try{ const recent = hist ? hist.prices.slice(-20).join(‘, ‘) : ‘N/A’; const prompt = ‘You are a financial analyst AI. Analyze and respond ONLY with valid JSON, no markdown.\n\nStock: ‘+sym+’\nPrice: $’+q.price+’\nChange: ‘+q.chg+’ (‘+q.pct+’%)\nRSI(14): ‘+rsi+’\nMACD: ‘+(macd?macd.macd:’N/A’)+’ Signal: ‘+(macd?macd.signal:’N/A’)+’\nRecent 20-day closes: ‘+recent+’\n\nJSON format:\n{“signal”:”BUY”|”SELL”|”HOLD”,”confidence”:0-100,”reason”:”2-3 sentence analysis”,”predictions”:{“today”:number,”tomorrow”:number,”week”:number,”month”:number,”quarter”:number,”year”:number},”rsi_signal”:”Oversold”|”Neutral”|”Overbought”,”macd_signal”:”Bullish”|”Bearish”|”Neutral”,”trend”:”Uptrend”|”Downtrend”|”Sideways”}’; const resp = await fetch(‘https://api.anthropic.com/v1/messages&#8217;,{ method:’POST’, headers:{‘Content-Type’:’application/json’,’x-api-key’:CL,’anthropic-version’:’2023-06-01′}, body:JSON.stringify({model:’claude-sonnet-4-20250514′,max_tokens:800,messages:[{role:’user’,content:prompt}]}) }); const data = await resp.json(); const txt = data.content[0].text.replace(/“`json|“`/g,”).trim(); return JSON.parse(txt); }catch(e){} } return localSig(q, rsi, macd); } function localSig(q, rsi, macd){ const r = parseFloat(rsi)||50; const bull = parseFloat((macd&&macd.macd)||0) > parseFloat((macd&&macd.signal)||0); const score = (r<35?3:r70?-3:r>60?-1:0)+(bull?2:-2)+(q.chg>=0?1:-1); const sig = score>=3?’BUY’:score=0?1:-1; const p = q.price; return{ signal:sig, confidence:Math.min(91,58+Math.abs(score)*5), reason:’RSI at ‘+r+(r65?’ is overbought, caution advised.’:’ is neutral.’)+’ MACD is ‘+(bull?’bullish, supporting buying interest.’:’bearish, indicating selling pressure.’)+’ Price is ‘+(q.chg>=0?’up today, adding momentum.’:’down today, showing weakness.’), predictions:{ today:+(p*(1+t*0.003+Math.random()*0.005-0.002)).toFixed(2), tomorrow:+(p*(1+t*0.007+Math.random()*0.01-0.004)).toFixed(2), week:+(p*(1+t*0.022+Math.random()*0.025-0.01)).toFixed(2), month:+(p*(1+t*0.055+Math.random()*0.05-0.02)).toFixed(2), quarter:+(p*(1+t*0.12+Math.random()*0.12-0.04)).toFixed(2), year:+(p*(1+t*0.26+Math.random()*0.25-0.08)).toFixed(2) }, rsi_signal:r65?’Overbought’:’Neutral’, macd_signal:bull?’Bullish’:’Bearish’, trend:q.chg>=0?’Uptrend’:’Downtrend’ }; } async function load(sym){ if(!AV){ const k = prompt(‘Enter your Alpha Vantage API key to load live data (free at alphavantage.co):’); if(k){ AV=k.trim(); localStorage.setItem(‘spp_av’,AV); fetchRate(); } else return; } SYM=sym; renderWL(); updWBtn(); showSkel(sym); const [q,hist,rsi,macd] = await Promise.all([fetchQ(sym),fetchHist(sym),fetchRSI(sym),fetchMACD(sym)]); if(!q){ document.getElementById(‘sai-main’).innerHTML=’
Could not load ‘+sym+’. Check your API key or try another ticker.
‘; return; } const ai = await getAI(sym,q,rsi,macd,hist); CD={sym,q,hist,rsi,macd,ai}; renderFull(sym,q,hist,rsi,macd,ai); } function showSkel(sym){ document.getElementById(‘sai-main’).innerHTML= ‘
‘+sym+’
‘+ ‘
‘+ ‘
Fetching live data and running AI analysis…
‘+ ‘
‘+’
‘.repeat(4)+’
‘; } function renderFull(sym,q,hist,rsi,macd,ai){ const sc=ai.signal===’BUY’?’sbuy’:ai.signal===’SELL’?’ssell’:’shold’; const sc2=ai.signal===’BUY’?’#10b981′:ai.signal===’SELL’?’#ef4444′:’#f59e0b’; const rn=parseFloat(rsi)||50; const mb=parseFloat((macd&&macd.macd)||0)>parseFloat((macd&&macd.signal)||0); const isTSX=sym.endsWith(‘.TO’); const preds=[ {l:’Today close’,v:ai.predictions.today},{l:’Tomorrow’,v:ai.predictions.tomorrow}, {l:’Next week’,v:ai.predictions.week},{l:’Next month’,v:ai.predictions.month}, {l:’Next quarter’,v:ai.predictions.quarter},{l:’Next year’,v:ai.predictions.year} ]; const inR=q.high52>q.low52?((q.price-q.low52)/(q.high52-q.low52)*100).toFixed(0):’50’; let h=’
‘+sym+’
‘; h+=’
‘+(q.name||sym)+’ · ‘+(q.ex||”)+ ‘
‘; h+=’
‘+fp(q.price)+’
‘; h+=’
=0?’up’:’dn’)+’”>’+(q.chg>=0?’+’:”)+fx(q.chg).toFixed(2)+’ (‘+q.pct+’%)
‘; if(CUR===’CAD’&&!isTSX) h+=’
USD ‘+q.price.toFixed(2)+’ × ‘+CADR.toFixed(4)+’
‘; h+=’
Live · 15min delay
‘; h+=’
‘+ai.signal+’
‘; h+=’
Confidence: ‘+ai.confidence+’%
‘; h+=’
‘; h+=’
‘+ai.reason+(CL?’ · Claude AI‘:”)+’
‘; h+=’
‘; h+=’
Open
‘+fp(q.open||q.price)+’
‘; h+=’
High / Low
‘+fp(q.high||q.price)+’ / ‘+fp(q.low||q.price)+’
‘; h+=’
Volume
‘+(q.vol||’—’)+’
‘; h+=’
52W Range
‘+fp(q.low52)+’ – ‘+fp(q.high52)+’
‘; h+=’
Price history (‘+CUR+’)‘; h+=’
‘; h+=’
‘; h+=’
AI price predictions (‘+CUR+’)
‘; preds.forEach(p=>{ const diff=(p.v-q.price)/q.price*100; const up=diff>=0; h+=’
‘+p.l+’
‘+fp(p.v)+’
‘; h+=’
‘+(up?’+’:”)+diff.toFixed(2)+’%
‘; h+=’
‘; }); h+=’
Technical indicators
‘; h+=’
RSI (14)
<div class="ival" style="color:'+(rn65?’#ef4444′:’var(–text)’)+’”>’+rn+’
<div class="ipill '+(rn65?’pbear’:’pneu’)+’”>’+ai.rsi_signal+’
‘; h+=’
MACD
‘+(macd?macd.macd:’—’)+’
‘+ai.macd_signal+’
‘; h+=’
Trend
‘+ai.trend+’
‘+ai.trend+’
‘; h+=’
52W Position
‘+inR+’%
of range
‘; h+=’
AI Signal
‘+ai.signal+’
‘+ai.confidence+’% conf.
‘; h+=’
Currency
‘+CUR+’
‘+(isTSX?’TSX listed’:’US listed’)+’
‘; h+=’
‘; document.getElementById(‘sai-main’).innerHTML=h; buildChart(hist,q.price); } function buildChart(hist,curP){ if(PC){PC.destroy();PC=null;} const canvas=document.getElementById(‘sai-chart’); if(!canvas||!hist) return; const prices=hist.prices.slice(-30).map(p=>fx(p)); const labels=hist.labels.slice(-30); const up=prices[prices.length-1]>=prices[0]; const col=up?’#10b981′:’#ef4444′; const ctx=canvas.getContext(‘2d’); const grad=ctx.createLinearGradient(0,0,0,175); grad.addColorStop(0,col+’44’); grad.addColorStop(1,col+’00’); PC=new Chart(canvas,{ type:’line’, data:{labels,datasets:[{data:prices,borderColor:col,borderWidth:2,pointRadius:0,tension:0.3,fill:true,backgroundColor:grad}]}, options:{responsive:true,maintainAspectRatio:false, plugins:{legend:{display:false},tooltip:{mode:’index’,intersect:false,callbacks:{label:c=>fp(c.parsed.y)}}}, scales:{x:{grid:{color:’#1e2d4555′},ticks:{color:’#6b7a99′,font:{size:10},maxTicksLimit:6}}, y:{grid:{color:’#1e2d4555′},ticks:{color:’#6b7a99′,font:{size:10},callback:v=>fp(v)},position:’right’}}} }); } function sliceChart(btn,days){ document.querySelectorAll(‘#sai-wrap .tf’).forEach(b=>b.classList.remove(‘on’)); btn.classList.add(‘on’); if(!CD||!CD.hist) return; if(PC){PC.destroy();PC=null;} const canvas=document.getElementById(‘sai-chart’); if(!canvas) return; const prices=CD.hist.prices.slice(-days).map(p=>fx(p)); const labels=CD.hist.labels.slice(-days); const up=prices[prices.length-1]>=prices[0]; const col=up?’#10b981′:’#ef4444′; const ctx=canvas.getContext(‘2d’); const grad=ctx.createLinearGradient(0,0,0,175); grad.addColorStop(0,col+’44’); grad.addColorStop(1,col+’00’); PC=new Chart(canvas,{ type:’line’, data:{labels,datasets:[{data:prices,borderColor:col,borderWidth:2,pointRadius:0,tension:0.3,fill:true,backgroundColor:grad}]}, options:{responsive:true,maintainAspectRatio:false, plugins:{legend:{display:false},tooltip:{mode:’index’,intersect:false,callbacks:{label:c=>fp(c.parsed.y)}}}, scales:{x:{grid:{color:’#1e2d4555′},ticks:{color:’#6b7a99′,font:{size:10},maxTicksLimit:6}}, y:{grid:{color:’#1e2d4555′},ticks:{color:’#6b7a99′,font:{size:10},callback:v=>fp(v)},position:’right’}}} }); } function renderWL(){ const el=document.getElementById(‘sai-wl’); if(!WL.length){el.innerHTML=’
No stocks added yet
‘;return;} el.innerHTML=WL.map(sym=>{ const tick=TICKS.find(t=>t.s===sym)||{n:sym}; return ‘
‘+’
‘+sym+’
‘+tick.n.split(‘ ‘)[0]+’
‘; }).join(”); } function toggleWatch(){ if(!SYM) return; if(WL.includes(SYM)) WL=WL.filter(s=>s!==SYM); else WL.push(SYM); localStorage.setItem(‘spp_wl’,JSON.stringify(WL)); renderWL(); updWBtn(); } function updWBtn(){ const btn=document.getElementById(‘wbtn’); const w=WL.includes(SYM); btn.textContent=w?’\u2713 Watching’:’+ Watchlist’; btn.className=’wbtn’+(w?’ added’:”); } function setCur(c){ CUR=c; document.getElementById(‘ubtn’).className=’cur-btn’+(c===’USD’?’ on’:”); document.getElementById(‘cbtn’).className=’cur-btn’+(c===’CAD’?’ on’:”); if(CD) renderFull(CD.sym,CD.q,CD.hist,CD.rsi,CD.macd,CD.ai); renderWL(); } function setupSearch(){ const inp=document.getElementById(‘sai-inp’); const dd=document.getElementById(‘sai-dd’); inp.addEventListener(‘input’,function(){ const q=this.value.toLowerCase().trim(); if(!q){dd.classList.remove(‘open’);return;} const hits=TICKS.filter(t=>t.s.toLowerCase().includes(q)||t.n.toLowerCase().includes(q)).slice(0,8); if(!hits.length){dd.classList.remove(‘open’);return;} dd.innerHTML=hits.map(t=>’
‘+’
‘+t.s+’‘+t.n+’
‘+’‘+t.e+’
‘).join(”); dd.classList.add(‘open’); }); document.addEventListener(‘click’,e=>{ if(!e.target.closest(‘.sai-srch’)) dd.classList.remove(‘open’); }); } function selStock(sym){ document.getElementById(‘sai-inp’).value=”; document.getElementById(‘sai-dd’).classList.remove(‘open’); load(sym); } // Init AV = localStorage.getItem(‘spp_av’)||”; CL = localStorage.getItem(‘spp_cl’)||”; if(AV) fetchRate(); setupSearch(); renderWL(); if(WL.length) load(WL[0]); return {load, setCur, toggleWatch, sliceChart, selStock}; })();