commit
bc59d476b1
|
@ -517,9 +517,9 @@ class Client:
|
|||
contract, ticker, details = await self.get_sym_details(symbol)
|
||||
|
||||
# ensure a last price gets filled in before we deliver quote
|
||||
for _ in range(1):
|
||||
for _ in range(100):
|
||||
if isnan(ticker.last):
|
||||
await asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.01)
|
||||
log.warning(f'Quote for {symbol} timed out: market is closed?')
|
||||
ticker = await ticker.updateEvent
|
||||
else:
|
||||
|
@ -792,7 +792,7 @@ async def load_aio_clients(
|
|||
try_ports = list(ports.values())
|
||||
ports = try_ports if port is None else [port]
|
||||
# we_connected = []
|
||||
connect_timeout = 1 if platform.system() != 'Windows' else 2
|
||||
connect_timeout = 2
|
||||
combos = list(itertools.product(hosts, ports))
|
||||
|
||||
# allocate new and/or reload disconnected but cached clients
|
||||
|
@ -1428,8 +1428,14 @@ async def stream_quotes(
|
|||
'''
|
||||
# TODO: support multiple subscriptions
|
||||
sym = symbols[0]
|
||||
details: Optional[dict] = None
|
||||
|
||||
with trio.fail_after(16):
|
||||
contract, first_ticker, details = await _trio_run_client_method(
|
||||
method='get_sym_details',
|
||||
symbol=sym,
|
||||
)
|
||||
|
||||
with trio.move_on_after(1):
|
||||
contract, first_ticker, details = await _trio_run_client_method(
|
||||
method='get_quote',
|
||||
symbol=sym,
|
||||
|
|
|
@ -29,7 +29,8 @@ from ._messages import BrokerdPosition, Status
|
|||
|
||||
|
||||
class Position(BaseModel):
|
||||
'''Basic pp (personal position) model with attached fills history.
|
||||
'''
|
||||
Basic pp (personal position) model with attached fills history.
|
||||
|
||||
This type should be IPC wire ready?
|
||||
|
||||
|
@ -61,6 +62,15 @@ class Position(BaseModel):
|
|||
self.avg_price = avg_price
|
||||
self.size = size
|
||||
|
||||
@property
|
||||
def dsize(self) -> float:
|
||||
'''
|
||||
The "dollar" size of the pp, normally in trading (fiat) unit
|
||||
terms.
|
||||
|
||||
'''
|
||||
return self.avg_price * self.size
|
||||
|
||||
|
||||
_size_units = bidict({
|
||||
'currency': '$ size',
|
||||
|
@ -114,7 +124,8 @@ class Allocator(BaseModel):
|
|||
def step_sizes(
|
||||
self,
|
||||
) -> (float, float):
|
||||
'''Return the units size for each unit type as a tuple.
|
||||
'''
|
||||
Return the units size for each unit type as a tuple.
|
||||
|
||||
'''
|
||||
slots = self.slots
|
||||
|
@ -142,7 +153,8 @@ class Allocator(BaseModel):
|
|||
action: str,
|
||||
|
||||
) -> dict:
|
||||
'''Generate order request info for the "next" submittable order
|
||||
'''
|
||||
Generate order request info for the "next" submittable order
|
||||
depending on position / order entry config.
|
||||
|
||||
'''
|
||||
|
@ -250,7 +262,8 @@ class Allocator(BaseModel):
|
|||
pp: Position,
|
||||
|
||||
) -> float:
|
||||
'''Calc and return the number of slots used by this ``Position``.
|
||||
'''
|
||||
Calc and return the number of slots used by this ``Position``.
|
||||
|
||||
'''
|
||||
abs_pp_size = abs(pp.size)
|
||||
|
|
|
@ -21,6 +21,7 @@ Text entry "forms" widgets (mostly for configuration and UI user input).
|
|||
from __future__ import annotations
|
||||
from contextlib import asynccontextmanager
|
||||
from functools import partial
|
||||
from math import floor
|
||||
from typing import (
|
||||
Optional, Any, Callable, Awaitable
|
||||
)
|
||||
|
@ -542,7 +543,7 @@ class FillStatusBar(QProgressBar):
|
|||
|
||||
'''
|
||||
border_px: int = 2
|
||||
slot_margin_px: int = 2
|
||||
slot_margin_px: int = 1
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -553,7 +554,11 @@ class FillStatusBar(QProgressBar):
|
|||
|
||||
) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self.approx_h = approx_height_px
|
||||
|
||||
self.approx_h = int(round(approx_height_px))
|
||||
self.setMinimumHeight(self.approx_h)
|
||||
self.setMaximumHeight(self.approx_h)
|
||||
|
||||
self.font_size = font_size
|
||||
|
||||
self.setFormat('') # label format
|
||||
|
@ -567,17 +572,12 @@ class FillStatusBar(QProgressBar):
|
|||
|
||||
) -> None:
|
||||
|
||||
approx_h = self.approx_h
|
||||
# TODO: compute "used height" thus far and mostly fill the rest
|
||||
tot_slot_h, r = divmod(
|
||||
approx_h,
|
||||
slots,
|
||||
)
|
||||
clipped = int(slots * tot_slot_h + 2*self.border_px)
|
||||
self.setMaximumHeight(clipped)
|
||||
slot_height_px = tot_slot_h - 2*self.slot_margin_px
|
||||
|
||||
self.setOrientation(Qt.Vertical)
|
||||
h = self.height()
|
||||
|
||||
# TODO: compute "used height" thus far and mostly fill the rest
|
||||
tot_slot_h, r = divmod(h, slots)
|
||||
|
||||
self.setStyleSheet(
|
||||
f"""
|
||||
QProgressBar {{
|
||||
|
@ -592,21 +592,28 @@ class FillStatusBar(QProgressBar):
|
|||
border: {self.border_px}px solid {hcolor('default_light')};
|
||||
border-radius: 2px;
|
||||
}}
|
||||
|
||||
QProgressBar::chunk {{
|
||||
|
||||
background-color: {hcolor('default_spotlight')};
|
||||
color: {hcolor('bracket')};
|
||||
|
||||
border-radius: 2px;
|
||||
|
||||
margin: {self.slot_margin_px}px;
|
||||
height: {slot_height_px}px;
|
||||
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# to set a discrete "block" per slot...
|
||||
# XXX: couldn't get the discrete math to work here such
|
||||
# that it was always correctly showing a discretized value
|
||||
# up to the limit; not sure if it's the ``.setRange()``
|
||||
# / ``.setValue()`` api or not but i was able to get something
|
||||
# close screwing with the divmod above above but after so large
|
||||
# a value it would always be less chunks then the correct
|
||||
# value..
|
||||
# margin: {self.slot_margin_px}px;
|
||||
# height: {slot_height_px}px;
|
||||
|
||||
|
||||
# margin-bottom: {slot_margin_px*2}px;
|
||||
# margin-top: {slot_margin_px*2}px;
|
||||
# color: #19232D;
|
||||
|
|
|
@ -71,10 +71,10 @@ async def update_pnl_from_feed(
|
|||
log.info(f'Starting pnl display for {pp.alloc.account}')
|
||||
|
||||
if live.size < 0:
|
||||
types = ('ask', 'last', 'last', 'utrade')
|
||||
types = ('ask', 'last', 'last', 'dark_trade')
|
||||
|
||||
elif live.size > 0:
|
||||
types = ('bid', 'last', 'last', 'utrade')
|
||||
types = ('bid', 'last', 'last', 'dark_trade')
|
||||
|
||||
else:
|
||||
log.info(f'No position (yet) for {tracker.alloc.account}@{key}')
|
||||
|
@ -119,7 +119,8 @@ async def update_pnl_from_feed(
|
|||
|
||||
@dataclass
|
||||
class SettingsPane:
|
||||
'''Composite set of widgets plus an allocator model for configuring
|
||||
'''
|
||||
Composite set of widgets plus an allocator model for configuring
|
||||
order entry sizes and position limits per tradable instrument.
|
||||
|
||||
'''
|
||||
|
@ -151,7 +152,8 @@ class SettingsPane:
|
|||
key: str,
|
||||
|
||||
) -> None:
|
||||
'''Called on any order pane drop down selection change.
|
||||
'''
|
||||
Called on any order pane drop down selection change.
|
||||
|
||||
'''
|
||||
log.info(f'selection input {key}:{text}')
|
||||
|
@ -164,7 +166,8 @@ class SettingsPane:
|
|||
value: str,
|
||||
|
||||
) -> bool:
|
||||
'''Called on any order pane edit field value change.
|
||||
'''
|
||||
Called on any order pane edit field value change.
|
||||
|
||||
'''
|
||||
mode = self.order_mode
|
||||
|
@ -219,16 +222,36 @@ class SettingsPane:
|
|||
else:
|
||||
value = puterize(value)
|
||||
if key == 'limit':
|
||||
pp = mode.current_pp.live_pp
|
||||
|
||||
if size_unit == 'currency':
|
||||
dsize = pp.dsize
|
||||
if dsize > value:
|
||||
log.error(
|
||||
f'limit must > then current pp: {dsize}'
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
alloc.currency_limit = value
|
||||
|
||||
else:
|
||||
size = pp.size
|
||||
if size > value:
|
||||
log.error(
|
||||
f'limit must > then current pp: {size}'
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
alloc.units_limit = value
|
||||
|
||||
elif key == 'slots':
|
||||
if value <= 0:
|
||||
raise ValueError('slots must be > 0')
|
||||
alloc.slots = int(value)
|
||||
|
||||
else:
|
||||
raise ValueError(f'Unknown setting {key}')
|
||||
log.error(f'Unknown setting {key}')
|
||||
raise ValueError
|
||||
|
||||
log.info(f'settings change: {key}: {value}')
|
||||
|
||||
|
@ -363,7 +386,8 @@ def position_line(
|
|||
marker: Optional[LevelMarker] = None,
|
||||
|
||||
) -> LevelLine:
|
||||
'''Convenience routine to create a line graphic representing a "pp"
|
||||
'''
|
||||
Convenience routine to create a line graphic representing a "pp"
|
||||
aka the acro for a,
|
||||
"{piker, private, personal, puny, <place your p-word here>} position".
|
||||
|
||||
|
@ -417,7 +441,8 @@ def position_line(
|
|||
|
||||
|
||||
class PositionTracker:
|
||||
'''Track and display real-time positions for a single symbol
|
||||
'''
|
||||
Track and display real-time positions for a single symbol
|
||||
over multiple accounts on a single chart.
|
||||
|
||||
Graphically composed of a level line and marker as well as labels
|
||||
|
@ -497,7 +522,8 @@ class PositionTracker:
|
|||
|
||||
@property
|
||||
def pane(self) -> FieldsForm:
|
||||
'''Return handle to pp side pane form.
|
||||
'''
|
||||
Return handle to pp side pane form.
|
||||
|
||||
'''
|
||||
return self.chart.linked.godwidget.pp_pane
|
||||
|
@ -507,7 +533,8 @@ class PositionTracker:
|
|||
marker: LevelMarker
|
||||
|
||||
) -> None:
|
||||
'''Update all labels.
|
||||
'''
|
||||
Update all labels.
|
||||
|
||||
Meant to be called from the maker ``.paint()``
|
||||
for immediate, lag free label draws.
|
||||
|
|
|
@ -107,7 +107,8 @@ def on_level_change_update_next_order_info(
|
|||
|
||||
@dataclass
|
||||
class OrderMode:
|
||||
'''Major UX mode for placing orders on a chart view providing so
|
||||
'''
|
||||
Major UX mode for placing orders on a chart view providing so
|
||||
called, "chart trading".
|
||||
|
||||
This is the other "main" mode that pairs with "view mode" (when
|
||||
|
|
Loading…
Reference in New Issue