IbcAlpha / IBC

Automation of Interactive Brokers TWS. You can download the latest release here: https://github.com/ibcalpha/ibc/releases/latest
GNU General Public License v3.0
1.04k stars 181 forks source link

How to automate closing java.exe for DDE Socket Bridge after a trading session while keeping open java.exe for TWS? #197

Closed fettekatz closed 1 year ago

fettekatz commented 1 year ago

So now we have to keep TWS on all week, in addition to starting DDE Socket Bridge every day if we're running an API. Fair enough. But if you keep DDE open until the next day, it no longer works, so you must start a new instance each day. I have opening DDE automated, but is there a way to automate closing out DDE after a trading session? If I run taskkill on java.exe, it kills both TWS and DDE, as they both use java.exe but from two directories:

TWS: C:\Users\user\AppData\Local\Programs\Common\i4j_jres\Oda-jK0QgTEmVssfllLP\1.8.0_202_64\bin\java.exe

DDE: C:\Program Files (x86)\Common Files\Oracle\Java\javapath_target_153253484\java.exe

Is there a way to kill all processes within that second root folder? That should do the trick.

rlktradewright commented 1 year ago

I think the way to do this is via the command line used to start the process. If we could add something unique to the java command that runs the DDE Socket Bridge, then we could access the command line of running java processes and identify which one contains that uniqueness.

The easiest way I know of to 'uniquefy' the java command is to add a -D argument: these take the form

-D<name>=<value>

and are passed transparently through to the Java program to use in any way it needs (or just ignore it).

So we can modify the java command in RunDdeSocketBridge.bat like this:

java -DddeSocketBridge=42 -Djava.library.path=.\src\main\resources -jar DdeSocketBridge.jar

and that additional parameter will then be visible in anything that shows the command line (and the socket bridge program will just ignore it). The command line for all running Java processes can be obtained using this command:

wmic PROCESS where "name like 'java.exe'" get Processid, CommandLine

On my computer at the moment I have two DdeSocketBridge processes running and no other Java programs, and I get this output:

CommandLine                                                                                    ProcessId
java  -DddeSocketBridge=42 -Djava.library.path=.\src\main\resources -jar DdeSocketBridge.jar   20448
java  -DddeSocketBridge=101 -Djava.library.path=.\src\main\resources -jar DdeSocketBridge.jar  17268

Now with a bit of clever use of FOR statements (and probably a large jar of pixie dust to get the damn thing working) it should be possible to identify which command line contains the correct -DddeSocketBridge argument, and then parse out the process id so you can kill the process. Also wmic has other output formats that might make it easier to deal with.

Are you a batch file wizard and able to do this parsing? If not, I can have a go, but past experience shows that dealing with FOR statements is a unique form of torture...

fettekatz commented 1 year ago

I am absolutely not a batch file wizard. Googling "how to kill processes in a directory" gives some vague results, but I was wondering if there was something concrete, and simple haha. It's not a huge issue I guess. Having an older DdeSocketBridge process running doesn't seem to cause any issues.

rlktradewright commented 1 year ago

Ok, I'll have a go at it, but don't hold your breath!

Or I might try it in Powershell which is much more like a real programming platform than batch files.

fettekatz commented 1 year ago

Just kidding, multiple instances of DDE does present an issue, or at least it has been for me starting this last week. Keeping open the DDE instance from the day before causes this prompt to open in Excel when data from IB is requested, which must be closed manually, screwing up any automated macros.

DDE Prompt

Running TWS 10.20, TWS API 10.20, IBC 3.16, Win 11 64-bit, latest version Java 8.0.361

I'll be posting this question of how to kill .exe's in a specific directory on tech support forums as well, since it's actually a minor issue for me now.

rlktradewright commented 1 year ago

I have actually produced a script to do this shutdown, but I've forgotten about it over the past few days.

I'll try and make it available tomorrow - too late at night now!

rlktradewright commented 1 year ago

Ok, let's start with the simple stuff.

If you're only going to run one DdeSocketBridge at a time, then here's a command that will shut it down:

for /F "usebackq skip=1" %%i in (
    `wmic PROCESS where^
                "commandline like '%%-jar DdeSocketBridge.jar%%' and name like '%%java%%'"^
        get ProcessId ^
        ^| findstr /r /v "^$"`
) do taskkill /PID %%i

So that's nice and easy. You can put this command in a StopDdeSocketBridge.bat file. Needless to say, this needs to be run on the computer that is running the DdeSocketBridge, which can be different from the computer running TWS if required.

Warning: as you might have noticed, the DdeSocketBridge is not terribly well engineered. It won't run properly if TWS/Gateway isn't already running before you start it. And if TWS/Gateway stops for any reason (in particular if they auto-restart), then the DdeSocketBridge just keeps running but is no longer connected to the API, so nothing works.

I have half a mind to fix these deficiencies myself and submit the updates to IB for inclusion in the product. What I have in mind is that if TWS/Gateway isn't running when you start DdeSocketBridge, it will periodically retry connecting until it succeeds. And if it loses the connection it will start trying to reconnect again, repeating until it succeeds.

Trouble is I have better things to do than write IB's code for them. I might try submitting an issue about this to the API repository suggesting they make this enhancement, but it's quite likely they won't bother.

Running more than one DdeSocketBridge

If we want to run more than one DdeSocketBridge, for example for live and paper accounts, then we have to add something unique to the java command line when running RunDdeSocketBridge.bat. If we don't do this, the above command will shut down all currently running DdeSocketBridge instances. We may also need to set the client id, port, host and dde user name (see DDE Socket Bridge API for details of how to do this).

For example, edit RunDdeSocketBridge.bat so that it looks like this:

echo off
if not exist "DdeSocketBridge.jar" goto :error
java -DsocketBridgeId=1 -Djava.library.path=.\src\main\resources -jar DdeSocketBridge.jar -p7496 -c665544 -sLiveUser
goto :end
:error
echo DdeSocketBridge.jar is not found
:end

What I've done here is add the extra parameter -DsocketBridgeId=1 to the Java command line (along with the port, clientid and dde username). As I explained in my earlier reply, this extra parameter is visible to anything which can get the command line for the running process, but has no effect at all on the actual process. Now we can modify the command to only close the DdeSocketBridge instance that has this parameter, like so:

for /F "usebackq skip=1" %%i in (
    `wmic PROCESS where^
                "commandline like '%%-DsocketBridgeId=1%%' and name like '%%java%%'"^
        get ProcessId ^
        ^| findstr /r /v "^$"`
) do taskkill /PID %%i

So now create another start script, say RunDdeSocketBridgePaper.bat, identical to the first one except that its java command looks like this:

java -DsocketBridgeId=2 -Djava.library.path=.\src\main\resources -jar DdeSocketBridge.jar -p7497 -c445566 -sPaperUser

The command to stop this second instance is as above, but with:

                "commandline like '%%-DsocketBridgeId=2%%' and name like '%%java%%'"^

I hope this all makes some kind of sense!

fettekatz commented 1 year ago

Yup, that works! Thanks a lot for all your work on this Richard.