Kalshi Trading Bot Version 2
Version 2 of the Kalshi 15-minute crypto market trading bot, built on top of v1 by popular request. This update adds fully customizable stop loss and time delay restriction features for the Kalshi 14-minute crypto market, giving you tighter risk control and smarter entry timing on every session.
YouTube Video
Screen Map

HUD v2 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 u=(...t)=>t.forEach(t=>{try{t()}catch{}}),d=()=>{if(s)return;const t=Date.now()-a;t>=300?(a=Date.now(),u(P,W,R)):s=setTimeout(()=>{s=null,a=Date.now(),u(P,W,R)},300-t)},p=document.createElement("div");p.id="KALSHI_HUD",p.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)&&(p.style.left=``${t.left}px``,p.style.top=``${t.top}px``)}catch{}const f=localStorage.getItem(i)||"";p.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="copyPrice">COPY</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="${f.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 ``,p.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 ``}),p.querySelector("#dec").style.padding="10px 14px",p.querySelector("#inc").style.padding="10px 14px",p.querySelector("#copyPrice").style.padding="10px 16px",p.querySelector("#liveSession").style.padding="10px 16px",p.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 b=p.querySelector("#fillDollars");b.style.padding="12px 16px",b.style.fontSize="16px",b.style.fontWeight="800",document.body.appendChild(p);const y=p.querySelector("#dbg"),x=p.querySelector("#posBox"),S=p.querySelector("#tbl"),g=p.querySelector("#hudHeader"),m=p.querySelector("#aLabel"),L=p.querySelector("#bLabel"),v=p.querySelector("#aPrice"),w=p.querySelector("#bPrice"),E=p.querySelector("#tVal"),h=t=>y.textContent=t;let C=!1,I=0,$=0;const D=()=>{try{localStorage.setItem(l,JSON.stringify({left:p.offsetLeft,top:p.offsetTop}))}catch{}};g.addEventListener("mousedown",t=>{t.target.closest("button")||t.target.closest("input")||(C=!0,I=t.clientX-p.offsetLeft,$=t.clientY-p.offsetTop,t.preventDefault())}),document.addEventListener("mousemove",t=>{C&&(p.style.left=t.clientX-I+"px",p.style.top=t.clientY-$+"px")},{signal:n}),document.addEventListener("mouseup",()=>{C&&(C=!1,D())},{signal:n}),window.addEventListener("blur",()=>{C&&(C=!1,D())},{signal:n}),p.querySelector("#inc").onclick=()=>{c+=2,S.style.fontSize=c+"px",localStorage.setItem(r,c)},p.querySelector("#dec").onclick=()=>{c=Math.max(10,c-2),S.style.fontSize=c+"px",localStorage.setItem(r,c)};const q=t=>new Promise(e=>setTimeout(e,t)),U=t=>(t||"").replace(/\s+/g," ").trim().toLowerCase();function O(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=U(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=U(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 A(t){if(!t)return null;const e=(t.textContent||"").match(/([\d.]+)\s*¢/);return e?String(Math.floor(parseFloat(e[1]))):null}function P(){const{aLabel:t,bLabel:n,aEl:o,bEl:i}=_();m.textContent=t,L.textContent=n,v.textContent=A(o)??"--",w.textContent=A(i)??"--",e.aEl=o,e.bEl=i,h(``Prices: Up=${v.textContent} Down=${w.textContent}``)}const B=/^([\d,.]+)\s+(yes|no)$/i,N=/([\d,.]+)\s+(yes|no)/i;let Y=null;const k=p.querySelector("#buyA"),H=p.querySelector("#buyB");function T(t){if(!t)return!1;if(null===t.offsetParent&&"fixed"!==getComputedStyle(t).position)return!1;const e=t.getBoundingClientRect();return e.width>0&&e.height>0}function M(t){if(!t)return null;const e=t.querySelectorAll("span");for(const t of e){const e=(t.textContent||"").replace(/\s+/g," ").trim().match(B);if(!e)continue;const n=parseFloat(e[1].replace(/,/g,""));if(Number.isFinite(n))return{qty:n,side:e[2].toLowerCase()}}const n=(t.textContent||"").replace(/\s+/g," ").trim().match(N);if(n){const t=parseFloat(n[1].replace(/,/g,""));if(Number.isFinite(t))return{qty:t,side:n[2].toLowerCase()}}return null}function F(){x.textContent=0,Y=null,k.textContent="BUY UP",H.textContent="BUY DOWN"}function R(){const{qty:t,side:e}=function(){const t=document.querySelectorAll("span");for(const e of t){if(p.contains(e))continue;if("Your position"!==(e.textContent||"").trim())continue;if(!T(e))continue;const t=M(e.closest(".justify-between")||e.closest("div"));if(t)return t}const e=document.querySelectorAll('svg[data-testid="CheckCircleIcon"]');for(const t of e){if(p.contains(t))continue;if(!T(t))continue;const e=M(t.closest(".justify-between")||t.closest("div"));if(e)return e}const n=document.querySelectorAll('span[class*="text-yes"], span[class*="text-no"]');for(const t of n){if(p.contains(t))continue;if(!T(t))continue;const e=(t.textContent||"").replace(/\s+/g," ").trim().match(B);if(!e)continue;const n=parseFloat(e[1].replace(/,/g,""));if(Number.isFinite(n))return{qty:n,side:e[2].toLowerCase()}}return{qty:0,side:null}}();x.textContent=t?Math.round(t):0,Y=e,"yes"===e?(k.textContent="SELL UP",H.textContent="BUY DOWN"):"no"===e?(k.textContent="BUY UP",H.textContent="SELL DOWN"):(k.textContent="BUY UP",H.textContent="BUY DOWN")}function W(){const t=function(){const t=document.querySelectorAll("span");for(const e of t)if(!p.contains(e)&&(e.style.fontFeatureSettings||"").includes("tnum")){const t=(e.textContent||"").trim();if(/^\d{1,2}:\d{2}$/.test(t))return e}return null}();if(!t)return E.textContent="--",void F();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());null!==e?(E.textContent=e,e<=1&&F()):E.textContent="--"}const z=p.querySelector("#dollarsBox");function K(){const t=(z.value||"").trim();if(!t)return h("FILL DOLLARS: empty");const e=t.replace(/[^\d.]/g,"");if(!e)return h("FILL DOLLARS: invalid");const n=document.querySelector("#cost-input")||null;if(!n)return h("FILL DOLLARS: dollars input not found");n.focus(),O(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(),h(``FILL DOLLARS: ${e}``)}function V(){return[...document.querySelectorAll("button")].find(t=>!p.contains(t)&&("review"===U(t.textContent)&&!t.disabled))||null}function j(){return[...document.querySelectorAll("button")].find(t=>!p.contains(t)&&("submit"===U(t.textContent)&&!t.disabled))||null}async function X(t,e,n=6e4){const o=Date.now();let i=100;for(;Date.now()-o<n;){const e=t();if(e)return e;await q(i),i=Math.min(1.5*i,1e3)}return null}async function J(t){const{aEl:e,bEl:n}=_(),o="UP"===t?e:n;if(!o)return h(``BUY ${t}: pill not found``);h(``BUY ${t}: select``),O(o),await q(200),h(``BUY ${t}: fill dollars``),K(),await q(300),h(``BUY ${t}: waiting for Review``);const i=await X(V);if(!i)return h(``BUY ${t}: Review not found``);h(``BUY ${t}: click Review``),O(i),h(``BUY ${t}: waiting for Submit``);const l=await X(j);if(!l)return h(``BUY ${t}: Submit not found``);h(``BUY ${t}: click Submit``),O(l),h(``BUY ${t}: DONE``)}function Z(){return[...document.querySelectorAll('button[type="button"]')].find(t=>!p.contains(t)&&"sell"===U(t.textContent))||null}async function G(t){const{aEl:e,bEl:n}=_(),o="UP"===t?e:n;if(!o)return h(``SELL ${t}: pill not found``);h(``SELL ${t}: select pill``),O(o),await q(300),h(``SELL ${t}: switching to Sell tab``);const i=await X(Z);if(!i)return h(``SELL ${t}: Sell tab not found``);O(i),await q(300),h(``SELL ${t}: fill dollars``),K(),await q(300),h(``SELL ${t}: waiting for Review``);const l=await X(V);if(!l)return h(``SELL ${t}: Review not found``);h(``SELL ${t}: click Review``),O(l),h(``SELL ${t}: waiting for Submit``);const r=await X(j);if(!r)return h(``SELL ${t}: Submit not found``);h(``SELL ${t}: click Submit``),O(r),h(``SELL ${t}: DONE``)}function Q(){const t=function(){const t=document.querySelectorAll(".animate-ping");for(const e of t){if(p.contains(e))continue;const t=e.closest("a, button");if(t&&!p.contains(t)&&/\b\d{1,2}:\d{2}\s*(AM|PM)\b/i.test(t.textContent||""))return t}return null}();if(!t)return h("LIVE SESSION: no live session found");h("LIVE SESSION: clicking "+(t.textContent||"").trim()),O(t)}function tt(){const t=document.querySelectorAll("button");for(const e of t){if(p.contains(e))continue;if("done"!==U(e.textContent))continue;if(e.closest("div.sticky"))return h("AUTO-DISMISS: clicking Done"),void O(e)}}z.addEventListener("input",()=>localStorage.setItem(i,z.value)),p.querySelector("#fillDollars").onclick=()=>K(),k.onclick=()=>"yes"===Y?G("UP"):J("UP"),H.onclick=()=>"no"===Y?G("DOWN"):J("DOWN"),p.querySelector("#liveSession").onclick=()=>Q(),p.querySelector("#copyPrice").onclick=()=>{let t=null;if("yes"===Y){const e=v.textContent.trim();"--"!==e&&(t=e)}else if("no"===Y){const e=w.textContent.trim();"--"!==e&&(t=e)}if(null===t)return h("COPY: no position to copy");navigator.clipboard.writeText(t).then(()=>h(``COPIED: ${t}``),()=>h("COPY: clipboard failed"))};const et=new MutationObserver(t=>{for(const e of t)if(!p.contains(e.target))return void d()});et.observe(document.body,{childList:!0,subtree:!0,characterData:!0}),e.observers.push(et),e.timerInterval&&clearInterval(e.timerInterval),e.timerInterval=setInterval(()=>{u(W,R,tt)},1e3),d(),h("Kalshi HUD: ON")})();
)
LoadIndicator() {
global Script
Clipboard :=
Clipboard := Script
ClipWait, 1
SendInput, ^+j
Sleep 1200
SendInput, ^v
Sleep 1200
SendInput, allow pasting
Sleep 4000
SendInput, {Enter}
Sleep 2000
SendInput, ^v
Sleep 1500
SendInput, {Enter}
Sleep 1000
SendInput, ^+j
}
click(point.a)
LoadIndicator()
Nightshark TradingBot code
triggerPoint := 70 ; BUY Price Point
exitPoint := 40 ; Stop Loss price point
triggerMinute := 14 ; remaining minutes
;WARNING: TriggerMinute cannot be greater than 15. This is exclusively for 15 minute Market
Log("Application Started !!")
loop {
click(point.c)
Log("Waiting for trade window last " . triggerMinute . " Minutes")
loop {
if (isXMinRemaining(triggerMinute))
break
sleep 500
}
Log("Monitoring UP & Down to hit Trigger Price > " . triggerPoint )
loop{
read_areas()
sleep 50 ; Adding a small sleep to prevent excessive CPU usage
} until (toNumber(area[1]) > triggerPoint || toNumber(area[2]) > triggerPoint || is5SecBeforeQuarter() )
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
}
}
if(toNumber(area[4]) > 0) {
Log("Monitoring StopLoss @" . exitPoint . " or Resolution")
loop {
read_areas()
if (toNumber(area[1]) < exitPoint) {
if(DoubleExitCheck() < exitPoint) {
break
}
}
sleep 50 ; Adding a small sleep to prevent excessive CPU usage
} until (is5SecBeforeQuarter())
if (toNumber(area[1]) < exitPoint && DoubleExitCheck() < exitPoint && toNumber(area[1]) > 2) {
Log("Stop Loss Triggered !! Closing UP Position")
loop, 4 {
click(point.a)
sleep 4000
read_areas()
if (toNumber(area[4]) =0) {
Log("UP Position closed ")
break
}
else {
Log("Position not confirmed ! Retrying ...")
sleep 1000
}
}
}
}
else {
Log("Position did not filled on 4 retries ! Skipping to next session")
}
}
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
}
}
if(toNumber(area[4]) > 0) {
Log("Monitoring StopLoss @" . exitPoint . " or Resolution")
loop {
read_areas()
if (toNumber(area[2]) < exitPoint) {
if(DoubleExitCheck() < exitPoint) {
break
}
}
sleep 50 ; Adding a small sleep to prevent excessive CPU usage
} until (is5SecBeforeQuarter())
if (toNumber(area[2]) < exitPoint && DoubleExitCheck() < exitPoint && toNumber(area[2]) > 2) {
Log("Stop Loss Triggered !! Closing Down Position")
loop, 4 {
click(point.b)
sleep 4000
read_areas()
if (toNumber(area[4]) = 0) {
Log("Down Position closed ")
break
}
else {
Log("Position not confirmed ! Retrying ...")
sleep 1000
}
}
}
}
else {
Log("Position did not filled on 4 retries ! Skipping to next session")
}
}
else if (toNumber(area[4]) > 0) {
Log("Position already exists , skipping this session")
}
Log("Waiting for Next Session")
loop {
read_area(3)
} until (is5SecBeforeQuarter())
Log("Loading New Session")
sleep 5000
click(point.c)
}
isXMinRemaining(x) {
currentMinute := A_Min + 0
remaining := 15 - Mod(currentMinute, 15)
if (remaining <= x)
return true
else
return false
}
is5SecBeforeQuarter() {
minute := A_Min + 0
second := A_Sec + 0
if (Mod(minute, 15) = 14 && second >= 55) {
Log("Last 5 second of session. waiting for new session")
return true
}
else {
return false
}
}
DoubleExitCheck() {
click(point.d)
sleep 1000
value := Clipboard
return value
}