Add draft `pydantic`-`QWidget` ORM system
Move all the ``pydantic`` finagling to an `_orm.py` and just keep an `Allocator` as the backing model for our pp controls in the position module. This all needs to be tied together in some sane with with facility for multiple symbols/streams per chart for when we get to charting-trading aggregate feeds.fsp_feeds
parent
2d1deb7ab7
commit
c982634839
|
@ -0,0 +1,129 @@
|
|||
# piker: trading gear for hackers
|
||||
# Copyright (C) Tyler Goodlet
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
micro-ORM for coupling ``pydantic`` models with Qt input/output widgets.
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import (
|
||||
Optional, Generic,
|
||||
TypeVar, Callable,
|
||||
Literal,
|
||||
)
|
||||
import enum
|
||||
import sys
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.generics import GenericModel
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget,
|
||||
QComboBox,
|
||||
)
|
||||
|
||||
from ._forms import (
|
||||
# FontScaledDelegate,
|
||||
FontAndChartAwareLineEdit,
|
||||
)
|
||||
|
||||
|
||||
DataType = TypeVar('DataType')
|
||||
|
||||
|
||||
class Field(GenericModel, Generic[DataType]):
|
||||
widget_factory: Optional[
|
||||
Callable[
|
||||
[QWidget, 'Field'],
|
||||
QWidget
|
||||
]
|
||||
]
|
||||
value: Optional[DataType] = None
|
||||
|
||||
|
||||
class Selection(Field[DataType], Generic[DataType]):
|
||||
'''Model which maps to a finite set of drop down entries declared as
|
||||
a ``dict[str, DataType]``.
|
||||
|
||||
'''
|
||||
widget_factory = QComboBox
|
||||
options: dict[str, DataType]
|
||||
# value: DataType = None
|
||||
|
||||
@validator('value') # , always=True)
|
||||
def set_value_first(
|
||||
cls,
|
||||
|
||||
v: DataType,
|
||||
values: dict[str, DataType],
|
||||
|
||||
) -> DataType:
|
||||
'''If no initial value is set, use the first in
|
||||
the ``options`` dict.
|
||||
|
||||
'''
|
||||
# breakpoint()
|
||||
options = values['options']
|
||||
if v is None:
|
||||
return next(options.values())
|
||||
else:
|
||||
assert v in options, f'{v} is not in {options}'
|
||||
return v
|
||||
|
||||
|
||||
# class SizeUnit(Enum):
|
||||
|
||||
# currency = '$ size'
|
||||
# percent_of_port = '% of port'
|
||||
# shares = '# shares'
|
||||
|
||||
|
||||
# class Weighter(str, Enum):
|
||||
# uniform = 'uniform'
|
||||
|
||||
|
||||
class Edit(Field[DataType], Generic[DataType]):
|
||||
'''An edit field which takes a number.
|
||||
'''
|
||||
widget_factory = FontAndChartAwareLineEdit
|
||||
|
||||
|
||||
class AllocatorPane(BaseModel):
|
||||
|
||||
account = Selection[str](
|
||||
options=dict.fromkeys(
|
||||
['paper', 'ib.paper', 'ib.margin'],
|
||||
'paper',
|
||||
),
|
||||
)
|
||||
|
||||
allocate = Selection[str](
|
||||
# options=list(Size),
|
||||
options={
|
||||
'$ size': 'currency',
|
||||
'% of port': 'percent_of_port',
|
||||
'# shares': 'shares',
|
||||
},
|
||||
# TODO: save/load from config and/or last session
|
||||
# value='currency'
|
||||
)
|
||||
weight = Selection[str](
|
||||
options={
|
||||
'uniform': 'uniform',
|
||||
},
|
||||
# value='uniform',
|
||||
)
|
||||
size = Edit[float](value=1000)
|
||||
slots = Edit[int](value=4)
|
|
@ -18,12 +18,17 @@
|
|||
Position info and display
|
||||
|
||||
"""
|
||||
from typing import Optional
|
||||
from __future__ import annotations
|
||||
import enum
|
||||
from functools import partial
|
||||
from math import floor
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
from bidict import bidict
|
||||
from pyqtgraph import functions as fn
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, validator
|
||||
# from pydantic.generics import GenericModel
|
||||
# from PyQt5.QtCore import QPointF
|
||||
# from PyQt5.QtGui import QGraphicsPathItem
|
||||
|
||||
|
@ -56,6 +61,70 @@ class Position(BaseModel):
|
|||
fills: list[Status] = []
|
||||
|
||||
|
||||
def mk_pp_alloc(
|
||||
|
||||
accounts: dict[str, Optional[str]] = {
|
||||
'paper': None,
|
||||
'ib.paper': 'DU1435481',
|
||||
'ib.margin': 'U10983%',
|
||||
},
|
||||
|
||||
) -> Allocator: # noqa
|
||||
|
||||
# lol we have to do this module patching bc ``pydantic``
|
||||
# needs types to exist at module level:
|
||||
# https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
|
||||
mod = sys.modules[__name__]
|
||||
|
||||
accounts = bidict(accounts)
|
||||
Account = mod.Account = enum.Enum('Account', accounts)
|
||||
|
||||
size_units = bidict({
|
||||
'$ size': 'currency',
|
||||
'% of port': 'percent_of_port',
|
||||
'# shares': 'shares',
|
||||
})
|
||||
SizeUnit = mod.SizeUnit = enum.Enum(
|
||||
'SizeUnit',
|
||||
size_units.inverse
|
||||
)
|
||||
|
||||
class Allocator(BaseModel):
|
||||
|
||||
account: Account = None
|
||||
_accounts: dict[str, Optional[str]] = accounts
|
||||
|
||||
size_unit: SizeUnit = 'currency'
|
||||
_size_units: dict[str, Optional[str]] = size_units
|
||||
|
||||
disti_weight: str = 'uniform'
|
||||
|
||||
size: float
|
||||
slots: int
|
||||
|
||||
_position: Position = None
|
||||
|
||||
def get_order_info(
|
||||
self,
|
||||
price: float,
|
||||
|
||||
) -> dict:
|
||||
units, r = divmod(
|
||||
round((self.size / self.slots)),
|
||||
price,
|
||||
)
|
||||
print(f'# shares: {units}, r: {r}')
|
||||
|
||||
# Allocator.update_forward_refs()
|
||||
|
||||
return Allocator(
|
||||
account=None,
|
||||
size_unit=size_units.inverse['currency'],
|
||||
size=2000,
|
||||
slots=4,
|
||||
)
|
||||
|
||||
|
||||
class PositionTracker:
|
||||
'''Track and display a real-time position for a single symbol
|
||||
on a chart.
|
||||
|
|
Loading…
Reference in New Issue