Compare commits
	
		
			13 Commits 
		
	
	
		
			d767be9bd9
			...
			44c339779f
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						44c339779f | |
| 
							
							
								 | 
						614f1c00c5 | |
| 
							
							
								 | 
						8339b39d9e | |
| 
							
							
								 | 
						a07e0a11d3 | |
| 
							
							
								 | 
						d89d2a0c04 | |
| 
							
							
								 | 
						cca2b14548 | |
| 
							
							
								 | 
						4d50bebf11 | |
| 
							
							
								 | 
						d6f3f47df3 | |
| 
							
							
								 | 
						0c7edb3223 | |
| 
							
							
								 | 
						3751140fca | |
| 
							
							
								 | 
						588569edb3 | |
| 
							
							
								 | 
						8a5bb688af | |
| 
							
							
								 | 
						513ced6a70 | 
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
################
 | 
			
		||||
# ---- CEXY ----
 | 
			
		||||
################
 | 
			
		||||
 | 
			
		||||
[binance]
 | 
			
		||||
accounts.paper = 'paper'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,28 +12,41 @@ accounts.spot = 'spot'
 | 
			
		|||
spot.use_testnet = false
 | 
			
		||||
spot.api_key = ''
 | 
			
		||||
spot.api_secret = ''
 | 
			
		||||
# ------ binance ------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[deribit]
 | 
			
		||||
# std assets
 | 
			
		||||
key_id = ''
 | 
			
		||||
key_secret = ''
 | 
			
		||||
# options
 | 
			
		||||
accounts.option = 'option'
 | 
			
		||||
option.use_testnet = false
 | 
			
		||||
option.key_id = ''
 | 
			
		||||
option.key_secret = ''
 | 
			
		||||
# aux logging from `cryptofeed`
 | 
			
		||||
option.log.filename = 'cryptofeed.log'
 | 
			
		||||
option.log.level = 'DEBUG'
 | 
			
		||||
option.log.disabled = true
 | 
			
		||||
# ------ deribit ------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[kraken]
 | 
			
		||||
key_descr = ''
 | 
			
		||||
api_key = ''
 | 
			
		||||
secret = ''
 | 
			
		||||
# ------ kraken ------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[kucoin]
 | 
			
		||||
key_id = ''
 | 
			
		||||
key_secret = ''
 | 
			
		||||
key_passphrase = ''
 | 
			
		||||
# ------ kucoin ------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################
 | 
			
		||||
# -- BROKERZ ---
 | 
			
		||||
################
 | 
			
		||||
 | 
			
		||||
[questrade]
 | 
			
		||||
refresh_token = ''
 | 
			
		||||
access_token = ''
 | 
			
		||||
| 
						 | 
				
			
			@ -42,44 +54,55 @@ api_server = 'https://api06.iq.questrade.com/'
 | 
			
		|||
expires_in = 1800
 | 
			
		||||
token_type = 'Bearer'
 | 
			
		||||
expires_at = 1616095326.355846
 | 
			
		||||
# ------ questrade ------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[ib]
 | 
			
		||||
# define the (set of) host-port socketaddrs that
 | 
			
		||||
# brokerd.ib will scan to connect to an API endpoint
 | 
			
		||||
# (ib-gw or ib-tws listening instances)
 | 
			
		||||
hosts = [
 | 
			
		||||
    '127.0.0.1',
 | 
			
		||||
]
 | 
			
		||||
# XXX: the order in which ports will be scanned
 | 
			
		||||
# (by the `brokerd` daemon-actor)
 | 
			
		||||
# is determined # by the line order here.
 | 
			
		||||
# TODO: when we eventually spawn gateways in our
 | 
			
		||||
# container, we can just dynamically allocate these
 | 
			
		||||
# using IBC.
 | 
			
		||||
ports = [
 | 
			
		||||
    4002,  # gw
 | 
			
		||||
    7497,  # tws
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# XXX: for a paper account the flex web query service
 | 
			
		||||
# is not supported so you have to manually download
 | 
			
		||||
# and XML report and put it in a location that can be
 | 
			
		||||
# accessed by the ``brokerd.ib`` backend code for parsing.
 | 
			
		||||
flex_token = ''
 | 
			
		||||
flex_trades_query_id = ''  # live account
 | 
			
		||||
 | 
			
		||||
# when clients are being scanned this determines
 | 
			
		||||
# which clients are preferred to be used for data
 | 
			
		||||
# feeds based on the order of account names, if
 | 
			
		||||
# detected as active on an API client.
 | 
			
		||||
# When API endpoints are being scanned durin startup, the order
 | 
			
		||||
# of user-defined-account "names" (as defined below) here
 | 
			
		||||
# determines which py-client connection is given priority to be
 | 
			
		||||
# used for data-feed-requests by according to whichever client
 | 
			
		||||
# connected to an API endpoing which reported the equivalent
 | 
			
		||||
# account number for that name.
 | 
			
		||||
prefer_data_account = [
 | 
			
		||||
    'paper',
 | 
			
		||||
    'margin',
 | 
			
		||||
    'ira',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# For long-term trades txn (transaction) history
 | 
			
		||||
# processing (i.e your txn ledger with IB) you can
 | 
			
		||||
# (automatically for live accounts) query the FLEX
 | 
			
		||||
# report system for past history.
 | 
			
		||||
#
 | 
			
		||||
# (For paper accounts the web query service
 | 
			
		||||
# is not supported so you have to manually download
 | 
			
		||||
# an XML report and put it in a location that can be
 | 
			
		||||
# accessed by our `brokerd.ib` backend code for parsing).
 | 
			
		||||
#
 | 
			
		||||
flex_token = ''
 | 
			
		||||
flex_trades_query_id = ''  # live account
 | 
			
		||||
 | 
			
		||||
# define "aliases" (names) for each account number
 | 
			
		||||
# such that the names can be reffed and logged throughout
 | 
			
		||||
# `piker.accounting` subsys and more easily
 | 
			
		||||
# referred to  by the user.
 | 
			
		||||
#
 | 
			
		||||
# These keys will be the set exposed through the order-mode
 | 
			
		||||
# account-selection UI so that numbers are never shown.
 | 
			
		||||
[ib.accounts]
 | 
			
		||||
# the order in which accounts will be selectable
 | 
			
		||||
# in the order mode UI (if found via clients during
 | 
			
		||||
# API-app scanning)when a new symbol is loaded.
 | 
			
		||||
paper = 'XX0000000'
 | 
			
		||||
margin = 'X0000000'
 | 
			
		||||
ira = 'X0000000'
 | 
			
		||||
paper = 'DU0000000'  # <- literal account #
 | 
			
		||||
margin = 'U0000000'
 | 
			
		||||
ira = 'U0000000'
 | 
			
		||||
# ------ ib ------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
[network]
 | 
			
		||||
tsdb.backend = 'marketstore'
 | 
			
		||||
tsdb.host = 'localhost'
 | 
			
		||||
tsdb.grpc_port = 5995
 | 
			
		||||
pikerd = [
 | 
			
		||||
  '/ipv4/127.0.0.1/tcp/6116',  # std localhost daemon-actor tree
 | 
			
		||||
  # '/uds/6116',  # TODO std uds socket file
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[ui]
 | 
			
		||||
# set custom font + size which will scale entire UI
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,30 +1,138 @@
 | 
			
		|||
running ``ib`` gateway in ``docker``
 | 
			
		||||
------------------------------------
 | 
			
		||||
We have a config based on the (now defunct)
 | 
			
		||||
image from "waytrade":
 | 
			
		||||
We have a config based on a well maintained community
 | 
			
		||||
image from `@gnzsnz`:
 | 
			
		||||
 | 
			
		||||
https://github.com/waytrade/ib-gateway-docker
 | 
			
		||||
https://github.com/gnzsnz/ib-gateway-docker
 | 
			
		||||
 | 
			
		||||
To startup this image with our custom settings
 | 
			
		||||
simply run the command::
 | 
			
		||||
 | 
			
		||||
To startup this image simply run the command::
 | 
			
		||||
 | 
			
		||||
    docker compose up
 | 
			
		||||
 | 
			
		||||
And you should have the following socket-available services:
 | 
			
		||||
(For further usage^ see the official `docker-compose`_ docs)
 | 
			
		||||
 | 
			
		||||
- ``x11vnc1@127.0.0.1:3003``
 | 
			
		||||
- ``ib-gw@127.0.0.1:4002``
 | 
			
		||||
 | 
			
		||||
You can attach to the container via a VNC client
 | 
			
		||||
without password auth.
 | 
			
		||||
And you should have the following socket-available services by
 | 
			
		||||
default:
 | 
			
		||||
 | 
			
		||||
SECURITY STUFF!?!?!
 | 
			
		||||
-------------------
 | 
			
		||||
Though "``ib``" claims they host filter connections outside
 | 
			
		||||
localhost (aka ``127.0.0.1``) it's probably better if you filter
 | 
			
		||||
the socket at the OS level using a stateless firewall rule::
 | 
			
		||||
- ``x11vnc1 @ 127.0.0.1:5900``
 | 
			
		||||
- ``ib-gw @ 127.0.0.1:4002``
 | 
			
		||||
 | 
			
		||||
You can now attach to the container via a VNC client with password-auth;
 | 
			
		||||
here is an example using ``vncclient`` on ``linux``::
 | 
			
		||||
 | 
			
		||||
    vncviewer localhost:5900
 | 
			
		||||
 | 
			
		||||
now enter the pw (password) you set via an (see second code blob)
 | 
			
		||||
`.env file`_ or pw-file according to the `credentials section`_.
 | 
			
		||||
 | 
			
		||||
If you want to change away from their default config see the example
 | 
			
		||||
`docker-compose.yml`-config issue and config-section of the readme,
 | 
			
		||||
 | 
			
		||||
  - https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#configuration
 | 
			
		||||
  - https://github.com/gnzsnz/ib-gateway-docker/discussions/103
 | 
			
		||||
 | 
			
		||||
.. _.env file: https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#how-to-use-it
 | 
			
		||||
.. _docker-compose: https://docs.docker.com/compose/
 | 
			
		||||
.. _credentials section: https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#credentials
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Connecting to the API from `piker`
 | 
			
		||||
---------------------------------
 | 
			
		||||
In order to expose the container's API endpoint to the
 | 
			
		||||
`brokerd/datad/ib` actor, we need to add a section to the user's
 | 
			
		||||
`brokers.toml` config (note the below is similar to the repo-shipped
 | 
			
		||||
template file),
 | 
			
		||||
 | 
			
		||||
.. code:: toml
 | 
			
		||||
 | 
			
		||||
    [ib]
 | 
			
		||||
    # define the (set of) host-port socketaddrs that
 | 
			
		||||
    # brokerd.ib will scan to connect to an API endpoint
 | 
			
		||||
    # (ib-gw or ib-tws listening instances)
 | 
			
		||||
    hosts = [
 | 
			
		||||
        '127.0.0.1',
 | 
			
		||||
    ]
 | 
			
		||||
    ports = [
 | 
			
		||||
        4002,  # gw
 | 
			
		||||
        7497,  # tws
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # When API endpoints are being scanned durin startup, the order
 | 
			
		||||
    # of user-defined-account "names" (as defined below) here
 | 
			
		||||
    # determines which py-client connection is given priority to be
 | 
			
		||||
    # used for data-feed-requests by according to whichever client
 | 
			
		||||
    # connected to an API endpoing which reported the equivalent
 | 
			
		||||
    # account number for that name.
 | 
			
		||||
    prefer_data_account = [
 | 
			
		||||
        'paper',
 | 
			
		||||
        'margin',
 | 
			
		||||
        'ira',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # define "aliases" (names) for each account number
 | 
			
		||||
    # such that the names can be reffed and logged throughout
 | 
			
		||||
    # `piker.accounting` subsys and more easily
 | 
			
		||||
    # referred to  by the user.
 | 
			
		||||
    #
 | 
			
		||||
    # These keys will be the set exposed through the order-mode
 | 
			
		||||
    # account-selection UI so that numbers are never shown.
 | 
			
		||||
    [ib.accounts]
 | 
			
		||||
    paper = 'XX0000000'
 | 
			
		||||
    margin = 'X0000000'
 | 
			
		||||
    ira = 'X0000000'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
the broker daemon can also connect to the container's VNC server for
 | 
			
		||||
added functionalies including,
 | 
			
		||||
 | 
			
		||||
- viewing the API endpoint program's GUI for manual interventions,
 | 
			
		||||
- workarounds for historical data throttling using hotkey hacks,
 | 
			
		||||
 | 
			
		||||
Add a further section to `brokers.toml` which maps each API-ep's
 | 
			
		||||
port to a table of VNC server connection info like,
 | 
			
		||||
 | 
			
		||||
.. code:: toml
 | 
			
		||||
 | 
			
		||||
    [ib.vnc_addrs]
 | 
			
		||||
    4002 = {host = 'localhost', port = 5900, pw = 'doggy'}
 | 
			
		||||
 | 
			
		||||
The `pw = 'doggy'` here ^ should the same value as the particular
 | 
			
		||||
container instances `.env` file setting (when it was run),
 | 
			
		||||
 | 
			
		||||
.. code:: ini
 | 
			
		||||
 | 
			
		||||
    VNC_SERVER_PASSWORD='doggy'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IF you also want to run ``TWS``
 | 
			
		||||
-------------------------------
 | 
			
		||||
You can also run it containerized,
 | 
			
		||||
 | 
			
		||||
https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#using-tws
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SECURITY stuff (advanced, only if you're paranoid)
 | 
			
		||||
--------------------------------------------------
 | 
			
		||||
First and foremost if doing a "distributed" container setup where you
 | 
			
		||||
run the ``ib-gw`` docker container and your connecting API client
 | 
			
		||||
(likely ``ib_async`` from python) on **different hosts** be sure to
 | 
			
		||||
read the `security considerations`_ section!
 | 
			
		||||
 | 
			
		||||
And for a further (somewhat paranoid) perspective from
 | 
			
		||||
a long-time-ago serious devops eng..
 | 
			
		||||
 | 
			
		||||
Though "``ib``" claims they filter remote host connections outside
 | 
			
		||||
``localhost`` (aka ``127.0.0.1`` on ipv4) it's prolly justified if
 | 
			
		||||
you'd like to filter the socket at the *OS level* using a stateless
 | 
			
		||||
firewall rule::
 | 
			
		||||
 | 
			
		||||
    ip rule add not unicast iif lo to 0.0.0.0/0 dport 4002
 | 
			
		||||
 | 
			
		||||
We will soon have this baked into our own custom image but for
 | 
			
		||||
now you'll have to do it urself dawgy.
 | 
			
		||||
 | 
			
		||||
We will soon have this either baked into our own custom derivative
 | 
			
		||||
image (or patched into the current upstream one after further testin)
 | 
			
		||||
but for now you'll have to do it urself, diggity dawg.
 | 
			
		||||
 | 
			
		||||
.. _security considerations: https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#security-considerations
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,15 @@
 | 
			
		|||
# rework from the original @
 | 
			
		||||
# https://github.com/waytrade/ib-gateway-docker/blob/master/docker-compose.yml
 | 
			
		||||
version: "3.5"
 | 
			
		||||
 | 
			
		||||
# a community maintained IB API container!
 | 
			
		||||
#
 | 
			
		||||
# https://github.com/gnzsnz/ib-gateway-docker
 | 
			
		||||
#
 | 
			
		||||
# For piker we (currently) include some minor deviations
 | 
			
		||||
# for some config files in the `volumes` section.
 | 
			
		||||
#
 | 
			
		||||
# See full configuration settings @
 | 
			
		||||
# - https://github.com/gnzsnz/ib-gateway-docker?tab=readme-ov-file#configuration
 | 
			
		||||
# - https://github.com/gnzsnz/ib-gateway-docker/discussions/103
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  ib_gw_paper:
 | 
			
		||||
 | 
			
		||||
    # apparently java is a mega cukc:
 | 
			
		||||
| 
						 | 
				
			
			@ -50,16 +55,22 @@ services:
 | 
			
		|||
        target: /root/scripts/run_x11_vnc.sh
 | 
			
		||||
        read_only: true
 | 
			
		||||
 | 
			
		||||
    # NOTE:to fill these out, define an `.env` file in the same dir as
 | 
			
		||||
    # this compose file which looks something like:
 | 
			
		||||
    # TWS_USERID='myuser'
 | 
			
		||||
    # TWS_PASSWORD='guest'
 | 
			
		||||
    # NOTE: an alt method to fill these out is to
 | 
			
		||||
    # define an `.env` file in the same dir as
 | 
			
		||||
    # this compose file.
 | 
			
		||||
    environment:
 | 
			
		||||
      TWS_USERID: ${TWS_USERID}
 | 
			
		||||
      # TWS_USERID: 'myuser'
 | 
			
		||||
      TWS_PASSWORD: ${TWS_PASSWORD}
 | 
			
		||||
      TRADING_MODE: 'paper'
 | 
			
		||||
      VNC_SERVER_PASSWORD: 'doggy'
 | 
			
		||||
      VNC_SERVER_PORT: '3003'
 | 
			
		||||
      # TWS_PASSWORD: 'guest'
 | 
			
		||||
      TRADING_MODE: ${TRADING_MODE}
 | 
			
		||||
      # TRADING_MODE: 'paper'
 | 
			
		||||
      VNC_SERVER_PASSWORD: ${VNC_SERVER_PASSWORD}
 | 
			
		||||
      # VNC_SERVER_PASSWORD: 'doggy'
 | 
			
		||||
 | 
			
		||||
      # TODO, see if we can get this supported like it
 | 
			
		||||
      # was on the old `waytrade` image?
 | 
			
		||||
      # VNC_SERVER_PORT: '3003'
 | 
			
		||||
 | 
			
		||||
    # ports:
 | 
			
		||||
    #   - target: 4002
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +87,9 @@ services:
 | 
			
		|||
      # - "127.0.0.1:4002:4002"
 | 
			
		||||
      # - "127.0.0.1:5900:5900"
 | 
			
		||||
 | 
			
		||||
  # TODO, a masked but working example of dual paper + live
 | 
			
		||||
  # ib-gw instances running in a single app run!
 | 
			
		||||
  #
 | 
			
		||||
  # ib_gw_live:
 | 
			
		||||
  #   image: waytrade/ib-gateway:1012.2i
 | 
			
		||||
  #   restart: no
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -289,15 +289,26 @@ def iter_by_dt(
 | 
			
		|||
            else:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        # XXX: should never get here..
 | 
			
		||||
        # XXX: we should never really get here bc it means some kinda
 | 
			
		||||
        # bad txn-record (field) data..
 | 
			
		||||
        #
 | 
			
		||||
        # -> set the `debug_mode = True` if you want to trace such
 | 
			
		||||
        # cases from REPL ;)
 | 
			
		||||
        else:
 | 
			
		||||
            with maybe_open_crash_handler(pdb=True):
 | 
			
		||||
                raise ValueError(
 | 
			
		||||
                    f'Invalid txn time ??\n'
 | 
			
		||||
                    f'txn-id: {k!r}\n'
 | 
			
		||||
                    f'{k!r}: {v!r}\n'
 | 
			
		||||
                )
 | 
			
		||||
                    # assert v is not None, f'No valid value for `{k}`!?'
 | 
			
		||||
            debug_mode: bool = False
 | 
			
		||||
            report: str = (
 | 
			
		||||
                f'Invalid txn time ??\n'
 | 
			
		||||
                f'txn-id: {k!r}\n'
 | 
			
		||||
                f'{k!r}: {v!r}\n'
 | 
			
		||||
            )
 | 
			
		||||
            if debug_mode:
 | 
			
		||||
                with maybe_open_crash_handler(
 | 
			
		||||
                    pdb=debug_mode,
 | 
			
		||||
                    raise_on_exit=False,
 | 
			
		||||
                ):
 | 
			
		||||
                    raise ValueError(report)
 | 
			
		||||
            else:
 | 
			
		||||
                log.error(report)
 | 
			
		||||
 | 
			
		||||
            if _invalid is not None:
 | 
			
		||||
                _invalid.append(tx)
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +411,10 @@ def open_ledger_dfs(
 | 
			
		|||
    can update the ledger on exit.
 | 
			
		||||
 | 
			
		||||
    '''
 | 
			
		||||
    with maybe_open_crash_handler(pdb=debug_mode):
 | 
			
		||||
    with maybe_open_crash_handler(
 | 
			
		||||
        pdb=debug_mode,
 | 
			
		||||
        # raise_on_exit=False,
 | 
			
		||||
    ):
 | 
			
		||||
        if not ledger:
 | 
			
		||||
            import time
 | 
			
		||||
            from ._ledger import open_trade_ledger
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ class Pair(Struct, frozen=True, kw_only=True):
 | 
			
		|||
    # https://developers.binance.com/docs/binance-spot-api-docs#2025-08-26
 | 
			
		||||
    # will become non-optional 2025-08-28?
 | 
			
		||||
    # https://developers.binance.com/docs/binance-spot-api-docs#future-changes
 | 
			
		||||
    pegInstructionsAllowed: bool|None = None
 | 
			
		||||
    pegInstructionsAllowed: bool = False
 | 
			
		||||
 | 
			
		||||
    filters: dict[
 | 
			
		||||
        str,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,6 @@ from piker.brokers._util import get_logger
 | 
			
		|||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from .api import Client
 | 
			
		||||
    from ib_insync import IB
 | 
			
		||||
    import i3ipc
 | 
			
		||||
 | 
			
		||||
log = get_logger('piker.brokers.ib')
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +61,7 @@ no_setup_msg:str = (
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def try_xdo_manual(
 | 
			
		||||
    vnc_sockaddr: str,
 | 
			
		||||
    client: Client,
 | 
			
		||||
):
 | 
			
		||||
    '''
 | 
			
		||||
    Do the "manual" `xdo`-based screen switch + click
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +78,7 @@ def try_xdo_manual(
 | 
			
		|||
        _reset_tech = 'i3ipc_xdotool'
 | 
			
		||||
        return True
 | 
			
		||||
    except OSError:
 | 
			
		||||
        vnc_sockaddr: str = client.conf.vnc_addrs
 | 
			
		||||
        log.exception(
 | 
			
		||||
            no_setup_msg.format(vnc_sockaddr=vnc_sockaddr)
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,6 @@ def try_xdo_manual(
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
async def data_reset_hack(
 | 
			
		||||
    # vnc_host: str,
 | 
			
		||||
    client: Client,
 | 
			
		||||
    reset_type: Literal['data', 'connection'],
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -118,36 +117,24 @@ async def data_reset_hack(
 | 
			
		|||
          that need to be wrangle.
 | 
			
		||||
 | 
			
		||||
    '''
 | 
			
		||||
    ib_client: IB = client.ib
 | 
			
		||||
 | 
			
		||||
    # look up any user defined vnc socket address mapped from
 | 
			
		||||
    # a particular API socket port.
 | 
			
		||||
    api_port: str = str(ib_client.client.port)
 | 
			
		||||
    vnc_host: str
 | 
			
		||||
    vnc_port: int
 | 
			
		||||
    vnc_sockaddr: tuple[str] | None = client.conf.get('vnc_addrs')
 | 
			
		||||
 | 
			
		||||
    if not vnc_sockaddr:
 | 
			
		||||
    vnc_addrs: tuple[str]|None = client.conf.get('vnc_addrs')
 | 
			
		||||
    if not vnc_addrs:
 | 
			
		||||
        log.warning(
 | 
			
		||||
            no_setup_msg.format(vnc_sockaddr=vnc_sockaddr)
 | 
			
		||||
            no_setup_msg.format(vnc_sockaddr=client.conf)
 | 
			
		||||
            +
 | 
			
		||||
            'REQUIRES A `vnc_addrs: array` ENTRY'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    vnc_host, vnc_port = vnc_sockaddr.get(
 | 
			
		||||
        api_port,
 | 
			
		||||
        ('localhost', 3003)
 | 
			
		||||
    )
 | 
			
		||||
    global _reset_tech
 | 
			
		||||
 | 
			
		||||
    match _reset_tech:
 | 
			
		||||
        case 'vnc':
 | 
			
		||||
            try:
 | 
			
		||||
                await tractor.to_asyncio.run_task(
 | 
			
		||||
                    partial(
 | 
			
		||||
                        vnc_click_hack,
 | 
			
		||||
                        host=vnc_host,
 | 
			
		||||
                        port=vnc_port,
 | 
			
		||||
                        client=client,
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            except (
 | 
			
		||||
| 
						 | 
				
			
			@ -158,29 +145,31 @@ async def data_reset_hack(
 | 
			
		|||
                    import i3ipc  # noqa  (since a deps dynamic check)
 | 
			
		||||
                except ModuleNotFoundError:
 | 
			
		||||
                    log.warning(
 | 
			
		||||
                        no_setup_msg.format(vnc_sockaddr=vnc_sockaddr)
 | 
			
		||||
                        no_setup_msg.format(vnc_sockaddr=client.conf)
 | 
			
		||||
                    )
 | 
			
		||||
                    return False
 | 
			
		||||
 | 
			
		||||
                if vnc_host not in {
 | 
			
		||||
                    'localhost',
 | 
			
		||||
                    '127.0.0.1',
 | 
			
		||||
                }:
 | 
			
		||||
                    focussed, matches = i3ipc_fin_wins_titled()
 | 
			
		||||
                    if not matches:
 | 
			
		||||
                        log.warning(
 | 
			
		||||
                            no_setup_msg.format(vnc_sockaddr=vnc_sockaddr)
 | 
			
		||||
                        )
 | 
			
		||||
                        return False
 | 
			
		||||
                    else:
 | 
			
		||||
                        try_xdo_manual(vnc_sockaddr)
 | 
			
		||||
                # XXX, Xorg only workaround..
 | 
			
		||||
                # TODO? remove now that we have `pyvnc`?
 | 
			
		||||
                # if vnc_host not in {
 | 
			
		||||
                #     'localhost',
 | 
			
		||||
                #     '127.0.0.1',
 | 
			
		||||
                # }:
 | 
			
		||||
                #     focussed, matches = i3ipc_fin_wins_titled()
 | 
			
		||||
                #     if not matches:
 | 
			
		||||
                #         log.warning(
 | 
			
		||||
                #             no_setup_msg.format(vnc_sockaddr=vnc_sockaddr)
 | 
			
		||||
                #         )
 | 
			
		||||
                #         return False
 | 
			
		||||
                #     else:
 | 
			
		||||
                #         try_xdo_manual(vnc_sockaddr)
 | 
			
		||||
 | 
			
		||||
                # localhost but no vnc-client or it borked..
 | 
			
		||||
                else:
 | 
			
		||||
                    try_xdo_manual(vnc_sockaddr)
 | 
			
		||||
                    try_xdo_manual(client)
 | 
			
		||||
 | 
			
		||||
        case 'i3ipc_xdotool':
 | 
			
		||||
            try_xdo_manual(vnc_sockaddr)
 | 
			
		||||
            try_xdo_manual(client)
 | 
			
		||||
            # i3ipc_xdotool_manual_click_hack()
 | 
			
		||||
 | 
			
		||||
        case _ as tech:
 | 
			
		||||
| 
						 | 
				
			
			@ -191,15 +180,55 @@ async def data_reset_hack(
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
async def vnc_click_hack(
 | 
			
		||||
    host: str,
 | 
			
		||||
    port: int,
 | 
			
		||||
    reset_type: str = 'data'
 | 
			
		||||
    client: Client,
 | 
			
		||||
    reset_type: str = 'data',
 | 
			
		||||
    pw: str|None = None,
 | 
			
		||||
 | 
			
		||||
) -> None:
 | 
			
		||||
    '''
 | 
			
		||||
    Reset the data or network connection for the VNC attached
 | 
			
		||||
    ib-gateway using a (magic) keybinding combo.
 | 
			
		||||
 | 
			
		||||
    A vnc-server password can be set either by an input `pw` param or
 | 
			
		||||
    set in the client's config with the latter loaded from the user's
 | 
			
		||||
    `brokers.toml` in a vnc-addrs-port-mapping section,
 | 
			
		||||
 | 
			
		||||
    .. code:: toml
 | 
			
		||||
 | 
			
		||||
        [ib.vnc_addrs]
 | 
			
		||||
        4002 = {host = 'localhost', port = 5900, pw = 'doggy'}
 | 
			
		||||
 | 
			
		||||
    '''
 | 
			
		||||
    api_port: str = str(client.ib.client.port)
 | 
			
		||||
    conf: dict = client.conf
 | 
			
		||||
    vnc_addrs: dict[int, tuple] = conf.get('vnc_addrs')
 | 
			
		||||
    if not vnc_addrs:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    addr_entry: dict|tuple = vnc_addrs.get(
 | 
			
		||||
        api_port,
 | 
			
		||||
        ('localhost', 5900)  # a typical default
 | 
			
		||||
    )
 | 
			
		||||
    if pw is None:
 | 
			
		||||
        match addr_entry:
 | 
			
		||||
            case (
 | 
			
		||||
                host,
 | 
			
		||||
                port,
 | 
			
		||||
            ):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
            case {
 | 
			
		||||
                'host': host,
 | 
			
		||||
                'port': port,
 | 
			
		||||
                'pw': pw
 | 
			
		||||
            }:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
            case _:
 | 
			
		||||
                raise ValueError(
 | 
			
		||||
                    f'Invalid `ib.vnc_addrs` entry ?\n'
 | 
			
		||||
                    f'{addr_entry!r}\n'
 | 
			
		||||
                )
 | 
			
		||||
    try:
 | 
			
		||||
        from pyvnc import (
 | 
			
		||||
            AsyncVNCClient,
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +255,7 @@ async def vnc_click_hack(
 | 
			
		|||
            VNCConfig(
 | 
			
		||||
                host=host,
 | 
			
		||||
                port=port,
 | 
			
		||||
                password='doggy',
 | 
			
		||||
                password=pw,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        async with client:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -214,7 +214,9 @@ async def open_history_client(
 | 
			
		|||
 | 
			
		||||
            # could be trying to retreive bars over weekend
 | 
			
		||||
            if out is None:
 | 
			
		||||
                log.error(f"Can't grab bars starting at {end_dt}!?!?")
 | 
			
		||||
                log.error(
 | 
			
		||||
                    f"No bars starting at {end_dt!r} !?!?"
 | 
			
		||||
                )
 | 
			
		||||
                if (
 | 
			
		||||
                    end_dt
 | 
			
		||||
                    and head_dt
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,6 @@ from typing import (
 | 
			
		|||
 | 
			
		||||
import numpy as np
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from .. import config
 | 
			
		||||
from ..service import (
 | 
			
		||||
    check_for_service,
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +161,10 @@ class StorageConnectionError(ConnectionError):
 | 
			
		|||
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
def get_storagemod(name: str) -> ModuleType:
 | 
			
		||||
def get_storagemod(
 | 
			
		||||
    name: str,
 | 
			
		||||
 | 
			
		||||
) -> ModuleType:
 | 
			
		||||
    mod: ModuleType = import_module(
 | 
			
		||||
        '.' + name,
 | 
			
		||||
        'piker.storage',
 | 
			
		||||
| 
						 | 
				
			
			@ -175,9 +177,12 @@ def get_storagemod(name: str) -> ModuleType:
 | 
			
		|||
 | 
			
		||||
@acm
 | 
			
		||||
async def open_storage_client(
 | 
			
		||||
    backend: str | None = None,
 | 
			
		||||
    backend: str|None = None,
 | 
			
		||||
 | 
			
		||||
) -> tuple[ModuleType, StorageClient]:
 | 
			
		||||
) -> tuple[
 | 
			
		||||
    ModuleType,
 | 
			
		||||
    StorageClient,
 | 
			
		||||
]:
 | 
			
		||||
    '''
 | 
			
		||||
    Load the ``StorageClient`` for named backend.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +282,10 @@ async def open_tsdb_client(
 | 
			
		|||
    from ..data.feed import maybe_open_feed
 | 
			
		||||
 | 
			
		||||
    async with (
 | 
			
		||||
        open_storage_client() as (_, storage),
 | 
			
		||||
        open_storage_client() as (
 | 
			
		||||
            _,
 | 
			
		||||
            storage,
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
        maybe_open_feed(
 | 
			
		||||
            [fqme],
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +293,7 @@ async def open_tsdb_client(
 | 
			
		|||
 | 
			
		||||
        ) as feed,
 | 
			
		||||
    ):
 | 
			
		||||
        profiler(f'opened feed for {fqme}')
 | 
			
		||||
        profiler(f'opened feed for {fqme!r}')
 | 
			
		||||
 | 
			
		||||
        # to_append = feed.hist_shm.array
 | 
			
		||||
        # to_prepend = None
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue