Add ib config section support

brokers_config
Tyler Goodlet 2021-03-19 13:23:33 -04:00
parent 96b0102f04
commit a10d20c041
3 changed files with 82 additions and 6 deletions

View File

@ -0,0 +1,25 @@
[questrade]
refresh_token = ""
access_token = ""
api_server = "https://api06.iq.questrade.com/"
expires_in = 1800
token_type = "Bearer"
expires_at = 1616095326.355846
[kraken]
key_descr = "api_0"
public_key = ""
private_key = ""
[ib.api]
ipaddr = "127.0.0.1"
[ib.accounts]
margin = ""
registered = ""
paper = ""
[ib.api.ports]
gw = 4002
tws = 7497
order = [ "gw", "tws",]

View File

@ -40,6 +40,16 @@ def _override_config_dir(
def get_broker_conf_path(): def get_broker_conf_path():
"""Return the default config path normally under
``~/.config/piker`` on linux.
Contains files such as:
- brokers.toml
- watchlists.toml
- signals.toml
- strats.toml
"""
return os.path.join(_config_dir, _file_name) return os.path.join(_config_dir, _file_name)

View File

@ -31,6 +31,7 @@ from pprint import pformat
import inspect import inspect
import itertools import itertools
import logging import logging
from random import randint
import time import time
import trio import trio
@ -49,6 +50,7 @@ from ib_insync.client import Client as ib_Client
from fuzzywuzzy import process as fuzzy from fuzzywuzzy import process as fuzzy
import numpy as np import numpy as np
from . import config
from ..log import get_logger, get_console_log from ..log import get_logger, get_console_log
from .._daemon import maybe_spawn_brokerd from .._daemon import maybe_spawn_brokerd
from ..data._source import from_df from ..data._source import from_df
@ -310,7 +312,8 @@ class Client:
unique_sym = f'{con.symbol}.{con.primaryExchange}' unique_sym = f'{con.symbol}.{con.primaryExchange}'
as_dict = asdict(d) as_dict = asdict(d)
# nested dataclass we probably don't need and that won't IPC serialize # nested dataclass we probably don't need and that
# won't IPC serialize
as_dict.pop('secIdList') as_dict.pop('secIdList')
details[unique_sym] = as_dict details[unique_sym] = as_dict
@ -637,11 +640,29 @@ class Client:
# default config ports # default config ports
_tws_port: int = 7497 _tws_port: int = 7497
_gw_port: int = 4002 _gw_port: int = 4002
_try_ports = [_gw_port, _tws_port] _try_ports = [
_client_ids = itertools.count() _gw_port,
_tws_port
]
# TODO: remove the randint stuff and use proper error checking in client
# factor below..
_client_ids = itertools.count(randint(1, 100))
_client_cache = {} _client_cache = {}
def get_config() -> dict[str, Any]:
conf, path = config.load()
section = conf.get('ib')
if not section:
log.warning(f'No config section found for ib in {path}')
return
return section
@asynccontextmanager @asynccontextmanager
async def _aio_get_client( async def _aio_get_client(
host: str = '127.0.0.1', host: str = '127.0.0.1',
@ -654,8 +675,9 @@ async def _aio_get_client(
TODO: consider doing this with a ctx mngr eventually? TODO: consider doing this with a ctx mngr eventually?
""" """
# first check cache for existing client conf = get_config()
# first check cache for existing client
try: try:
if port: if port:
client = _client_cache[(host, port)] client = _client_cache[(host, port)]
@ -666,6 +688,7 @@ async def _aio_get_client(
yield client yield client
except (KeyError, IndexError): except (KeyError, IndexError):
# TODO: in case the arbiter has no record # TODO: in case the arbiter has no record
# of existing brokerd we need to broadcast for one. # of existing brokerd we need to broadcast for one.
@ -675,9 +698,27 @@ async def _aio_get_client(
client_id = next(_client_ids) client_id = next(_client_ids)
ib = NonShittyIB() ib = NonShittyIB()
ports = _try_ports if port is None else [port]
# attempt to get connection info from config
ports = conf['api'].get(
'ports',
{
# default order is to check for gw first
'gw': 4002,
'tws': 7497,
'order': ['gw', 'tws']
}
)
order = ports['order']
try_ports = [ports[key] for key in order]
ports = try_ports if port is None else [port]
# TODO: support multiple clients allowing for execution on
# multiple accounts (including a paper instance running on the
# same machine) and switching between accounts in the EMs
_err = None _err = None
for port in ports: for port in ports:
try: try:
log.info(f"Connecting to the EYEBEE on port {port}!") log.info(f"Connecting to the EYEBEE on port {port}!")
@ -1360,7 +1401,7 @@ async def trades_dialogue(
'contract': asdict(fill.contract), 'contract': asdict(fill.contract),
'execution': asdict(fill.execution), 'execution': asdict(fill.execution),
'commissions': asdict(fill.commissionReport), 'commissions': asdict(fill.commissionReport),
'broker_time': execu.time, # supposedly IB server fill time 'broker_time': execu.time, # supposedly server fill time
'name': 'ib', 'name': 'ib',
} }