#Requires AutoHotkey v2.0
; v 3.3 - Added third signal (long, short and now flatten).
; v 3.2 - Fixed keeping track of the current order and what to do when the signal changes.
; v 3.1 - Added global error messages
; v 3.0 - Updated GUI - rid of start/stop TOS. Use labels to display statuses.
; v 2.9 - Added move limit to defined $profit. Code to progress towards increased profit with limit moves. Moved events file to screenshot folder. Get price from position panel.
; v 2.8 - Added don't reenter trade if limited out. Wait for next signal. Find P/L.
; v 2.7 - Added flattenout subroutine to get out of trade instead of limitmove
; v 2.6 - Added screenshot to each cycle.
; v 2.5 - Moved buy sell to subroutines. changed limit move to % move towards price. changed trigger to be change of color right corner.
; v 2.4 - Handle orange color = flatten without buying, close excel and remove lock from editing
; v 2.3 - Open workbooks line 120
; v 2.2 - Added 15 min pause after buy or sell. This will stop double purchasing if stopped out.
; v 2.1 - Stable release
; FORCE Run as Administrator
full_command_line := DllCall("GetCommandLine", "str")
if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
try
{
if A_IsCompiled
Run '*RunAs "' A_ScriptFullPath '" /restart'
else
Run '*RunAs "' A_AhkPath '" /restart "' A_ScriptFullPath '"'
}
ExitApp
}
xl_filePath := A_Desktop . "\SCREENSHOTS\Autotrade_Events.xlsx"
try {
xl := ComObjActive("Excel.Application")
} catch {
xl := ComObject("Excel.Application")
}
; Open the workbook and make the Excel application visible.
wb := xl.Workbooks.Open(xl_filePath)
ws := wb.Worksheets("Sheet1")
Capture2TextPath := A_Desktop . "\Capture2Text\Capture2Text_CLI.exe"
Destination := A_Desktop . "\SCREENSHOTS"
IfNotExist(Destination) ; if not already a folder create one
{
DirCreate(Destination)
}
CoordMode "Pixel", "Screen"
CoordMode "Mouse", "Screen"
; signal between bar changes
cyan_signal := 0X00FFFF ; CYAN used for bar changes
yellow_signal := 0xFFFF00 ; YELLOW used for bar chagens
magenta_signal := 0xFF00FF ; MAGENTA
; where to look for bar change (each bar change in upper right) this is for timing.
bar_xpos := 36
bar_ypos := 204
; signal colors to trigger events
green_signal := 0x90EE90 ; LIGHT_GREEN used for long
red_signal := 0xFF3F00 ; LIGHT_RED used for short
orange_signal := 0xFFA500 ; LIGHT_ORANGE used for flatten
; where to look for main buy/sell signal
signal_xpos := 95
signal_ypos := 204
; colors of many buttons used
buymkt_inactive_color := 0x08663A
buymkt_active_color := 0x0A7F48
sellmkt_inactive_color := 0x973030
sellmkt_active_color := 0xAE3D3D
flatten_inactive_color := 0x4A4A4A
flatten_active_color := 0x595959
sell_order_color := 0x922E2E
buy_order_color := 0X075832
; where are limit and stops relative to position
limit_banner_color_green := 0x075832
limit_banner_color_red := 0X96110E
limit_banner_height := 20
limit_banner_width := 120
limit_banner_top_x := 820
limit_banner_top_y := 184
limit_banner_bot_x := 940
limit_banner_bot_y := 715
; shows when in a trade this is the red or green bubble with a line - find the bubble
position_banner_color_red := 0X96110E
position_banner_color_green := 0x075832
position_banner_height := 20
position_banner_width := 120
position_banner_top_x := 25
position_banner_top_y := 215
position_banner_bot_x := 145
position_banner_bot_y := 733
; used for test and click function. test to see if button is there before pressing it
buymkt_ul_xpos := 1520
buymkt_ul_ypos := 143
buymkt_dr_xpos := 1597
buymkt_dr_ypos := 171
sellmkt_ul_xpos := 1620
sellmkt_ul_ypos := 143
sellmkt_dr_xpos := 1697
sellmkt_dr_ypos := 171
flatten_ul_xpos := 1719
flatten_ul_ypos := 143
flatten_dr_xpos := 1790
flatten_dr_ypos := 171
; to open the position panel
pos_button_x := 125
pos_button_y := 1012
; to find the order short or long in the position panel
order_topx := 1334
order_topy := 948
order_botx := 1344
order_boty := 1020
; was supposed to hide the teamviewer panel
teamviewer_xpos := 1590
teamviewer_ypos := 921
teamviewer_color_xpos := 1780
teamviewer_color_ypos := 840
teamviewer_panel_xpos := 1592
teamviewer_panel_ypos := 891
teamviewer_color := 0x2747D0
; to keep the window refreshed
return_x := 1167
return_y := 586
;#####################
global debug_mode := 0 ; Set debug_mode to 1 (one) to have messageboxes show up which subroutine is being called
;#####################
ExcelAppend(ws, "Program Start", "Autotrade")
TimeString := "Time: " . FormatTime(, "HH:mm.ss")
BarChangeString := "Bar Change: " . FormatTime(, "HH:mm.ss")
SignalString := "No Signal Change"
ErrorString := "No Errors Seen"
MyGui := Gui()
MyGui.SetFont("s14 w700", "Segoe UI")
MyGui.Add("Text", "Center w230", "Autotrade for the win!")
TimeDisplay := MyGui.Add("Text", "Center w230", TimeString)
BarChangeDisplay := MyGui.Add("Text", "Center w230", BarChangeString)
SignalChangeDisplay := MyGui.Add("Text", "Center w230", SignalString)
MyGui.SetFont("s10 w700", "Segoe UI")
ErrorDisplay := MyGui.Add("Edit", "r10 w230 ReadOnly +Wrap +Vertical", ErrorString)
Spacer := MyGui.Add("Text", "Center w230", " ")
Radio1 := MyGui.AddRadio("w230", "Press to Run Watch/Trade")
Radio1.OnEvent("Click", sub_WatchTrade)
Radio2 := MyGui.AddRadio("w230", "Press to Pause Watch/Trade")
Radio2.OnEvent("Click", sub_PauseTrade)
Radio8 := MyGui.AddRadio("w230", "Press to Close Watch/Trade")
Radio8.OnEvent("Click", sub_CloseAT)
MyGui.Show("w250")
OnError(MyErrorHandler)
MyErrorHandler(err, mode) {
ErrorDisplay.Value := err.Message . " at: " . FormatTime(, "HH:mm.ss")
return 1
}
sub_WatchTrade(*)
{
initial_restart := 1 ; On initial restart, disregard the first signal or we'll process a LONG, SHORT or FLATTEN
last_limit_move := -1 ; Keep track of the last limit dollar move. Force this only to increase.
Try
{
prev_bar_color := 0x123456 ; a dummy value
prev_signal_color :=0x123456 ; a dummy value
order_stat := GetOrderStatus()
current_state := "OUT" ; "LONG", "SHORT", or "OUT"
if (order_stat = 2 || order_stat = 4) ; If SHORT order with limits is detected
{
current_state := "SHORT"
}
if (order_stat = 1 || order_stat = 3) ; If LONG order with limits is detected
{
current_state := "LONG"
}
loop
{
TimeDisplay.Value := current_state . ": " . FormatTime(, "HH:mm.ss")
if (debug_mode = 1)
{
msgbox "Watch/Trade loop"
}
if (debug_mode = 1)
{
msgbox current_state
}
if WinExist("Sponsored session") ; kill TeamViewer
{
WinClose("Sponsored session")
}
; sub_CloseTVSidePanel() ; close that pesky TeamViewer Bottom Side Panel
WinActivate("Main@thinkorswim")
Sleep(500)
MouseClick "left", return_x, return_y
; Watch for change of color in upper left box - yellow or cyan changes with each bar.
bar_color := PixelGetColor(bar_xpos, bar_ypos)
if (debug_mode = 1)
{
msgbox "bar_color: " bar_color " - prev_bar_color: " prev_bar_color
}
if (bar_color != prev_bar_color) ; change from bar to bar
{
BarChangeDisplay.Value := "BarChange: " . FormatTime(, "HH:mm.ss")
; Find signal indicator color
signal_color := PixelGetColor(signal_xpos, signal_ypos)
Sleep(500)
prev_bar_color := bar_color
if (debug_mode = 1)
{
msgbox "signal_color: " signal_color
msgbox "checking for green signal"
}
if (signal_color != prev_signal_color)
{
order_stat := GetOrderStatus()
current_state := "OUT" ; "LONG", "SHORT", or "OUT"
if (order_stat = 2 || order_stat = 4) ; If SHORT order with limits is detected
{
current_state := "SHORT"
}
if (order_stat = 1 || order_stat = 3) ; If LONG order with limits is detected
{
current_state := "LONG"
}
}
if (signal_color = green_signal && current_state != "LONG" && initial_restart != 1) ; the LONG signal is detected and wasn't recently limited out
{
SignalChangeDisplay.Value := "BUY LONG: " . FormatTime(, "HH:mm.ss")
sub_ProcessLong()
prev_signal_color := signal_color
current_state := "LONG"
; sub_Screenshot()
}
if (debug_mode = 1)
{
msgbox "not green signal / checking for red"
}
if (signal_color = red_signal && current_state != "SHORT" && initial_restart != 1) ; the SHORT signal is detected and wasn't recently limited out
{
SignalChangeDisplay.Value := "SELL SHORT: " . FormatTime(, "HH:mm.ss")
sub_ProcessShort()
prev_signal_color := signal_color
current_state := "SHORT"
; sub_Screenshot()
}
if (debug_mode = 1)
{
msgbox "not red signal"
}
if (signal_color = orange_signal && initial_restart != 1) ; the FLATTEN signal is detected
{
SignalChangeDisplay.Value := "FLATTEN: " . FormatTime(, "HH:mm.ss")
sub_FlattenOut()
prev_signal_color := signal_color
current_state := "OUT"
; sub_Screenshot()
}
if (initial_restart = 1)
{
initial_restart := 0
sleep(50)
}
}
Sleep(12000) ; wait 12 seconds til we do it all again
MouseClick "left", return_x + 8, return_y + 8
}
}
Catch
{
ErrorDisplay.Value := A_LastError . " at: " . FormatTime(, "HH:mm.ss")
}
}
sub_PauseTrade(*)
{
loop
{
Sleep(60000)
}
}
sub_CloseAT(*)
{
Excel := ComObjActive("Excel.Application")
loop Excel.workbooks.count
{
if fileexist(Excel.workbooks(a_index).fullname)
Excel.Workbooks(a_index).save
}
Excel.quit()
ExitApp
}
sub_ProcessLong(*)
{
if (debug_mode = 1)
{
msgbox "sub_ProcessLong"
}
ord_stat := GetOrderStatus()
if (ord_stat = 2 || ord_stat = 4) ; If short order with or without limits is detected
{
; Flatten
TestAndClick(flatten_ul_xpos, flatten_ul_ypos, flatten_dr_xpos, flatten_dr_ypos, flatten_inactive_color, flatten_active_color)
; Wait for the trade to go through
Sleep(15000)
; Update status of GUI
ExcelAppend(ws, "Flatten", "Complete")
}
WinActivate("Main@thinkorswim")
Sleep(2500)
ord_stat := GetOrderStatus()
Sleep(5000)
if (ord_stat = 0) ; If flatten button is inactive we should place LONG order
{
; Buy
TestAndClick(buymkt_ul_xpos, buymkt_ul_ypos, buymkt_dr_xpos, buymkt_dr_ypos, buymkt_inactive_color, buymkt_active_color)
Sleep(15000)
; Update status of GUI
ExcelAppend(ws, "Long", "Complete")
}
}
sub_ProcessShort(*)
{
if (debug_mode = 1)
{
msgbox "sub_ProcessShort"
}
ord_stat := GetOrderStatus()
if (ord_stat = 1 || ord_stat = 3) ; If long order with limits is detected
{
; Flatten
TestAndClick(flatten_ul_xpos, flatten_ul_ypos, flatten_dr_xpos, flatten_dr_ypos, flatten_inactive_color, flatten_active_color)
; Wait for the trade to go through
Sleep(15000)
; msgbox "Flattened"
; Update status of GUI
ExcelAppend(ws, "Flatten", "Complete")
}
WinActivate("Main@thinkorswim")
Sleep(2500)
ord_stat := GetOrderStatus()
Sleep(5000)
if (ord_stat = 0) ; If flatten button is inactive we should place SHORT order
{
; Sell
TestAndClick(sellmkt_ul_xpos, sellmkt_ul_ypos, sellmkt_dr_xpos, sellmkt_dr_ypos, sellmkt_inactive_color, sellmkt_active_color)
Sleep(15000)
; Update status of GUI
ExcelAppend(ws, "Short", "Complete")
}
}
sub_FlattenOut(*)
{
if (debug_mode = 1)
{
msgbox "sub_FlattenOut"
}
ord_stat := GetOrderStatus()
if (ord_stat = 1 || ord_stat = 2 || ord_stat = 3 || ord_stat = 4) ; If order with or without limits is detected
{
; Flatten
TestAndClick(flatten_ul_xpos, flatten_ul_ypos, flatten_dr_xpos, flatten_dr_ypos, flatten_inactive_color, flatten_active_color)
; Wait for the trade to go through
Sleep(15000)
; msgbox "Flattened"
; Update status of GUI
ExcelAppend(ws, "Flatten", "Complete")
}
WinActivate("Main@thinkorswim")
Sleep(2500)
ord_stat := GetOrderStatus()
Sleep(5000)
}
sub_Status(*)
{
ord_stat := GetOrderStatus()
ExcelAppend(ws, "Order Status", ord_stat)
if (ord_stat = 0)
{
msgbox "No orders"
}
else if (ord_stat = 1)
{
msgbox "Buy (unfilled) with sell limits"
}
else if (ord_stat = 2)
{
msgbox "Sell (unfilled) with buy limits"
}
else if (ord_stat = 3)
{
msgbox "Bought with sell limits"
}
else if (ord_stat = 4)
{
msgbox "Sold with buy limits"
}
}
; GetOrderStatus will open the order book and return one of 5 values
; 0 - is for no orders
; 1 - Orders detected - Buy (unfilled) with sell limits
; 2 - Orders detected - Sell (unfilled) with buylimits
; 3 - Orders detected - Bought with sell limits
; 4 - Orders detected - Sold with buy limits
GetOrderStatus(*)
{
WinActivate("Main@thinkorswim")
Sleep(500)
MouseClick "left", pos_button_x, pos_button_y ; open the position tab
Sleep(4000)
MouseMove return_x, return_y
top_order_sell := PixelSearch(&top_sell_x, &top_sell_y, order_topx, order_topy, order_botx, order_boty, sell_order_color)
top_order_buy := PixelSearch(&top_buy_x, &top_buy_y, order_topx, order_topy, order_botx, order_boty, buy_order_color)
bot_order_sell := PixelSearch(&bot_sell_x, &bot_sell_y, order_botx, order_boty, order_topx, order_topy, sell_order_color)
bot_order_buy := PixelSearch(&bot_buy_x, &bot_buy_y, order_botx, order_boty, order_topx, order_topy, buy_order_color)
; msgbox PixelGetColor(order_topx, 932) ", Top"
; msgbox PixelGetColor(order_topx, 960) ", Bottom"
; msgbox top_sell_y " -sell- " bot_sell_y
; msgbox top_buy_y " -buy- " bot_buy_y
if (top_order_sell AND top_order_buy)
{
if (top_sell_y > top_buy_y)
{
order_status := 1
}
else if (top_sell_y < top_buy_y)
{
order_status := 2
}
}
else if (top_order_sell = 0 AND top_order_buy = 0)
{
order_status := 0
short_bubble := PixelSearch(&Pos_LimitX, &Pos_LimitY, position_banner_top_x, position_banner_top_y, position_banner_bot_x, position_banner_bot_y, position_banner_color_red)
long_bubble := PixelSearch(&Pos_LimitX, &Pos_LimitY, position_banner_top_x, position_banner_top_y, position_banner_bot_x, position_banner_bot_y, position_banner_color_green)
if (short_bubble)
{
order_status := 4
}
if (long_bubble)
{
order_status := 3
}
}
else
{
if (top_order_sell) ;
{
order_status := 3
}
else if (top_order_buy) ;
{
order_status := 4
}
}
return order_status
}
TestAndClick(upper_left_x, upper_left_y, lower_right_x, lower_right_y, inactive_color, active_color)
{
; msgbox PixelGetColor(upper_left_x, upper_left_y) ", " upper_left_x ", " upper_left_y
inactive_res := PixelSearch(&inactive_x, &inactive_y, upper_left_x, upper_left_y, lower_right_x, lower_right_y, inactive_color)
Sleep(500)
; msgbox inactive_res
if (inactive_res = 1)
{
MouseMove inactive_x, inactive_y
Sleep(500)
highlight_color := PixelGetColor(inactive_x, inactive_y)
; msgbox highlight_color
if (active_color = highlight_color)
{
MouseClick "left", inactive_x, inactive_y
; msgbox "clicked"
}
}
else
{
MouseMove return_x, return_y
Sleep(500)
fnd_color := PixelGetColor(upper_left_x, upper_left_y)
; msgbox fnd_color
}
MouseMove return_x, return_y
}
; This function checks if a specific Excel file is open.
; It returns the Workbook object if found, otherwise returns false.
IsExcelFileOpen(filePath)
{
try {
; Attempt to connect to a running instance of Excel
excelApp := ComObjActive("Excel.Application")
; Extract the file name from the full path
fileName := SubStr(filePath, InStr(filePath, "\", , -1) + 1)
; Loop through all open workbooks
for workbook in excelApp.Workbooks {
if (workbook.Name == fileName) {
return workbook ; Return the Workbook object
}
}
} catch {
; Excel is not running, so the file cannot be open
return false
}
return false
}
ExcelAppend(excelWorksheet, dataType, dataText)
{
now_date := FormatTime(A_Now, "yyyy-MM-dd")
now_time := FormatTime(A_Now, "HH:mm:ss")
lastRow := excelWorksheet.Cells(excelWorksheet.Rows.Count, 1).End(-4162).Row
newRow := lastRow + 1
excelWorksheet.Cells(newRow, 1).Value := now_date
excelWorksheet.Cells(newRow, 2).Value := now_time
excelWorksheet.Cells(newRow, 3).Value := dataType
excelWorksheet.Cells(newRow, 4).Value := dataText
wb.Save()
}
sub_Screenshot(*)
{
WinActivate("Main@thinkorswim")
currentDateTime := FormatTime(A_Now, "yyyy-MM-dd_HHmmss")
exePath := '"' . A_ProgramFiles . '\IrfanView\i_view64.exe"'
destPath := '"' . Destination . '\capture_$U(' . currentDateTime . ').jpg"'
cmd := exePath . ' /capture=3 /crop=(0,0,2000,1400) /resize=(200,140) /jpgq=60 /convert=' . destPath
; msgbox cmd
RunWait(cmd)
}
; Close Teamviewer side panel
sub_CloseTVSidePanel(*)
{
tv_color := PixelGetColor(teamviewer_color_xpos, teamviewer_color_ypos)
if (tv_color = teamviewer_color)
{
MouseClick "left", teamviewer_panel_xpos, teamviewer_panel_ypos
}
}