Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LIFO/"breakeven" pps for ib #336

Merged
merged 58 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
450009f
Add `open_trade_ledger()` for writing `<confdir>/ledgers/trades_<brok…
goodboy Jun 7, 2022
050aa75
Simplify trades ledger collection to single pass loop
goodboy Jun 7, 2022
725909a
Convert accounts table to `bidict` after config load
goodboy Jun 7, 2022
1eb7e10
Start `piker.pp` module, LIFO pp updates
goodboy Jun 8, 2022
add0e92
Drop old trade log config writing code
goodboy Jun 8, 2022
f768576
Delegate paper engine pp tracking to new type
goodboy Jun 8, 2022
eb2bad5
Make our `Symbol` a `msgspec.Struct`
goodboy Jun 10, 2022
88b4ccc
Add API trade/exec entry parsing and ledger updates
goodboy Jun 10, 2022
f8f7ca3
Extend trade-record tools, add ledger to pps extraction
goodboy Jun 10, 2022
2a641ab
Call it `pps.toml`, allows toml passthrough kwargs
goodboy Jun 10, 2022
dd05ed1
Implement updates and write to config: `pps.toml`
goodboy Jun 10, 2022
73fa320
Cut schema-related comment down to major sections
goodboy Jun 10, 2022
b629ce1
Ensure `.fills` are filled in during object construct..
goodboy Jun 11, 2022
ce1eb11
Use new ledger pps but cross-ref with what ib says
goodboy Jun 11, 2022
de77c7d
Better doc strings and detailed comments
goodboy Jun 11, 2022
5d774be
Move `open_trade_ledger()` to pp mod, add `get_pps()`
goodboy Jun 13, 2022
c1b63f4
Use `IB.fills()` method for `Client.trades()`
goodboy Jun 14, 2022
412138a
Add transaction costs to "fills"
goodboy Jun 14, 2022
05a1a4e
Use new `Position.bsuid` field throughout
goodboy Jun 14, 2022
82b718d
Many, many `ib` trade log schema hackz
goodboy Jun 14, 2022
cd3bfb1
Maybe load from ledger in `get_pps()`, allow account filtering
goodboy Jun 15, 2022
cbcbb2b
Filter pps loading to client-active accounts set
goodboy Jun 15, 2022
7b2e8f1
Return object form from `update_pps_conf()`
goodboy Jun 15, 2022
3991d8f
Add `update_and_audit()` in prep for rt per-trade-event pp udpates
goodboy Jun 15, 2022
fbee33b
Get real-time trade oriented pp updates workin
goodboy Jun 16, 2022
3dcb72d
Only finally-write around the ledger yield up
goodboy Jun 16, 2022
5147cd7
Drop global proxies table, isn't multi-task safe..
goodboy Jun 16, 2022
ecdc747
Allow packing pps by a different key set
goodboy Jun 16, 2022
b6f344f
Only emit pps msg for trade triggering instrument
goodboy Jun 16, 2022
21153a0
Ugh, hack our own toml encoder since it seems everything in the lib i…
goodboy Jun 17, 2022
ff74f43
Support pp expiries, datetimes on transactions
goodboy Jun 18, 2022
c617a06
Port everything to `Position.be_price`
goodboy Jun 18, 2022
bfad676
Add expiry and datetime support to ledger parsing
goodboy Jun 18, 2022
16b2937
Passthrough toml lib kwargs
goodboy Jun 18, 2022
f1fe369
Write clears table as a list of tables in toml
goodboy Jun 18, 2022
68b3220
Key pps by bsuid to avoid incorrect disparate entries
goodboy Jun 19, 2022
fe14605
Fix null case return
goodboy Jun 20, 2022
2063b9d
Drop ledger entries that have no transaction id
goodboy Jun 20, 2022
f32b4d3
Support pp audits with multiple accounts
goodboy Jun 20, 2022
4fdfb81
Support re-processing a filtered ledger entry set
goodboy Jun 21, 2022
3713288
Strip ib prefix before acctid use
goodboy Jun 21, 2022
4475823
Add draft ip-mismatch skip case
goodboy Jun 21, 2022
7ebf8a8
Add `tomli` as dep being fastest in the west
goodboy Jun 21, 2022
cc68501
Make pp msg `.currency` not required
goodboy Jun 21, 2022
a12e680
Support per-symbol reload from ledger pp loading
goodboy Jun 22, 2022
f9c4b3c
Fixes for newly opened and closed pps
goodboy Jun 22, 2022
566a54f
Reset the clears table on zero size conditions
goodboy Jun 22, 2022
87f3015
Simplify updates to single-pass, fix clears minimizing
goodboy Jun 23, 2022
aec48a1
Right, zero sized "closed out" msgs are totally fine
goodboy Jun 23, 2022
8a7e391
Terser startup msg fields
goodboy Jun 23, 2022
c6efa26
Cost part of position breakeven calc is direction dependent
goodboy Jun 23, 2022
557562e
Build out adhoc sym map from futes list
goodboy Jun 24, 2022
fa89207
Use sign of the new size which indicates direction of position
goodboy Jun 24, 2022
d6c32bb
Use new adhoc sym map for symbols without exchange tags (usually futes)
goodboy Jun 24, 2022
695ba52
Comment-drop adhoc symbol (futes) matching in search
goodboy Jun 24, 2022
2b1fb90
Add tractor breaker assert..
goodboy Jun 25, 2022
453ebdf
Fix field name to new `.bsuid`
goodboy Jun 25, 2022
287a2c8
Put swb2 in venue filter for now
goodboy Jun 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion piker/brokers/ib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
open_symbol_search,
stream_quotes,
)
from .broker import trades_dialogue
from .broker import (
trades_dialogue,
norm_trade_records,
)

__all__ = [
'get_client',
Expand Down
122 changes: 85 additions & 37 deletions piker/brokers/ib/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,21 @@
from types import SimpleNamespace


from bidict import bidict
import trio
import tractor
from tractor import to_asyncio
import ib_insync as ibis
from ib_insync.wrapper import RequestError
from ib_insync.contract import Contract, ContractDetails
from ib_insync.order import Order
from ib_insync.ticker import Ticker
from ib_insync.objects import Position
import ib_insync as ibis
from ib_insync.objects import (
Position,
Fill,
Execution,
CommissionReport,
)
from ib_insync.wrapper import Wrapper
from ib_insync.client import Client as ib_Client
import numpy as np
Expand Down Expand Up @@ -155,30 +161,23 @@ def __init__(self):
self.client.apiEnd += self.disconnectedEvent


# map of symbols to contract ids
_adhoc_cmdty_data_map = {
# https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=69067924

# NOTE: some cmdtys/metals don't have trade data like gold/usd:
# https://groups.io/g/twsapi/message/44174
'XAUUSD': ({'conId': 69067924}, {'whatToShow': 'MIDPOINT'}),
}

_futes_venues = (
'GLOBEX',
'NYMEX',
'CME',
'CMECRYPTO',
'COMEX',
'CMDTY', # special name case..
)

_adhoc_futes_set = {

# equities
'nq.globex',
'mnq.globex',
'mnq.globex', # micro

'es.globex',
'mes.globex',
'mes.globex', # micro

# cypto$
'brr.cmecrypto',
Expand All @@ -195,20 +194,46 @@ def __init__(self):
# metals
'xauusd.cmdty', # gold spot
'gc.nymex',
'mgc.nymex',
'mgc.nymex', # micro

# oil & gas
'cl.nymex',

'xagusd.cmdty', # silver spot
'ni.nymex', # silver futes
'qi.comex', # mini-silver futes
}


# map of symbols to contract ids
_adhoc_symbol_map = {
# https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=69067924

# NOTE: some cmdtys/metals don't have trade data like gold/usd:
# https://groups.io/g/twsapi/message/44174
'XAUUSD': ({'conId': 69067924}, {'whatToShow': 'MIDPOINT'}),
}
for qsn in _adhoc_futes_set:
sym, venue = qsn.split('.')
assert venue.upper() in _futes_venues, f'{venue}'
_adhoc_symbol_map[sym.upper()] = (
{'exchange': venue},
{},
)


# exchanges we don't support at the moment due to not knowing
# how to do symbol-contract lookup correctly likely due
# to not having the data feeds subscribed.
_exch_skip_list = {

'ASX', # aussie stocks
'MEXI', # mexican stocks
'VALUE', # no idea

# no idea
'VALUE',
'FUNDSERV',
'SWB2',
}

# https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=69067924
Expand Down Expand Up @@ -261,27 +286,29 @@ def __init__(

# NOTE: the ib.client here is "throttled" to 45 rps by default

async def trades(
self,
# api_only: bool = False,

) -> dict[str, Any]:
async def trades(self) -> dict[str, Any]:
'''
Return list of trade-fills from current session in ``dict``.

# orders = await self.ib.reqCompletedOrdersAsync(
# apiOnly=api_only
# )
fills = await self.ib.reqExecutionsAsync()
norm_fills = []
'''
fills: list[Fill] = self.ib.fills()
norm_fills: list[dict] = []
for fill in fills:
fill = fill._asdict() # namedtuple
for key, val in fill.copy().items():
if isinstance(val, Contract):
fill[key] = asdict(val)
for key, val in fill.items():
match val:
case Contract() | Execution() | CommissionReport():
fill[key] = asdict(val)

norm_fills.append(fill)

return norm_fills

async def orders(self) -> list[Order]:
return await self.ib.reqAllOpenOrdersAsync(
apiOnly=False,
)

async def bars(
self,
fqsn: str,
Expand Down Expand Up @@ -483,6 +510,14 @@ async def get_fute(

return con

async def get_con(
self,
conid: int,
) -> Contract:
return await self.ib.qualifyContractsAsync(
ibis.Contract(conId=conid)
)

async def find_contract(
self,
pattern: str,
Expand Down Expand Up @@ -553,7 +588,7 @@ async def find_contract(

# commodities
elif exch == 'CMDTY': # eg. XAUUSD.CMDTY
con_kwargs, bars_kwargs = _adhoc_cmdty_data_map[sym]
con_kwargs, bars_kwargs = _adhoc_symbol_map[sym]
con = ibis.Commodity(**con_kwargs)
con.bars_kwargs = bars_kwargs

Expand Down Expand Up @@ -811,10 +846,23 @@ def positions(

def get_config() -> dict[str, Any]:

conf, path = config.load()

conf, path = config.load('brokers')
section = conf.get('ib')

accounts = section.get('accounts')
if not accounts:
raise ValueError(
'brokers.toml -> `ib.accounts` must be defined\n'
f'location: {path}'
)

names = list(accounts.keys())
accts = section['accounts'] = bidict(accounts)
log.info(
f'brokers.toml defines {len(accts)} accounts: '
f'{pformat(names)}'
)

if section is None:
log.warning(f'No config section found for ib in {path}')
return {}
Expand Down Expand Up @@ -990,7 +1038,7 @@ async def load_aio_clients(
for acct, client in _accounts2clients.items():
log.info(f'Disconnecting {acct}@{client}')
client.ib.disconnect()
_client_cache.pop((host, port))
_client_cache.pop((host, port), None)


async def load_clients_for_trio(
Expand Down Expand Up @@ -1019,9 +1067,6 @@ async def load_clients_for_trio(
await asyncio.sleep(float('inf'))


_proxies: dict[str, MethodProxy] = {}


@acm
async def open_client_proxies() -> tuple[
dict[str, MethodProxy],
Expand All @@ -1044,13 +1089,14 @@ async def open_client_proxies() -> tuple[
if cache_hit:
log.info(f'Re-using cached clients: {clients}')

proxies = {}
for acct_name, client in clients.items():
proxy = await stack.enter_async_context(
open_client_proxy(client),
)
_proxies[acct_name] = proxy
proxies[acct_name] = proxy

yield _proxies, clients
yield proxies, clients


def get_preferred_data_client(
Expand Down Expand Up @@ -1199,11 +1245,13 @@ async def open_client_proxy(
event_table = {}

async with (

to_asyncio.open_channel_from(
open_aio_client_method_relay,
client=client,
event_consumers=event_table,
) as (first, chan),

trio.open_nursery() as relay_n,
):

Expand Down
Loading