diff --git a/piker/fsp/_engine.py b/piker/fsp/_engine.py index 2b7056da..bc55e154 100644 --- a/piker/fsp/_engine.py +++ b/piker/fsp/_engine.py @@ -34,7 +34,7 @@ from ..data import attach_shm_array from ..data.feed import Feed from ..data._sharedmem import ShmArray from ._momo import _rsi, _wma -from ._volume import _tina_vwap +from ._volume import _tina_vwap, dolla_vlm log = get_logger(__name__) @@ -42,6 +42,7 @@ _fsp_builtins = { 'rsi': _rsi, 'wma': _wma, 'vwap': _tina_vwap, + 'dolla_vlm': dolla_vlm, } # TODO: things to figure the heck out: diff --git a/piker/fsp/_volume.py b/piker/fsp/_volume.py index 30397920..e662343f 100644 --- a/piker/fsp/_volume.py +++ b/piker/fsp/_volume.py @@ -14,16 +14,20 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from typing import AsyncIterator, Optional +from typing import AsyncIterator, Optional, Union import numpy as np +from tractor.trionics._broadcast import AsyncReceiver from ..data._normalize import iterticks +from ..data._sharedmem import ShmArray def wap( + signal: np.ndarray, weights: np.ndarray, + ) -> np.ndarray: """Weighted average price from signal and weights. @@ -47,15 +51,22 @@ def wap( async def _tina_vwap( - source, #: AsyncStream[np.ndarray], - ohlcv: np.ndarray, # price time-frame "aware" + + source: AsyncReceiver[dict], + ohlcv: ShmArray, # OHLC sampled history + + # TODO: anchor logic (eg. to session start) anchors: Optional[np.ndarray] = None, -) -> AsyncIterator[np.ndarray]: # maybe something like like FspStream? - """Streaming volume weighted moving average. + +) -> Union[ + AsyncIterator[np.ndarray], + float +]: + '''Streaming volume weighted moving average. Calling this "tina" for now since we're using HLC3 instead of tick. - """ + ''' if anchors is None: # TODO: # anchor to session start of data if possible @@ -75,7 +86,6 @@ async def _tina_vwap( # vwap_tot = h_vwap[-1] async for quote in source: - for tick in iterticks(quote, types=['trade']): # c, h, l, v = ohlcv.array[-1][ @@ -91,3 +101,58 @@ async def _tina_vwap( # yield ((((o + h + l) / 3) * v) weights_tot) / v_tot yield w_tot / v_tot + + +# @fsp.config( +# name='dolla_vlm', +# ohlc=False, +# style='step', +# ) +async def dolla_vlm( + source: AsyncReceiver[dict], + ohlcv: ShmArray, # OHLC sampled history + +) -> Union[ + AsyncIterator[np.ndarray], + float +]: + ''' + "Dollar Volume", aka the volume in asset-currency-units (usually + a fiat) computed from some price function for the sample step + *times* the asset unit volume. + + Useful for comparing cross asset "money flow" in #s that are + asset-currency-independent. + + ''' + a = ohlcv.array + chl3 = (a['close'] + a['high'] + a['low']) / 3 + v = a['volume'] + + # history + yield chl3 * v + + i = ohlcv.index + lvlm = 0 + + async for quote in source: + for tick in iterticks(quote): + + # this computes tick-by-tick weightings from here forward + size = tick['size'] + price = tick['price'] + + li = ohlcv.index + if li > i: + i = li + lvlm = 0 + + c, h, l, v = ohlcv.last()[ + ['close', 'high', 'low', 'volume'] + ][0] + + lvlm += price * size + tina_lvlm = c+h+l/3 * v + # print(f' tinal vlm: {tina_lvlm}') + + yield lvlm diff --git a/piker/ui/_display.py b/piker/ui/_display.py index dcc0badd..43085b7d 100644 --- a/piker/ui/_display.py +++ b/piker/ui/_display.py @@ -932,7 +932,7 @@ async def maybe_open_vlm_display( async with open_fsp_sidepane( linked, { - '$_vlm': { + 'vlm': { 'params': { @@ -1102,6 +1102,20 @@ async def display_symbol_data( # TODO: eventually we'll support some kind of n-compose syntax fsp_conf = { + 'dolla_vlm': { + 'func_name': 'dolla_vlm', + 'zero_on_step': True, + 'params': { + 'price_func': { + 'default_value': 'chl3', + # tell target ``Edit`` widget to not allow + # edits for now. + 'widget_kwargs': {'readonly': True}, + }, + }, + 'chart_kwargs': {'style': 'step'} + }, + 'rsi': { 'func_name': 'rsi', # literal python func ref lookup name