Sadly, @SilverWolf is no longer an active member of the forum
(to see who is active; click on their avatar)
@useThinkScript
I wanted to share what I have been up to with the AHK script being developed in this thread. It has been a work in progress based on all previous contributors to this thread, many of which sadly are not active.
To use this you will need to set all the coordinates and images files. For the ToS warning Boxes, I don't have them very often at all so it has been a difficult test for me, if others can help on this it would be greatly appreciated. ( I am on 3820x2160 res, I use a 2 pane main window with just trade and chart active, You will absolutely need to use the Window Spy to confirm your cursor location and pixel color particularly for the "ToS Sync" and "study addlabel" calls, I currently am using "Position Indicator" as THE first item on the trade settings tab, as well as the label for your indicator which should be on the top left of your price pane).
You will need to add the GUI stuff that @SilverWolf and perhaps @Svanoy had previously posted. The GUI shows current variables and history, and can display debug if enabled.
I was hoping to speed this thing up, so got rid of all the mouse click stuff except for error and window refocus click stuff. (There are hotkey delays to adjust for your system/connection/lag times) Please check the top of the code comments, and I keep my main window active if script is active as a precaution)
It now acts on the same study label colors, but uses pixel colors info from a cursor position on your screen.
It also has a sync function that reads the platform position in the same manner.
Instead of clicking buy, sell, flat, reverse it now just uses Alt-Keys, Alt-B, Alt-S, Alt-F, Alt-R this has sped the script up tremendously.
If anyone has been using any previous versions or was dabbling with it, I hope this gets you either right there, or closer, quicker to what you want.
Remember, this will only act on your studies labels...use on high volume/news events etc sparingly
Also, don't forget to right click "run as administrator" and hit "ESC" to start it (and move the GUI somewhere that won't cover up your cursor pixel locations), Ctrl-X to EXIT and "Ctrl-P" to pause/unpause, there are a few more Ctrl-Keys as well
Cheers!
I'll attach a .zip of my working directory shortly (sadly too large give me a few)
https://drive.google.com/file/d/1NODJGPgrGTMcUT14pTpFns7szP4a5zYV/view?usp=sharing
Code:
; Created/Shared by Kevin N altered by SilverWolf
;https://usethinkscript.com/threads/auto-trade-algo-in-tos.7546/page-15#post-116225
; ================================================================
; Modified auto-trade-algo-in-tos > Sheriff Signal Follower
; ================================================================
; Logic:
;
; - On start (ESC): scan and enter long (white) or short (orange)
; - LONG + orange signal : reverse directly via Alt+R -> SHORT
; - SHORT + white signal : reverse directly via Alt+R -> LONG
; - Blue signal : flatten immediately, stay flat
; - FLAT + white signal : go long via Alt+B
; - FLAT + orange signal : go short via Alt+S
;
; Hotkeys Ctrl-?
;
; ^P:: Pause / Resume
; ^E:: manually trigger error check
; ^R:: reset trade counter to 0
; ^0:: manually override status to FLAT
; ^1:: manually override status to LONG
; ^2:: manually override status to SHORT
; ^D:: Pausing for 15 minutes
; ^F1:: Pausing for 1 hour
; ^F2:: Pausing for 2 hours
; ^F4:: Pausing for 4 hours
; ^X:: ExitApp
; ================================================================
#NoEnv
#SingleInstance Force
SendMode Input
SetWorkingDir C:\Users\Owner\Documents\AutoHotkey\Sheriff Follower
CoordMode, Pixel, Screen
CoordMode, Mouse, Screen
CoordMode, Tooltip, Screen
CoordMode, Caret, Screen
CoordMode, Menu, Screen
SetTitleMatchMode, 2 ; partial title match — build number bumps won't break focus
; ================================================================
; User Defineable Variables
; ================================================================
; Pixel coordinate to sample for signal color
signalX = 300
signalY = 405
; Signal colors (format: 0xRRGGBB)
; Run in debugMode=1 first to capture exact values, then paste here
whiteColor = 0xFFFFFF ; long signal
orangeColor = 0xFF7F00 ; short signal
blueColor = 0x0000FF ; neutral / flat signal
; Color match tolerance (0 = exact, higher = more tolerant)
; Increase if anti-aliasing causes slight color variation
colorVariance = 20
; Trade management
tradeLimit = 12 ; max trades before sitting out
resetPeriod = 900 ; seconds to sit out after tradeLimit hit
clearCounterPeriod = 3600 ; seconds before trade counter + j reset
entryPause = 1 ; seconds to pause before entry (0 = disabled)
startDelay = 2000 ; ms to wait after ESC before first scan
; Key send delays in ms (tune if platform is missing keystrokes)
buyKeyDelay = 750 ; delay after Alt+B
sellKeyDelay = 750 ; delay after Alt+S
flatKeyDelay = 750 ; delay after Alt+F
reverseKeyDelay = 750 ; delay after Alt+R
; TOS window title — partial match is active (SetTitleMatchMode 2 above)
; so just "thinkorswim" matches regardless of build number
tosWindowTitle = thinkorswim
; 1 = show raw pixel color every tick (use to calibrate color values)
; 0 = normal status display once colors are confirmed
debugMode = 0
; ----------------------------------------------------------------
; ToS Sync pixel — verifies platform position matches script state
; ----------------------------------------------------------------
tosSyncX = 135
tosSyncY = 261
tosSyncGreen = 0x3A6347 ; GREEN -> platform is LONG
tosSyncRed = 0x491414 ; RED -> platform is SHORT
tosSyncGrey = 0x5D5E60 ; GREY -> platform is FLAT
tosSyncYellow = 0x9D681D ; YELLOW -> platform is FLAT
tosSyncVariance = 30
; Max contracts per position (hard cap — script will not send more
; Buy/Sell/Reverse keystrokes once this many contracts are open)
maxPositionSize = 1
; ----------------------------------------------------------------
; Bounding box for TOS error dialog ImageSearch
; ----------------------------------------------------------------
errorSearchX1 = 1000
errorSearchY1 = 0
errorSearchX2 = 2500
errorSearchY2 = 1300
; ================================================================
; End User Defineable Variables
; ================================================================
; Script state
status = 0 ; 0=Flat 1=Long 2=Short
trades = 0
tosErrors = 0
j = 0 ; lifetime reset counter
firstRun = 1
tosSyncState = UNKNOWN
lastAction = NONE
tosActivated = 0
currentContracts = 0
stopTimer = 0
; Pre-initialize display variables
pixColor = 0x000000
whiteFound = 0
orangeFound = 0
blueFound = 0
syncMismatch = 0
syncRawColor = 0x000000
loopStartTime := A_TickCount
; ================================================================
; Always-on-top GUI status window
; ================================================================
Gui, Status:New, +AlwaysOnTop +ToolWindow
Gui, Status:Color, 111111, 111111
Gui, Status:Font, s15 bold, Courier New
Gui, Status:Add, Text, vStatusLine1 cWhite w500 h20, Initializing...
Gui, Status:Add, Text, vStatusLine2 cWhite w500 h20,
Gui, Status:Add, Text, vStatusLine3 cWhite w500 h20,
Gui, Status:Show, NoActivate x10 y10, Raptor Status
; ================================================================
; Hotkeys
; ================================================================
^P::
if (A_IsPaused) {
Pause, Off
gosub UpdateStatus
} else {
GuiControl, Status:, StatusLine1, *** PAUSED ***
GuiControl, Status:, StatusLine2, Press Ctrl+P to resume
GuiControl, Status:, StatusLine3,
Pause, On, 1
}
return
^E:: checkForErrors()
^R:: trades = 0
^0::
status = 0
currentContracts = 0
return
^1::
status = 1
currentContracts = %maxPositionSize%
return
^2::
status = 2
currentContracts = %maxPositionSize%
return
^D:: delayFunction(900, "Pausing for 15 minutes")
^F1:: delayFunction(3600, "Pausing for 1 hour")
^F2:: delayFunction(7200, "Pausing for 2 hours")
^F4:: delayFunction(14400, "Pausing for 4 hours")
^X:: ExitApp
; ================================================================
; ESC: start main loop
; ================================================================
ESC::
gosub UpdateStatus
loop {
; ---------------------------------------------------------------
; Tick pacing
; ---------------------------------------------------------------
if (firstRun = 1) {
firstRun = 0
sleep %startDelay%
} else {
sleep 1000
}
FormatTime, TimeString,, Time
; ---------------------------------------------------------------
; Sample signal pixel
; ---------------------------------------------------------------
PixelGetColor, pixColor, %signalX%, %signalY%, RGB
whiteFound := colorMatch(pixColor, whiteColor, colorVariance)
orangeFound := colorMatch(pixColor, orangeColor, colorVariance)
blueFound := colorMatch(pixColor, blueColor, colorVariance)
; ---------------------------------------------------------------
; Sample ToS sync pixel
; ---------------------------------------------------------------
gosub CheckTosSync
; ---------------------------------------------------------------
; Silent sync runs FIRST, updating status BEFORE
; syncMismatch is calculated — prevents a valid platform state
; that merely needs adoption from triggering full recovery.
; ---------------------------------------------------------------
if (tosSyncState = "GREEN" and whiteFound and status != 1) {
status = 1
currentContracts = %maxPositionSize%
} else if (tosSyncState = "RED" and orangeFound and status != 2) {
status = 2
currentContracts = %maxPositionSize%
}
; ---------------------------------------------------------------
; Calculate platformPos and syncMismatch AFTER silent sync
; ---------------------------------------------------------------
platformPos := (tosSyncState = "GREEN") ? 1 : (tosSyncState = "RED") ? 2 : 0
syncMismatch := (platformPos != status) ? 1 : 0
; ---------------------------------------------------------------
; NEUTRAL (blue): flatten immediately, stay flat
; Checked before mismatch recovery so blue always wins
; ---------------------------------------------------------------
if (blueFound and !whiteFound and !orangeFound) {
if (status == 1 or status == 2) {
lastAction = BLUE-FLATTEN
gosub DoFlat
}
gosub UpdateStatus
continue
}
; ---------------------------------------------------------------
; Mismatch recovery
;
; Old code: after sending !f it fell through into the re-entry
; block using the PRE-FLATTEN platformPos — causing double-sends
; and the stuck position you saw after a few trades.
;
; after flatten confirms we call CheckTosSync again and
; recalculate platformPos from live data before re-entering.
; The recovery also ends with a `continue` so normal signal
; logic is skipped this tick and re-evaluated cleanly next tick.
; ---------------------------------------------------------------
if (syncMismatch = 1) {
; Step 1 — flatten if platform is currently in a position
if (platformPos != 0) {
gosub ActivateTos
if (tosActivated) {
Send !f
sleep %flatKeyDelay%
currentContracts = 0
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "GREY" or tosSyncState = "YELLOW" or tosSyncState = "UNKNOWN")
break
}
}
}
; re-sample sync pixel and recalculate
; platformPos from LIVE data before making re-entry decision
gosub CheckTosSync
platformPos := (tosSyncState = "GREEN") ? 1 : (tosSyncState = "RED") ? 2 : 0
; re-enter only if platform is now flat and below cap
if (status == 1 and platformPos == 0 and currentContracts < maxPositionSize) {
lastAction = SYNC-BUY
gosub ActivateTos
if (tosActivated) {
contractsToSend := maxPositionSize - currentContracts
Loop, %contractsToSend% {
Send !b
sleep %buyKeyDelay%
}
currentContracts := maxPositionSize
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "GREEN")
break
}
}
} else if (status == 2 and platformPos == 0 and currentContracts < maxPositionSize) {
lastAction = SYNC-SELL
gosub ActivateTos
if (tosActivated) {
contractsToSend := maxPositionSize - currentContracts
Loop, %contractsToSend% {
Send !s
sleep %sellKeyDelay%
}
currentContracts := maxPositionSize
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "RED")
break
}
}
}
; Final sync state refresh and mismatch recalc before display
gosub CheckTosSync
platformPos := (tosSyncState = "GREEN") ? 1 : (tosSyncState = "RED") ? 2 : 0
syncMismatch := (platformPos != status) ? 1 : 0
gosub UpdateStatus
continue ; skip normal signal logic; re-evaluate next tick
}
; ---------------------------------------------------------------
; FLAT: enter on first directional signal
; ---------------------------------------------------------------
if (status == 0) {
if (whiteFound and !orangeFound and !blueFound and tosSyncState != "GREEN" and currentContracts < maxPositionSize) {
lastAction = FLAT-BUY
gosub DoLong
} else if (orangeFound and !whiteFound and !blueFound and tosSyncState != "RED" and currentContracts < maxPositionSize) {
lastAction = FLAT-SELL
gosub DoShort
}
}
; ---------------------------------------------------------------
; LONG: orange signal = reverse to short
; status set to TARGET state BEFORE gosub DoReverse
; so every internal CheckTosSync/UpdateStatus call inside
; DoReverse sees the correct intended state — prevents a false
; mismatch from firing on the very next tick.
; ---------------------------------------------------------------
else if (status == 1) {
if (orangeFound and !whiteFound and !blueFound and tosSyncState != "RED") {
lastAction = LONG-REVERSE-SHORT
status = 2 ; [B2 FIX] set target first
gosub DoReverse
}
}
; ---------------------------------------------------------------
; SHORT: white signal = reverse to long
; same pattern — status = 1 before gosub DoReverse
; ---------------------------------------------------------------
else if (status == 2) {
if (whiteFound and !orangeFound and !blueFound and tosSyncState != "GREEN") {
lastAction = SHORT-REVERSE-LONG
status = 1 ; [B2 FIX] set target first
gosub DoReverse
}
}
; ---------------------------------------------------------------
; Update GUI
; ---------------------------------------------------------------
gosub UpdateStatus
; ---------------------------------------------------------------
; Trade limit: flatten, sit out, then resume (max 2 resets)
; ---------------------------------------------------------------
if (trades >= tradeLimit) {
trades = 0
gosub DoFlat
GuiControl, Status:, StatusLine1, TRADE LIMIT - Sitting out %resetPeriod%s
GuiControl, Status:, StatusLine2,
GuiControl, Status:, StatusLine3,
resetMs := resetPeriod * 1000
sleep %resetMs%
j++
if (j == 2) {
GuiControl, Status:, StatusLine1, MAX RESETS REACHED - EXITING
GuiControl, Status:, StatusLine2,
GuiControl, Status:, StatusLine3,
ExitApp
}
}
; ---------------------------------------------------------------
; Wall-clock periodic reset
; ---------------------------------------------------------------
if (A_TickCount - loopStartTime >= clearCounterPeriod * 1000) {
loopStartTime := A_TickCount
trades = 0
j = 0
checkForErrors()
}
}
return
; ================================================================
; SUBROUTINES
; ================================================================
; ----------------------------------------------------------------
; ActivateTos — bring TOS window to focus
; ----------------------------------------------------------------
ActivateTos:
WinActivate, %tosWindowTitle%
WinWaitActive, %tosWindowTitle%,, 2
if (ErrorLevel) {
tosActivated = 0
GuiControl, Status:, StatusLine1, ERROR: TOS window not found!
GuiControl, Status:, StatusLine2, Check tosWindowTitle setting
GuiControl, Status:, StatusLine3,
} else {
tosActivated = 1
}
if (debugMode = 1)
GuiControl, Status:, StatusLine3, DBG ActivateTos: EL=%ErrorLevel% activated=%tosActivated%
return
; ----------------------------------------------------------------
; UpdateStatus — refresh GUI
; ----------------------------------------------------------------
UpdateStatus:
statusLabel := (status == 0) ? "FLAT" : (status == 1) ? "LONG" : "SHORT"
pauseLabel := A_IsPaused ? " [PAUSED]" : ""
FormatTime, TimeString,, Time
if (debugMode = 1) {
line1 := "DBG " . statusLabel . pauseLabel . " " . TimeString
line2 := "PIX=" . pixColor . " W=" . whiteFound . " O=" . orangeFound . " B=" . blueFound
line3 := "TOS=" . tosSyncState . " RAW=" . syncRawColor . " MM=" . syncMismatch . " POS=" . currentContracts
} else if (syncMismatch = 1) {
line1 := statusLabel . pauseLabel . " MISMATCH " . TimeString
line2 := "TOS-SYNC: " . tosSyncState . " Trades: " . trades . "/" . tradeLimit
line3 := "Last: " . lastAction . " Contracts: " . currentContracts . "/" . maxPositionSize
} else {
line1 := statusLabel . pauseLabel . " " . TimeString
line2 := "TOS-SYNC: " . tosSyncState . " Trades: " . trades . "/" . tradeLimit
line3 := "Errors: " . tosErrors . " Last: " . lastAction . " Contracts: " . currentContracts . "/" . maxPositionSize
}
GuiControl, Status:, StatusLine1, %line1%
GuiControl, Status:, StatusLine2, %line2%
GuiControl, Status:, StatusLine3, %line3%
return
; ----------------------------------------------------------------
; CheckTosSync — sample sync pixel and set tosSyncState
; ----------------------------------------------------------------
CheckTosSync:
PixelGetColor, syncColor, %tosSyncX%, %tosSyncY%, RGB
syncRawColor := syncColor
if (colorMatch(syncColor, tosSyncGreen, tosSyncVariance)) {
tosSyncState = GREEN
} else if (colorMatch(syncColor, tosSyncRed, tosSyncVariance)) {
tosSyncState = RED
} else if (colorMatch(syncColor, tosSyncGrey, tosSyncVariance)) {
tosSyncState = GREY
} else if (colorMatch(syncColor, tosSyncYellow, tosSyncVariance)) {
tosSyncState = YELLOW
} else {
tosSyncState = UNKNOWN
}
return
; ----------------------------------------------------------------
; colorMatch — returns 1 if two colors are within variance
; ----------------------------------------------------------------
colorMatch(c1, c2, variance) {
r1 := (c1 >> 16) & 0xFF
g1 := (c1 >> 8) & 0xFF
b1 := c1 & 0xFF
r2 := (c2 >> 16) & 0xFF
g2 := (c2 >> 8) & 0xFF
b2 := c2 & 0xFF
return (Abs(r1-r2) <= variance && Abs(g1-g2) <= variance && Abs(b1-b2) <= variance)
}
; ----------------------------------------------------------------
; entryPauseCheck — countdown before entry; aborts if blue seen
; ----------------------------------------------------------------
entryPauseCheck(label) {
global entryPause, signalX, signalY, blueColor, colorVariance, debugMode
k := entryPause
while (k > 0) {
GuiControl, Status:, StatusLine1, %label% in %k%s...
sleep 1000
PixelGetColor, chkColor, %signalX%, %signalY%, RGB
blueDetected := colorMatch(chkColor, blueColor, colorVariance)
if (debugMode = 1)
GuiControl, Status:, StatusLine3, DBG entryPause: chk=%chkColor% detected=%blueDetected%
if (blueDetected)
return 0
k--
}
return 1
}
; ----------------------------------------------------------------
; DoLong — enter long from flat via Alt+B
; ----------------------------------------------------------------
DoLong:
if (currentContracts >= maxPositionSize)
return
if (entryPause > 0) {
if (!entryPauseCheck("Going Long"))
return
}
gosub ActivateTos
if (!tosActivated)
return
contractsToSend := maxPositionSize - currentContracts
Loop, %contractsToSend% {
Send !b
sleep %buyKeyDelay%
}
currentContracts := maxPositionSize
status = 1
trades++
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "GREEN")
break
}
gosub UpdateStatus
return
; ----------------------------------------------------------------
; DoShort — enter short from flat via Alt+S
; ----------------------------------------------------------------
DoShort:
if (currentContracts >= maxPositionSize)
return
if (entryPause > 0) {
if (!entryPauseCheck("Going Short"))
return
}
gosub ActivateTos
if (!tosActivated)
return
contractsToSend := maxPositionSize - currentContracts
Loop, %contractsToSend% {
Send !s
sleep %sellKeyDelay%
}
currentContracts := maxPositionSize
status = 2
trades++
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "RED")
break
}
gosub UpdateStatus
return
; ----------------------------------------------------------------
; DoReverse — flip position via Alt+R
; IMPORTANT: caller must set status to the TARGET state BEFORE
; calling this subroutine so internal sync checks are accurate.
; ----------------------------------------------------------------
DoReverse:
if (entryPause > 0) {
if (!entryPauseCheck("Reversing"))
return
}
gosub ActivateTos
if (!tosActivated)
return
Send !r
sleep %reverseKeyDelay%
currentContracts := maxPositionSize
trades++
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "GREEN" or tosSyncState = "RED")
break
}
gosub UpdateStatus
return
; ----------------------------------------------------------------
; DoFlat — flatten position via Alt+F
; ----------------------------------------------------------------
DoFlat:
gosub ActivateTos
if (!tosActivated)
return
Send !f
sleep %flatKeyDelay%
status = 0
currentContracts = 0
Loop, 10 {
sleep 100
gosub CheckTosSync
if (tosSyncState = "GREY" or tosSyncState = "YELLOW" or tosSyncState = "UNKNOWN")
break
}
gosub UpdateStatus
return
; ----------------------------------------------------------------
; FormatSeconds — format seconds as h:mm:ss
; ----------------------------------------------------------------
FormatSeconds(NumberOfSeconds) {
time := 19990101
time += NumberOfSeconds, seconds
FormatTime, mmss, %time%, mm:ss
return NumberOfSeconds//3600 ":" mmss
}
; ----------------------------------------------------------------
; delayFunction — countdown pause with live GUI update
; ----------------------------------------------------------------
delayFunction(SecondsToWait, Message) {
global stopTimer
stopTimer = 1
k := SecondsToWait
while (k > 0) {
if (stopTimer != 1)
break
timeLeft := FormatSeconds(k)
GuiControl, Status:, StatusLine1, %Message%
GuiControl, Status:, StatusLine2, %timeLeft%
GuiControl, Status:, StatusLine3,
sleep 1000
k--
}
stopTimer = 0
}
; ----------------------------------------------------------------
; checkForErrors — find and dismiss TOS error dialogs
; ----------------------------------------------------------------
checkForErrors() {
global tosErrors, debugMode
global errorSearchX1, errorSearchY1, errorSearchX2, errorSearchY2
errorFound = 0
ImageSearch, FoundX, FoundY, %errorSearchX1%, %errorSearchY1%, %errorSearchX2%, %errorSearchY2%, tosWarningOk.bmp
if (ErrorLevel = 0) {
FoundX := FoundX + 5
FoundY := FoundY + 5
MouseClick, Left, %FoundX%, %FoundY%, 1, 0
tosErrors++
errorFound = 1
GuiControl, Status:, StatusLine3, OK Warning Cleared: %tosErrors% at %FoundX%x%FoundY%
} else if (debugMode = 1) {
GuiControl, Status:, StatusLine3, DBG: tosWarningOk.bmp not found (EL=%ErrorLevel%)
}
ImageSearch, FoundX, FoundY, %errorSearchX1%, %errorSearchY1%, %errorSearchX2%, %errorSearchY2%, tosWarningContinue.png
if (ErrorLevel = 0) {
FoundX := FoundX + 10
FoundY := FoundY + 10
MouseClick, Left, %FoundX%, %FoundY%, 1, 0
tosErrors++
errorFound = 1
GuiControl, Status:, StatusLine3, CONTINUE Warning Cleared: %tosErrors% at %FoundX%x%FoundY%
} else if (debugMode = 1) {
GuiControl, Status:, StatusLine3, DBG: tosWarningContinue.png not found (EL=%ErrorLevel%)
}
if (errorFound = 1)
MouseClick, Left, 2836, 846, 1, 0
return
}
Last edited: