From a3b2ba9ae9f14b309f84ba08b2c4081f44ee593b Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 15 May 2022 13:38:22 -0400 Subject: [PATCH 1/5] Use `numpy.datetime64` for x-axis tick strings --- piker/ui/_axes.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/piker/ui/_axes.py b/piker/ui/_axes.py index 2363cc84..93ac7af7 100644 --- a/piker/ui/_axes.py +++ b/piker/ui/_axes.py @@ -19,10 +19,10 @@ Chart axes graphics and behavior. """ from functools import lru_cache -from typing import List, Tuple, Optional, Callable +from typing import Optional, Callable from math import floor -import pandas as pd +import numpy as np import pyqtgraph as pg from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QPointF @@ -103,7 +103,7 @@ class Axis(pg.AxisItem): def size_to_values(self) -> None: pass - def txt_offsets(self) -> Tuple[int, int]: + def txt_offsets(self) -> tuple[int, int]: return tuple(self.style['tickTextOffset']) @@ -218,9 +218,9 @@ class DynamicDateAxis(Axis): def _indexes_to_timestrs( self, - indexes: List[int], + indexes: list[int], - ) -> List[str]: + ) -> list[str]: chart = self.linkedsplits.chart bars = chart._arrays[chart.name] @@ -241,10 +241,17 @@ class DynamicDateAxis(Axis): )] # TODO: **don't** have this hard coded shift to EST - dts = pd.to_datetime(epochs, unit='s') # - 4*pd.offsets.Hour() + # delay = times[-1] - times[-2] + dts = np.array(epochs, dtype='datetime64[s]') - delay = times[-1] - times[-2] - return dts.strftime(self.tick_tpl[delay]) + # see units listing: + # https://numpy.org/devdocs/reference/arrays.datetime.html#datetime-units + return list(np.datetime_as_string(dts)) + + # TODO: per timeframe formatting? + # - we probably need this based on zoom now right? + # prec = self.np_dt_precision[delay] + # return dts.strftime(self.tick_tpl[delay]) def tickStrings( self, @@ -430,7 +437,7 @@ class XAxisLabel(AxisLabel): | QtCore.Qt.AlignCenter ) - def size_hint(self) -> Tuple[float, float]: + def size_hint(self) -> tuple[float, float]: # size to parent axis height return self._parent.height(), None @@ -444,11 +451,11 @@ class XAxisLabel(AxisLabel): timestrs = self._parent._indexes_to_timestrs([int(value)]) - if not timestrs.any(): + if not len(timestrs): return pad = 1*' ' - self.label_str = pad + timestrs[0] + pad + self.label_str = pad + str(timestrs[0]) + pad _, y_offset = self._parent.txt_offsets() @@ -509,7 +516,7 @@ class YAxisLabel(AxisLabel): if getattr(self._parent, 'txt_offsets', False): self.x_offset, y_offset = self._parent.txt_offsets() - def size_hint(self) -> Tuple[float, float]: + def size_hint(self) -> tuple[float, float]: # size to parent axis width(-ish) wsh = self._dpifont.boundingRect(' ').height() / 2 return ( From 6e2e2fc03f472988455df7d391157cd5f616d321 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 15 May 2022 13:39:30 -0400 Subject: [PATCH 2/5] Use `pendulum` for timestamp parsing --- piker/data/marketstore.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/piker/data/marketstore.py b/piker/data/marketstore.py index 39fe1b70..e1fb38d5 100644 --- a/piker/data/marketstore.py +++ b/piker/data/marketstore.py @@ -40,7 +40,6 @@ from bidict import bidict import msgpack import pyqtgraph as pg import numpy as np -import pandas as pd import tractor from trio_websocket import open_websocket_url from anyio_marketstore import ( @@ -268,7 +267,7 @@ def quote_to_marketstore_structarray( ''' if last_fill: # new fill bby - now = timestamp(last_fill) + now = int(pendulum.parse(last_fill).timestamp) else: # this should get inserted upstream by the broker-client to # subtract from IPC latency @@ -298,15 +297,6 @@ def quote_to_marketstore_structarray( return np.array([tuple(array_input)], dtype=_quote_dt) -def timestamp(date, **kwargs) -> int: - ''' - Return marketstore compatible 'Epoch' integer in nanoseconds - from a date formatted str. - - ''' - return int(pd.Timestamp(date, **kwargs).value) - - @acm async def get_client( host: str = 'localhost', From fb5df5ab5e9baf3af5f74bb826a09810528f4679 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 15 May 2022 13:44:13 -0400 Subject: [PATCH 3/5] Drop `pandas` usage throughout brokers cli --- piker/brokers/cli.py | 88 ++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/piker/brokers/cli.py b/piker/brokers/cli.py index 164a060b..b614a4fd 100644 --- a/piker/brokers/cli.py +++ b/piker/brokers/cli.py @@ -23,7 +23,6 @@ from operator import attrgetter from operator import itemgetter import click -import pandas as pd import trio import tractor @@ -47,8 +46,10 @@ _watchlists_data_path = os.path.join(_config_dir, 'watchlists.json') @click.argument('kwargs', nargs=-1) @click.pass_obj def api(config, meth, kwargs, keys): - """Make a broker-client API method call - """ + ''' + Make a broker-client API method call + + ''' # global opts broker = config['brokers'][0] @@ -79,13 +80,13 @@ def api(config, meth, kwargs, keys): @cli.command() -@click.option('--df-output', '-df', flag_value=True, - help='Output in `pandas.DataFrame` format') @click.argument('tickers', nargs=-1, required=True) @click.pass_obj -def quote(config, tickers, df_output): - """Print symbol quotes to the console - """ +def quote(config, tickers): + ''' + Print symbol quotes to the console + + ''' # global opts brokermod = config['brokermods'][0] @@ -100,28 +101,19 @@ def quote(config, tickers, df_output): if ticker not in syms: brokermod.log.warn(f"Could not find symbol {ticker}?") - if df_output: - cols = next(filter(bool, quotes)).copy() - cols.pop('symbol') - df = pd.DataFrame( - (quote or {} for quote in quotes), - columns=cols, - ) - click.echo(df) - else: - click.echo(colorize_json(quotes)) + click.echo(colorize_json(quotes)) @cli.command() -@click.option('--df-output', '-df', flag_value=True, - help='Output in `pandas.DataFrame` format') @click.option('--count', '-c', default=1000, help='Number of bars to retrieve') @click.argument('symbol', required=True) @click.pass_obj -def bars(config, symbol, count, df_output): - """Retreive 1m bars for symbol and print on the console - """ +def bars(config, symbol, count): + ''' + Retreive 1m bars for symbol and print on the console + + ''' # global opts brokermod = config['brokermods'][0] @@ -133,7 +125,7 @@ def bars(config, symbol, count, df_output): brokermod, symbol, count=count, - as_np=df_output + as_np=False, ) ) @@ -141,10 +133,7 @@ def bars(config, symbol, count, df_output): log.error(f"No quotes could be found for {symbol}?") return - if df_output: - click.echo(pd.DataFrame(bars)) - else: - click.echo(colorize_json(bars)) + click.echo(colorize_json(bars)) @cli.command() @@ -156,8 +145,10 @@ def bars(config, symbol, count, df_output): @click.argument('name', nargs=1, required=True) @click.pass_obj def record(config, rate, name, dhost, filename): - """Record client side quotes to a file on disk - """ + ''' + Record client side quotes to a file on disk + + ''' # global opts brokermod = config['brokermods'][0] loglevel = config['loglevel'] @@ -195,8 +186,10 @@ def record(config, rate, name, dhost, filename): @click.argument('symbol', required=True) @click.pass_context def contracts(ctx, loglevel, broker, symbol, ids): - """Get list of all option contracts for symbol - """ + ''' + Get list of all option contracts for symbol + + ''' brokermod = get_brokermod(broker) get_console_log(loglevel) @@ -213,14 +206,14 @@ def contracts(ctx, loglevel, broker, symbol, ids): @cli.command() -@click.option('--df-output', '-df', flag_value=True, - help='Output in `pandas.DataFrame` format') @click.option('--date', '-d', help='Contracts expiry date') @click.argument('symbol', required=True) @click.pass_obj -def optsquote(config, symbol, df_output, date): - """Retreive symbol option quotes on the console - """ +def optsquote(config, symbol, date): + ''' + Retreive symbol option quotes on the console + + ''' # global opts brokermod = config['brokermods'][0] @@ -233,22 +226,17 @@ def optsquote(config, symbol, df_output, date): log.error(f"No option quotes could be found for {symbol}?") return - if df_output: - df = pd.DataFrame( - (quote.values() for quote in quotes), - columns=quotes[0].keys(), - ) - click.echo(df) - else: - click.echo(colorize_json(quotes)) + click.echo(colorize_json(quotes)) @cli.command() @click.argument('tickers', nargs=-1, required=True) @click.pass_obj def symbol_info(config, tickers): - """Print symbol quotes to the console - """ + ''' + Print symbol quotes to the console + + ''' # global opts brokermod = config['brokermods'][0] @@ -270,8 +258,10 @@ def symbol_info(config, tickers): @click.argument('pattern', required=True) @click.pass_obj def search(config, pattern): - """Search for symbols from broker backend(s). - """ + ''' + Search for symbols from broker backend(s). + + ''' # global opts brokermods = config['brokermods'] From e718120cc7e9ff26cf0669e51754b6df11b40f91 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 15 May 2022 13:47:48 -0400 Subject: [PATCH 4/5] Drop `pandas` as dep --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 17456003..6e58ec6e 100755 --- a/setup.py +++ b/setup.py @@ -68,7 +68,6 @@ setup( 'cython', 'numpy', 'numba', - 'pandas', # UI 'PyQt5', From 09f2f32d5b269efe4eecceff353d2f7c39921d70 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 15 May 2022 13:49:42 -0400 Subject: [PATCH 5/5] Drop `pandas` timestamp for qt --- piker/brokers/questrade.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/piker/brokers/questrade.py b/piker/brokers/questrade.py index bd364615..a3b5cfe0 100644 --- a/piker/brokers/questrade.py +++ b/piker/brokers/questrade.py @@ -35,7 +35,6 @@ import pendulum import trio import tractor from async_generator import asynccontextmanager -import pandas as pd import numpy as np import wrapt import asks @@ -669,7 +668,7 @@ def get_OHLCV( """ del bar['end'] del bar['VWAP'] - bar['start'] = pd.Timestamp(bar['start']).value/10**9 + bar['start'] = pendulum.from_timestamp(bar['start']) / 10**9 return tuple(bar.values())