Crypto 15 Min Kalshi Trading Bot
Build a crypto 15-minute market trading bot for Kalshi using trigger points for high-probability trades. This walkthrough shows how to identify confluence zones and trigger levels on TradingView, set up automated entries and exits, and execute on Kalshi’s crypto markets.
YouTube Video
Screen Map

HUD Code
Script =
(%
(()=>{const t=document.getElementById("KALSHI_HUD");t&&t.remove(),window.__KALSHI_HUD_STATE__||(window.__KALSHI_HUD_STATE__={});const e=window.__KALSHI_HUD_STATE__;try{(e.observers||[]).forEach(t=>t.disconnect())}catch{}if(e.observers=[],e.abortController)try{e.abortController.abort()}catch{}e.abortController=new AbortController;const n=e.abortController.signal,o="#00ff66",i="__KALSHI_DOLLARS__",l="__KALSHI_HUD_POS__",r="__KALSHI_HUD_ZOOM__";let c=parseInt(localStorage.getItem(r),10)||16;let s=null,a=0;const J=(...t)=>t.forEach(t=>{try{t()}catch{}});const u=()=>{if(s)return;const t=Date.now()-a;if(t>=300){a=Date.now();J(U,T,H)}else s=setTimeout(()=>{s=null,a=Date.now();J(U,T,H)},300-t)},d=document.createElement("div");d.id="KALSHI_HUD",d.style.cssText=``\n position: fixed;\n top: 90px;\n left: 90px;\n z-index: 2147483647;\n background: black;\n border: 2px solid ${o};\n border-radius: 12px;\n padding: 12px;\n font-family: monospace;\n color: ${o};\n user-select: none;\n min-width: 391px;\n ``;try{const t=JSON.parse(localStorage.getItem(l)||"null");t&&Number.isFinite(t.left)&&Number.isFinite(t.top)&&(d.style.left=``${t.left}px``,d.style.top=``${t.top}px``)}catch{}const p=localStorage.getItem(i)||"";d.innerHTML=``\n <div id="hudHeader" style="display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;cursor:move">\n <div style="opacity:.9;font-weight:700">HUD</div>\n <div style="display:flex;gap:10px">\n <button id="dec">−</button>\n <button id="inc">+</button>\n <button id="liveSession">LIVE SESSION</button>\n </div>\n </div>\n\n <table id="tbl" style="border-collapse:collapse;font-size:${c}px;width:100%;text-align:center">\n <tr>\n <td style="border:1px solid ${o};padding:18px" id="aLabel">UP</td>\n <td style="border:1px solid ${o};padding:18px" id="aPrice">--</td>\n </tr>\n <tr>\n <td style="border:1px solid ${o};padding:18px" id="bLabel">DOWN</td>\n <td style="border:1px solid ${o};padding:18px" id="bPrice">--</td>\n </tr>\n <tr>\n <td style="border:1px solid ${o};padding:18px" id="tLabel">TIME</td>\n <td style="border:1px solid ${o};padding:18px" id="tVal">--</td>\n </tr>\n </table>\n\n <div style="display:flex;gap:12px;margin-top:14px">\n <button id="buyA" class="bigBuy">BUY UP</button>\n <button id="buyB" class="bigBuy">BUY DOWN</button>\n </div>\n\n <div style="display:flex;gap:12px;margin-top:14px;align-items:center">\n <input id="dollarsBox" placeholder="DOLLARS" value="${p.replace(/"/g,""")}"\n style="\n flex:1;\n background:black;\n color:${o};\n border:1px solid ${o};\n padding:12px 14px;\n font-family:monospace;\n outline:none;\n font-size:16px;\n "/>\n <button id="fillDollars" class="fillBtn">FILL DOLLARS</button>\n </div>\n\n <div style="display:flex;gap:12px;margin-top:14px;align-items:center">\n <div style="min-width:98px;opacity:0.9;font-weight:800">POSITION</div>\n <div id="posBox" style="\n flex:1;\n border:2px solid ${o};\n padding:18px 22px;\n text-align:center;\n letter-spacing:2px;\n font-weight:900;\n font-size: 20px;\n ">0</div>\n </div>\n\n <div id="dbg" style="margin-top:12px;font-size:12px;opacity:0.85;text-align:left"></div>\n ``,d.querySelectorAll("button").forEach(t=>{t.style.cssText=``\n background:black;\n color:${o};\n border:1px solid ${o};\n cursor:pointer;\n font-family:monospace;\n white-space:nowrap;\n ``}),d.querySelector("#dec").style.padding="10px 14px",d.querySelector("#inc").style.padding="10px 14px",d.querySelector("#liveSession").style.padding="10px 16px",d.querySelectorAll(".bigBuy").forEach(t=>{t.style.flex="1",t.style.padding="22px 18px",t.style.fontSize="20px",t.style.fontWeight="900",t.style.letterSpacing="1px"});const f=d.querySelector("#fillDollars");f.style.padding="12px 16px",f.style.fontSize="16px",f.style.fontWeight="800",document.body.appendChild(d);const b=d.querySelector("#dbg"),y=d.querySelector("#posBox"),x=d.querySelector("#tbl"),S=d.querySelector("#hudHeader"),m=d.querySelector("#aLabel"),g=d.querySelector("#bLabel"),v=d.querySelector("#aPrice"),h=d.querySelector("#bPrice"),w=d.querySelector("#tVal"),L=t=>b.textContent=t;let E=!1,I=0,C=0;const _=()=>{try{localStorage.setItem(l,JSON.stringify({left:d.offsetLeft,top:d.offsetTop}))}catch{}};S.addEventListener("mousedown",t=>{t.target.closest("button")||t.target.closest("input")||(E=!0,I=t.clientX-d.offsetLeft,C=t.clientY-d.offsetTop,t.preventDefault())}),document.addEventListener("mousemove",t=>{E&&(d.style.left=t.clientX-I+"px",d.style.top=t.clientY-C+"px")},{signal:n}),document.addEventListener("mouseup",()=>{E&&(E=!1,_())},{signal:n}),window.addEventListener("blur",()=>{E&&(E=!1,_())},{signal:n}),d.querySelector("#inc").onclick=()=>{c+=2,x.style.fontSize=c+"px",localStorage.setItem(r,c)},d.querySelector("#dec").onclick=()=>{c=Math.max(10,c-2),x.style.fontSize=c+"px",localStorage.setItem(r,c)};const D=t=>new Promise(e=>setTimeout(e,t)),A=t=>(t||"").replace(/\s+/g," ").trim().toLowerCase();function q(t){if(!t)return!1;try{t.scrollIntoView({block:"center",inline:"center"})}catch{}const e=t.getBoundingClientRect(),n=Math.floor(e.left+e.width/2),o=Math.floor(e.top+e.height/2),i={bubbles:!0,cancelable:!0,composed:!0,view:window,clientX:n,clientY:o,screenX:n,screenY:o,button:0,buttons:1};try{return t.focus?.(),t.dispatchEvent(new PointerEvent("pointerdown",{...i,pointerId:1,pointerType:"mouse",isPrimary:!0})),t.dispatchEvent(new MouseEvent("mousedown",i)),t.dispatchEvent(new PointerEvent("pointerup",{...i,pointerId:1,pointerType:"mouse",isPrimary:!0})),t.dispatchEvent(new MouseEvent("mouseup",i)),t.dispatchEvent(new MouseEvent("click",i)),!0}catch{try{return t.click?.(),!0}catch{}return!1}}function $(){const t=[...document.querySelectorAll('button[data-testid="price-pill"]')];let e=null,n=null;for(const o of t){const t=A(o.textContent);!e&&/\bup\b/.test(t)&&(e=o),!n&&/\bdown\b/.test(t)&&(n=o)}if(e||n)return{aLabel:"UP",bLabel:"DOWN",aEl:e,bEl:n};let o=null,i=null;for(const e of t){const t=A(e.textContent);!o&&/\byes\b/.test(t)&&(o=e),!i&&/\bno\b/.test(t)&&(i=e)}return{aLabel:"UP",bLabel:"DOWN",aEl:o,bEl:i}}function O(t){if(!t)return null;const e=(t.textContent||"").match(/(\d+)\s*¢/);return e?e[1]:null}function U(){const{aLabel:t,bLabel:n,aEl:o,bEl:i}=$();m.textContent=t,g.textContent=n,v.textContent=O(o)??"--",h.textContent=O(i)??"--",e.aEl=o,e.bEl=i,L(``Prices: Up=${v.textContent} Down=${h.textContent}``)}const B=/^(\d+)\s+(yes|no)$/i;function G(){const t=document.querySelectorAll("div.sticky");for(const e of t){if(d.contains(e))continue;if(!e.querySelector("#cost-input"))continue;const t=e.getBoundingClientRect();if(0===t.width||0===t.height)continue;return e}return null}function Q(){y.textContent=0}function H(){y.textContent=function(){const t=G();if(!t)return 0;const e=t.querySelectorAll("span");for(const t of e){if("Your position"!==(t.textContent||"").trim())continue;const e=t.closest(".justify-between");if(!e)continue;const n=e.querySelectorAll("span");for(const t of n){const e=(t.textContent||"").trim().match(B);if(e)return parseInt(e[1],10)}}return 0}()}function T(){const t=function(){const t=document.querySelectorAll("span");for(const e of t){if(d.contains(e))continue;const t=e.style.fontFeatureSettings||"";if(t.includes("tnum")&&t.includes("cv09")){const t=(e.textContent||"").trim();if(/^\d{1,2}:\d{2}$/.test(t))return e}}return null}();if(!t)return w.textContent="--",void Q();const e=function(t){const e=t.split(":");if(2!==e.length)return null;const n=parseInt(e[0],10),o=parseInt(e[1],10);return isNaN(n)||isNaN(o)?null:60*n+o}((t.textContent||"").trim());w.textContent=null!==e?e:"--",null!==e&&e<=1&&Q()}const Y=d.querySelector("#dollarsBox");function M(){const t=(Y.value||"").trim();if(!t)return L("FILL DOLLARS: empty");const e=t.replace(/[^\d.]/g,"");if(!e)return L("FILL DOLLARS: invalid");const n=document.querySelector("#cost-input")||null;if(!n)return L("FILL DOLLARS: dollars input not found");n.focus(),q(n),function(t,e){const n=Object.getPrototypeOf(t),o=Object.getOwnPropertyDescriptor(n,"value");o&&o.set?o.set.call(t,e):t.value=e}(n,e),n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0})),n.blur(),L(``FILL DOLLARS: ${e}``)}function R(){return[...document.querySelectorAll("button")].find(t=>!d.contains(t)&&("review"===A(t.textContent)&&!t.disabled))||null}function z(){return[...document.querySelectorAll("button")].find(t=>!d.contains(t)&&("submit"===A(t.textContent)&&!t.disabled))||null}async function K(t,e,n=6e4){const o=Date.now();let i=100;for(;Date.now()-o<n;){const e=t();if(e)return e;await D(i),i=Math.min(1.5*i,1e3)}return null}async function F(t){const{aEl:e,bEl:n}=$(),o="UP"===t?e:n;if(!o)return L(``BUY ${t}: pill not found``);L(``BUY ${t}: select``),q(o),await D(200),L(``BUY ${t}: fill dollars``),M(),await D(300),L(``BUY ${t}: waiting for Review``);const i=await K(R);if(!i)return L(``BUY ${t}: Review not found``);L(``BUY ${t}: click Review``),q(i),L(``BUY ${t}: waiting for Submit``);const l=await K(z);if(!l)return L(``BUY ${t}: Submit not found``);L(``BUY ${t}: click Submit``),q(l),L(``BUY ${t}: DONE``)}function W(){const t=function(){const t=document.querySelectorAll(".animate-ping");for(const e of t){if(d.contains(e))continue;const t=e.closest("a, button");if(t&&!d.contains(t)&&/\b\d{1,2}:\d{2}\s*(AM|PM)\b/i.test(t.textContent||""))return t}return null}();if(!t)return L("LIVE SESSION: no live session found");L("LIVE SESSION: clicking "+(t.textContent||"").trim()),q(t)}Y.addEventListener("input",()=>localStorage.setItem(i,Y.value)),d.querySelector("#fillDollars").onclick=()=>M(),d.querySelector("#buyA").onclick=()=>F("UP"),d.querySelector("#buyB").onclick=()=>F("DOWN"),d.querySelector("#liveSession").onclick=()=>W();const V=new MutationObserver(t=>{for(const e of t)if(!d.contains(e.target))return void u()});V.observe(document.body,{childList:!0,subtree:!0,characterData:!0}),e.observers.push(V),e.timerInterval&&clearInterval(e.timerInterval),e.timerInterval=setInterval(()=>{J(T,H,function(){const t=document.querySelectorAll("button");for(const e of t)if(!d.contains(e)&&"done"===A(e.textContent)&&e.closest("div.sticky"))return L("AUTO-DISMISS: clicking Done"),void q(e)})},1e3),u(),L("Kalshi HUD: ON")})();
)
LoadIndicator() {
global Script
ClipSaved := ClipboardAll
Clipboard :=
Clipboard := Script
ClipWait, 1
SendInput, ^+j
Sleep 1200
SendInput, ^v
Sleep 1200
SendInput, allow pasting
Sleep 1000
SendInput, {Enter}
Sleep 4000
SendInput, ^v
SendInput, {Enter}
Sleep 1000
Clipboard := ClipSaved
sleep 1200
SendInput, ^+j
}
click(point.a)
LoadIndicator()
Nightshark Code
triggerPoint := 85 ; Trigger Price Point
Log("Application Started !!")
loop {
Log("Monitoring UP & Down to hit Trigger Price > " . triggerPoint )
click(point.c)
loop{
read_areas()
} until (toNumber(area[1]) > triggerPoint || toNumber(area[2]) > triggerPoint || toNumber(area[3]) < 60 )
if (toNumber(area[1]) > triggerPoint && toNumber(area[4]) = 0) {
Log("UP triggered !! Placing Up Order")
loop, 4{
click(point.a)
sleep 4000
read_areas()
if (toNumber(area[4]) > 0) {
Log("UP position confirmed")
break
}
else {
Log("Position not confirmed ! Retrying ...")
sleep 1000
}
}
}
else if (toNumber(area[2]) > triggerPoint && toNumber(area[4] = 0)) {
Log("Down triggered !! Placing Down Order")
loop, 4 {
click(point.b)
sleep 4000
read_areas()
if (toNumber(area[4]) > 0) {
Log("DOWN position confirmed")
break
}
else {
Log("Position not confirmed ! Retrying ...")
sleep 1000
}
}
}
else if (toNumber(area[3]) < 60) {
Log("Last Minute ! No Trade Zone. Skipping this session.")
}
else if (toNumber(area[4]) > 0) {
Log("Position already exists , skipping this session")
}
Log("Waiting for Next Session")
loop {
read_area(3)
} until (toNumber(area[3]) < 1 || toNumber(area[3]) > 850)
Log("Loading New Session")
sleep 3000
click(point.c)
}