Break wma calc into sync func
parent
3f0e175011
commit
41e85ccaa9
|
@ -112,7 +112,7 @@ async def cascade(
|
||||||
n.start_soon(increment_signals, feed, dst)
|
n.start_soon(increment_signals, feed, dst)
|
||||||
|
|
||||||
async for processed in out_stream:
|
async for processed in out_stream:
|
||||||
log.info(f"{fsp_func_name}: {processed}")
|
log.debug(f"{fsp_func_name}: {processed}")
|
||||||
index = src.index
|
index = src.index
|
||||||
dst.array[-1][fsp_func_name] = processed
|
dst.array[-1][fsp_func_name] = processed
|
||||||
await ctx.send_yield(index)
|
await ctx.send_yield(index)
|
||||||
|
|
|
@ -122,6 +122,22 @@ def rsi(
|
||||||
return rsi, up_ema[-1], down_ema[-1]
|
return rsi, up_ema[-1], down_ema[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def wma(
|
||||||
|
signal: np.ndarray,
|
||||||
|
length: int,
|
||||||
|
weights: Optional[np.ndarray] = None,
|
||||||
|
) -> np.ndarray:
|
||||||
|
if weights is None:
|
||||||
|
# default is a standard arithmetic mean
|
||||||
|
seq = np.full((length,), 1)
|
||||||
|
weights = seq / seq.sum()
|
||||||
|
|
||||||
|
assert length == len(weights)
|
||||||
|
|
||||||
|
# return np.convolve(ohlcv.array['close'], weights, 'valid')
|
||||||
|
return np.convolve(signal, weights, 'valid')
|
||||||
|
|
||||||
|
|
||||||
# @piker.fsp(
|
# @piker.fsp(
|
||||||
# aggregates=['30s', '1m', '5m', '1H', '4H', '1D'],
|
# aggregates=['30s', '1m', '5m', '1H', '4H', '1D'],
|
||||||
# )
|
# )
|
||||||
|
@ -136,9 +152,14 @@ async def _rsi(
|
||||||
"""
|
"""
|
||||||
sig = ohlcv.array['close']
|
sig = ohlcv.array['close']
|
||||||
|
|
||||||
|
# wilder says to seed the RSI EMAs with the SMA for the "period"
|
||||||
|
seed = wma(ohlcv.last(period)['close'], period)[0]
|
||||||
|
|
||||||
# TODO: the emas here should be seeded with a period SMA as per
|
# TODO: the emas here should be seeded with a period SMA as per
|
||||||
# wilder's original formula..
|
# wilder's original formula..
|
||||||
rsi_h, last_up_ema_close, last_down_ema_close = rsi(sig, period, None, None)
|
rsi_h, last_up_ema_close, last_down_ema_close = rsi(sig, period, seed, seed)
|
||||||
|
up_ema_last = last_up_ema_close
|
||||||
|
down_ema_last = last_down_ema_close
|
||||||
|
|
||||||
# deliver history
|
# deliver history
|
||||||
yield rsi_h
|
yield rsi_h
|
||||||
|
@ -150,9 +171,13 @@ async def _rsi(
|
||||||
for tick in iterticks(quote):
|
for tick in iterticks(quote):
|
||||||
# though incorrect below is interesting
|
# though incorrect below is interesting
|
||||||
# sig = ohlcv.last(period)['close']
|
# sig = ohlcv.last(period)['close']
|
||||||
|
|
||||||
|
# get only the last 2 "datums" which will be diffed to
|
||||||
|
# calculate the real-time RSI output datum
|
||||||
sig = ohlcv.last(2)['close']
|
sig = ohlcv.last(2)['close']
|
||||||
|
|
||||||
# the ema needs to be computed from the "last bar"
|
# the ema needs to be computed from the "last bar"
|
||||||
|
# TODO: how to make this cleaner
|
||||||
if ohlcv.index > index:
|
if ohlcv.index > index:
|
||||||
last_up_ema_close = up_ema_last
|
last_up_ema_close = up_ema_last
|
||||||
last_down_ema_close = down_ema_last
|
last_down_ema_close = down_ema_last
|
||||||
|
@ -164,25 +189,13 @@ async def _rsi(
|
||||||
up_ema_last=last_up_ema_close,
|
up_ema_last=last_up_ema_close,
|
||||||
down_ema_last=last_down_ema_close,
|
down_ema_last=last_down_ema_close,
|
||||||
)
|
)
|
||||||
print(f'rsi_out: {rsi_out}')
|
|
||||||
yield rsi_out[-1:]
|
yield rsi_out[-1:]
|
||||||
|
|
||||||
def wma(
|
|
||||||
signal: np.ndarray,
|
|
||||||
) -> np.ndarray:
|
|
||||||
if weights is None:
|
|
||||||
# default is a standard arithmetic mean
|
|
||||||
seq = np.full((length,), 1)
|
|
||||||
weights = seq / seq.sum()
|
|
||||||
|
|
||||||
assert length == len(weights)
|
async def _wma(
|
||||||
|
|
||||||
|
|
||||||
async def wma(
|
|
||||||
source, #: AsyncStream[np.ndarray],
|
source, #: AsyncStream[np.ndarray],
|
||||||
length: int,
|
length: int,
|
||||||
ohlcv: np.ndarray, # price time-frame "aware"
|
ohlcv: np.ndarray, # price time-frame "aware"
|
||||||
weights: Optional[np.ndarray] = None,
|
|
||||||
) -> AsyncIterator[np.ndarray]: # maybe something like like FspStream?
|
) -> AsyncIterator[np.ndarray]: # maybe something like like FspStream?
|
||||||
"""Streaming weighted moving average.
|
"""Streaming weighted moving average.
|
||||||
|
|
||||||
|
@ -191,32 +204,10 @@ async def wma(
|
||||||
``weights = np.arange(1, N) * N*(N-1)/2``.
|
``weights = np.arange(1, N) * N*(N-1)/2``.
|
||||||
"""
|
"""
|
||||||
# deliver historical output as "first yield"
|
# deliver historical output as "first yield"
|
||||||
yield np.convolve(ohlcv.array['close'], weights, 'valid')
|
yield wma(ohlcv.array['close'], length)
|
||||||
|
|
||||||
# begin real-time section
|
# begin real-time section
|
||||||
|
|
||||||
# fill length samples as lookback history
|
|
||||||
# ringbuf = RingBuffer(format='f', capacity=2*length)
|
|
||||||
# overflow = ringbuf.push(ohlcv['close'][-length + 1:])
|
|
||||||
# assert overflow is None
|
|
||||||
|
|
||||||
# lookback = np.zeros((length,))
|
|
||||||
# lookback[:-1] = ohlcv['close'][-length + 1:]
|
|
||||||
|
|
||||||
# async for frame in atleast(length, source):
|
|
||||||
async for quote in source:
|
async for quote in source:
|
||||||
for tick in iterticks(quote, type='trade'):
|
for tick in iterticks(quote, type='trade'):
|
||||||
# writes no matter what
|
yield wma(ohlcv.last(length))
|
||||||
overflow = ringbuf.push(np.array([tick['price']]))
|
|
||||||
assert overflow is None
|
|
||||||
|
|
||||||
# history = np.concatenate(ringbuf.pop(length - 1), frame)
|
|
||||||
|
|
||||||
sig = ohlcv.last(length)
|
|
||||||
history = ringbuf.pop(ringbuf.read_available)
|
|
||||||
yield np.convolve(history, weights, 'valid')
|
|
||||||
|
|
||||||
# push back `length-1` datums as lookback in preparation
|
|
||||||
# for next minimum 1 datum arrival which will require
|
|
||||||
# another "window's worth" of history.
|
|
||||||
ringbuf.push(history[-length + 1:])
|
|
||||||
|
|
Loading…
Reference in New Issue