Skip to content

Commit 414fc06

Browse files
committed
more fixes; threads ib_account through the system to deal with multiple accounts
1 parent d254280 commit 414fc06

File tree

11 files changed

+116
-103
lines changed

11 files changed

+116
-103
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ It relies on:
3333
- [ ] Backtesting
3434
- [ ] Algorithmic Strategy API and extensibility hooks (started)
3535
- [ ] Strategy and portfolio risk analysis (started)
36-
- [ ] Add/remove strategies
36+
- [x] Add/remove strategies
3737
- [ ] Hyperparameter search on strategies
3838

3939
There is still about 2-3 months of work left before MMR is 'shippable'. If you want to help speed that up, send me a message.
@@ -92,11 +92,11 @@ $ ssh trader@localhost -p 2222
9292

9393
Enter your Trader Workstation username and password. The script will proceed to automatically install the latest Trader Workstation version.
9494

95-
After this has completed, it will call a script `start_trader.sh` in the MMR root directory, which starts a [tmux](https://github.com/tmux/tmux/wiki) session with five commands:
95+
After this has completed, it will call a script `start_mmr.sh` in the MMR root directory, which starts a [tmux](https://github.com/tmux/tmux/wiki) session with five commands:
9696

9797
* `pycron` (MMR's process spawner and scheduler) which handles the process scheduling, maintenance and uptime of the MMR trading runtime, ArcticDB, Redis, X Windows, and Trader Workstation, ready for automatic trading. You can manually call this by: ```python3 pycron/pycron.py --config ./configs/pycron.yaml```
9898
* `cli` which is command line interface to interact with the trading system (check portfolio, check systems, manually create orders etc). You can manually call this using ```python3 cli.py```.
99-
* `async_cli` TUI (terminal UI) based way to interact with the trading system. (this will replace the `cli` module in the future).
99+
* `tui_cli` TUI (terminal UI) based way to interact with the trading system. (this will replace the `cli` module in the future).
100100
* `trader_service_log` displays the trader service log real time (see below for information on this service).
101101
* `strategy_service_log` displays the trader service log real time.
102102

@@ -156,7 +156,7 @@ Trading from your command line interface of choice is also supported:
156156

157157
There's also a terminal based TUI to quickly inspect the runtime, orders, algos and prices [TODO: this is under significant active development].
158158

159-
* ```python3 async_cli.py```
159+
* ```python3 tui_cli.py```
160160

161161
![](docs/2023-03-03-09-50-58.png)
162162

configs/pycron.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
restart_if_finished: True
7070
depends_on:
7171
- tws
72-
delay: 8
72+
delay: 15
7373
- name: strategy_service
7474
description: service that loads, hosts and executes trading strategies and algorithms
7575
command: python3
@@ -81,4 +81,4 @@ jobs:
8181
depends_on:
8282
- tws
8383
- trader_service
84-
delay: 12
84+
delay: 20

configs/trader.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ strategy_config_file: /home/trader/mmr/configs/strategy_runtime.yaml
55
logfile: logs/trader.log
66
strategies_directory: /home/trader/mmr/strategies
77
trading_mode: live
8+
# your IB account can be set here, or via environment variable "IB_ACCOUNT"
9+
# if you're trading your paper account, you'll need to switch trading_mode: paper,
10+
# and set your paper account number here
11+
ib_account:
812
ib_server_address: 127.0.0.1
913
ib_server_port: 7496
1014
trading_runtime_ib_client_id: 5

scripts/installation/.bash_profile

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ eval "$(pyenv init -)"
66

77
export TRADER_CONFIG="/home/trader/mmr/configs/trader.yaml"
88
cd /home/trader/mmr
9-
/home/trader/mmr/start_trader.sh
9+
/home/trader/mmr/start_mmr.sh

start_trader.sh renamed to start_mmr.sh

+66-81
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,70 @@ function parse_yaml {
6565
}'
6666
}
6767

68+
function rebuild_configuration () {
69+
# deal with trading mode option
70+
eval $(parse_yaml $TRADER_CONFIG "CONF_")
71+
72+
echo ""
73+
echo ""
74+
echo "TWS trading mode configured in $TRADER_CONFIG is: $CONF_trading_mode"
75+
echo "Hit enter to keep, or type 'paper' or 'live' to change: "
76+
read TRADING_MODE;
77+
78+
if [ -z "$TRADING_MODE" ]
79+
then
80+
echo "Using trading mode from $TRADER_CONFIG"
81+
TRADING_MODE=$CONF_trading_mode
82+
else
83+
echo "Using trading mode from user input: $TRADING_MODE"
84+
CONF_trading_mode=$TRADING_MODE
85+
fi
86+
87+
echo ""
88+
echo -n "Please enter Interactive Brokers username for trading mode $TRADING_MODE: "
89+
read USERNAME;
90+
91+
echo ""
92+
echo -n "Please enter Interactive Brokers password for trading mode $TRADING_MODE: "
93+
read -s PASSWORD;
94+
95+
echo ""
96+
echo "Please enter Interactive Brokers 'account number' for trading mode $TRADING_MODE "
97+
echo -n "(starts with either U or DU, and may also be set as an environment variable IB_ACCOUNT): "
98+
read -s IB_ACCOUNT;
99+
100+
sed -i "s/^TWSUSERID=.*/TWSUSERID=$USERNAME/" $IBC_DIR/twsstart.sh
101+
sed -i "s/^TWSPASSWORD=.*/TWSPASSWORD=$PASSWORD/" $IBC_DIR/twsstart.sh
102+
103+
sed -i "s/^IbLoginId=.*/IbLoginId=$USERNAME/" $IBC_DIR/config.ini
104+
sed -i "s/^IbPassword=.*/IbPassword=$PASSWORD/" $IBC_DIR/config.ini
105+
106+
CONF_trading_mode="$TRADING_MODE"
107+
sed -i "s/^TradingMode=.*/TradingMode=$CONF_trading_mode/" $IBC_DIR/config.ini
108+
sed -i "s/^trading_mode:.*/trading_mode: $CONF_trading_mode/" $TRADER_CONFIG
109+
110+
sed -i "s/^ib_account:.*/ib_account: $IB_ACCOUNT/" $TRADER_CONFIG
111+
112+
# deal with paper/live ports
113+
if [ "$TRADING_MODE" = "paper" ]; then
114+
sed -i "s/^ib_server_port:.*/ib_server_port: 7497/" $TRADER_CONFIG
115+
else
116+
sed -i "s/^ib_server_port:.*/ib_server_port: 7496/" $TRADER_CONFIG
117+
fi
118+
}
119+
120+
121+
echo ""
122+
echo " $(tput setab 2)${BLACK}--------------------------------------------${RESET}"
123+
echo " $(tput setab 2)${BLACK}|${RESET} start_mmr.sh started $(tput setab 2)${BLACK}|${RESET}"
124+
echo " $(tput setab 2)${BLACK}--------------------------------------------${RESET}"
125+
echo ""
126+
echo " $(tput setab 2)${BLACK}|${RESET} This script configures and runs MMR. There are three configuration files that it may read or modify: "
127+
echo " $(tput setab 2)${BLACK}|${RESET} * $TRADER_CONFIG "
128+
echo " $(tput setab 2)${BLACK}|${RESET} * $IBC_DIR/config.ini "
129+
echo " $(tput setab 2)${BLACK}|${RESET} * $JTS_DIR/jts.ini "
68130
echo ""
69-
echo " $(tput setab 2)${BLACK}----------------------------------------${RESET}"
70-
echo " $(tput setab 2)${BLACK}|${RESET} start_trader.sh started $(tput setab 2)${BLACK}|${RESET}"
71-
echo " $(tput setab 2)${BLACK}----------------------------------------${RESET}"
72131
echo ""
73-
74132

75133
set -e
76134

@@ -82,8 +140,6 @@ else
82140
fi
83141

84142
# check for --live and --paper arguments, and update config files if everything is already installed
85-
# todo: refactor this all out into a singular function to deal with username/passwords and configuration
86-
87143
if [ ! "$(grep -Fx TWSUSERID= $IBC_DIR/twsstart.sh)" ] && [ -f "$IBC_DIR/config.ini" ] && [ -f "$TRADER_CONFIG" ]; then
88144
eval $(parse_yaml $TRADER_CONFIG "CONF_")
89145
IBC_TRADING_MODE="$(grep -oP '(?<=TradingMode=).*$' $IBC_DIR/config.ini)"
@@ -98,29 +154,7 @@ if [ ! "$(grep -Fx TWSUSERID= $IBC_DIR/twsstart.sh)" ] && [ -f "$IBC_DIR/config.
98154
echo "resetting account configuration"
99155
echo ""
100156

101-
echo -n "Please enter Interactive Brokers username for trading account $TRADING_MODE: "
102-
read USERNAME;
103-
104-
echo ""
105-
echo -n "Please enter Interactive Brokers password for trading account $TRADING_MODE: "
106-
read -s PASSWORD;
107-
108-
sed -i "s/^TWSUSERID=.*/TWSUSERID=$USERNAME/" $IBC_DIR/twsstart.sh
109-
sed -i "s/^TWSPASSWORD=.*/TWSPASSWORD=$PASSWORD/" $IBC_DIR/twsstart.sh
110-
111-
sed -i "s/^IbLoginId=.*/IbLoginId=$USERNAME/" $IBC_DIR/config.ini
112-
sed -i "s/^IbPassword=.*/IbPassword=$PASSWORD/" $IBC_DIR/config.ini
113-
114-
CONF_trading_mode="$TRADING_MODE"
115-
sed -i "s/^TradingMode=.*/TradingMode=$CONF_trading_mode/" $IBC_DIR/config.ini
116-
sed -i "s/^trading_mode:.*/trading_mode: $CONF_trading_mode/" $TRADER_CONFIG
117-
fi
118-
119-
# deal with paper/live ports
120-
if [ "$TRADING_MODE" = "paper" ]; then
121-
sed -i "s/^ib_server_port:.*/ib_server_port: 7497/" $TRADER_CONFIG
122-
else
123-
sed -i "s/^ib_server_port:.*/ib_server_port: 7496/" $TRADER_CONFIG
157+
rebuild_configuration
124158
fi
125159
fi
126160

@@ -155,16 +189,15 @@ fi
155189
if [ ! -d $JTS_DIR ] || [ ! "$(ls -I *.ini $JTS_DIR)" ]; then
156190
# likely first time start
157191
echo "Can't find TWS, first time running? Let's download, install and configure Interactive Brokers!"
192+
echo ""
158193

159194
if [ ! -f $MMR_DIR/tws-latest-standalone-linux-x64.sh ]; then
160-
echo "latest TWS linux installer not found, downloading"
161195
rm -f $MMR_DIR/tws-latest-standalone-linux-x64.sh
162196
wget https://download2.interactivebrokers.com/installers/tws/latest-standalone/tws-latest-standalone-linux-x64.sh -P $MMR_DIR/third_party
163197
chmod +x $MMR_DIR/third_party/tws-latest-standalone-linux-x64.sh
164198
chmod +x $MMR_DIR/scripts/installation/install_tws.sh
165199
fi
166200

167-
echo ""
168201
echo ""
169202
echo "Automating the installation of Trader Workstation to $JTS_DIR..."
170203
echo ""
@@ -184,57 +217,9 @@ if [ "$(grep -Fx TWSUSERID= $IBC_DIR/twsstart.sh)" ]; then
184217
echo ""
185218
echo ""
186219
echo "[Can't find a username set in $IBC_DIR/twsstart.sh, prompting for credentials]:"
187-
188-
# deal with trading mode option
189-
eval $(parse_yaml $TRADER_CONFIG "CONF_")
190-
191220
echo ""
192-
echo "TWS trading mode configured in $TRADER_CONFIG is: $CONF_trading_mode"
193-
echo "Hit enter to keep, or type 'paper' or 'live' to change: "
194-
read TRADING_MODE;
195-
196-
if [ -z "$TRADING_MODE" ]
197-
then
198-
echo "Using trading mode from $TRADER_CONFIG"
199-
else
200-
echo "Using trading mode from user input: $TRADING_MODE"
201-
CONF_trading_mode=$TRADING_MODE
202-
fi
203221

204-
echo -n "Please enter Interactive Brokers username for trading account '$CONF_trading_mode': "
205-
read USERNAME;
206-
207-
echo ""
208-
echo -n "Please enter Interactive Brokers password for trading account '$CONF_trading_mode': "
209-
read -s PASSWORD;
210-
211-
if [ -z "$USERNAME" ]
212-
then
213-
echo "Username is empty, script will fail, exiting"
214-
exit 1
215-
fi
216-
217-
if [ -z "$PASSWORD" ]
218-
then
219-
echo "Password is empty, script will fail, exiting"
220-
exit 1
221-
fi
222-
223-
sed -i "s/^TWSUSERID=.*/TWSUSERID=$USERNAME/" $IBC_DIR/twsstart.sh
224-
sed -i "s/^TWSPASSWORD=.*/TWSPASSWORD=$PASSWORD/" $IBC_DIR/twsstart.sh
225-
226-
sed -i "s/^IbLoginId=.*/IbLoginId=$USERNAME/" $IBC_DIR/config.ini
227-
sed -i "s/^IbPassword=.*/IbPassword=$PASSWORD/" $IBC_DIR/config.ini
228-
229-
sed -i "s/^TradingMode=.*/TradingMode=$CONF_trading_mode/" $IBC_DIR/config.ini
230-
sed -i "s/^trading_mode:.*/trading_mode: $CONF_trading_mode/" $TRADER_CONFIG
231-
232-
# deal with paper/live ports
233-
if [ "$CONF_trading_mode" = "paper" ]; then
234-
sed -i "s/^ib_server_port:.*/ib_server_port: 7497/" $TRADER_CONFIG
235-
else
236-
sed -i "s/^ib_server_port:.*/ib_server_port: 7496/" $TRADER_CONFIG
237-
fi
222+
rebuild_configuration
238223

239224
# defaults for IBC config.ini, feel free to change these
240225
sed -i "s/^ExistingSessionDetectedAction=.*/ExistingSessionDetectedAction=primaryoverride/" $IBC_DIR/config.ini
@@ -260,7 +245,7 @@ if [ "$(grep -Fx TWSUSERID= $IBC_DIR/twsstart.sh)" ]; then
260245
echo ""
261246
echo ""
262247
chmod +x $IBC_DIR/scripts/displaybannerandlaunch.sh
263-
chmod +x $IBC_DIR/scripts/ibcstart.sh\
248+
chmod +x $IBC_DIR/scripts/ibcstart.sh
264249

265250
read NULL;
266251
fi

trader/container.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def resolve(self, t: Type, **extra_args):
2727
continue
2828
if extra_args and param.name in extra_args.keys():
2929
args[param.name] = extra_args[param.name]
30-
elif param.name in self.configuration:
30+
elif os.getenv(param.name.upper()):
31+
args[param.name] = os.getenv(param.name.upper())
32+
elif param.name in self.configuration and self.configuration[param.name]:
3133
args[param.name] = self.configuration[param.name]
3234
return t(**args)
3335

trader/listeners/ibreactive.py

+13-9
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ def __init__(
5656
ib_server_address: str,
5757
ib_server_port: int,
5858
ib_client_id: int,
59+
ib_account: str = '',
5960
read_only: bool = False
6061
):
6162
self.ib_server_address = ib_server_address
6263
self.ib_server_port = ib_server_port
6364
self.ib_client_id = ib_client_id
65+
self.ib_account = ib_account
6466
self.read_only = read_only
6567

6668
self.ib = IB()
@@ -144,20 +146,22 @@ def __handle_client_id_error(msg):
144146
net_client = cast(Client, self.ib.client)
145147
net_client.conn.disconnected += __handle_client_id_error
146148

147-
logging.debug('ibreactive.connect ib_server_address: {}, ib_server_port: {}, ib_client_id: {}'.format(
148-
self.ib_server_address, self.ib_server_port, self.ib_client_id
149+
logging.debug('ibreactive.connect ib_server_address: {}, ib_server_port: {}, ib_client_id: {} ib_account: {}'.format(
150+
self.ib_server_address, self.ib_server_port, self.ib_client_id, self.ib_account
149151
))
150152

151153
self.ib.connect(
152154
host=self.ib_server_address,
153155
port=self.ib_server_port,
154156
clientId=self.ib_client_id,
155157
timeout=10,
156-
readonly=self.read_only
158+
readonly=self.read_only,
159+
account=self.ib_account,
157160
)
158161

159162
net_client.conn.disconnected -= __handle_client_id_error
160163

164+
# todo: check to see if we need to pass through 'ib_account' here.
161165
self.history_worker = IBHistoryWorker(
162166
self.ib_server_address,
163167
self.ib_server_port,
@@ -351,7 +355,7 @@ async def subscribe_positions(self) -> Observable[List[Position]]:
351355
await self.positions_subject.call_event_subscriber(self.ib.reqPositionsAsync())
352356

353357
def get_positions() -> Iterator[List[Position]]:
354-
yield self.ib.positions()
358+
yield self.ib.positions(account=self.ib_account)
355359

356360
# deferred observable, primed with the latest portfolio items
357361
deferred = rx.defer(lambda _: rx.from_iterable(get_positions()))
@@ -363,7 +367,7 @@ async def subscribe_portfolio(self) -> Observable[PortfolioItem]:
363367
# def reqAccountUpdates(self, account: str = '') is called at startup
364368
# so we don't need to call any particular self.ib.req* method
365369
def get_portfolio_items() -> Iterator[PortfolioItem]:
366-
portfolio_items = self.ib.portfolio()
370+
portfolio_items = self.ib.portfolio(account=self.ib_account)
367371
for item in portfolio_items:
368372
yield item
369373

@@ -373,14 +377,14 @@ def get_portfolio_items() -> Iterator[PortfolioItem]:
373377
ops.concat(self.portfolio_subject)
374378
)
375379

376-
async def subscribe_single_pnl(self, account: str, contract: Contract) -> Observable[PnLSingle]:
380+
async def subscribe_single_pnl(self, contract: Contract) -> Observable[PnLSingle]:
377381
logging.debug('subscribe_single_pnl({})'.format(contract))
378382

379383
# if not already subscribed
380384
if contract.conId not in self.pnl_cache:
381385
self.pnl_subject.call_event_subscriber_sync(
382386
lambda: self.ib.reqPnLSingle(
383-
account=account,
387+
account=self.ib_account,
384388
modelCode='',
385389
conId=contract.conId
386390
),
@@ -434,10 +438,10 @@ async def __update(
434438
loop.call_later(1, asyncio.create_task, __update(subject, contract, start_date, end_date))
435439
return subject
436440

437-
async def cancel_single_pnl(self, account: str, contract: Contract):
441+
async def cancel_single_pnl(self, contract: Contract):
438442
logging.debug('cancel_single_pnl({})'.format(contract))
439443
if contract.conId in self.pnl_cache:
440-
self.ib.cancelPnLSingle(account=account, modelCode='', conId=contract.conId)
444+
self.ib.cancelPnLSingle(account=self.ib_account, modelCode='', conId=contract.conId)
441445
del self.pnl_cache[contract.conId]
442446

443447
async def get_open_orders(self) -> List[Order]:

trader/messaging/trader_service_api.py

+4
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,7 @@ async def enable_strategy(self, name: str, paper: bool) -> SuccessFail[StrategyS
171171
@RPCHandler.rpcmethod
172172
async def disable_strategy(self, name: str) -> SuccessFail[StrategyState]:
173173
return await self.trader.disable_strategy(name)
174+
175+
@RPCHandler.rpcmethod
176+
async def get_ib_account(self) -> str:
177+
return self.trader.ib_account

0 commit comments

Comments
 (0)