Breakout fsp rt loop as non-closure for readability
							parent
							
								
									f6f4a0cd8d
								
							
						
					
					
						commit
						efd93d058a
					
				| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
Financial signal processing for the peeps.
 | 
					Financial signal processing for the peeps.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					from functools import partial
 | 
				
			||||||
from typing import AsyncIterator, Callable, Tuple
 | 
					from typing import AsyncIterator, Callable, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import trio
 | 
					import trio
 | 
				
			||||||
| 
						 | 
					@ -29,6 +30,8 @@ from .. import data
 | 
				
			||||||
from ._momo import _rsi, _wma
 | 
					from ._momo import _rsi, _wma
 | 
				
			||||||
from ._volume import _tina_vwap
 | 
					from ._volume import _tina_vwap
 | 
				
			||||||
from ..data import attach_shm_array
 | 
					from ..data import attach_shm_array
 | 
				
			||||||
 | 
					from ..data.feed import Feed
 | 
				
			||||||
 | 
					from ..data._sharedmem import ShmArray
 | 
				
			||||||
 | 
					
 | 
				
			||||||
log = get_logger(__name__)
 | 
					log = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,6 +65,97 @@ async def latency(
 | 
				
			||||||
            yield value
 | 
					            yield value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def fsp_compute(
 | 
				
			||||||
 | 
					    ctx: tractor.Context,
 | 
				
			||||||
 | 
					    symbol: str,
 | 
				
			||||||
 | 
					    feed: Feed,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    src: ShmArray,
 | 
				
			||||||
 | 
					    dst: ShmArray,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fsp_func_name: str,
 | 
				
			||||||
 | 
					    func: Callable,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    task_status: TaskStatus[None] = trio.TASK_STATUS_IGNORED,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO: load appropriate fsp with input args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def filter_by_sym(
 | 
				
			||||||
 | 
					        sym: str,
 | 
				
			||||||
 | 
					        stream,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: make this the actualy first quote from feed
 | 
				
			||||||
 | 
					        # XXX: this allows for a single iteration to run for history
 | 
				
			||||||
 | 
					        # processing without waiting on the real-time feed for a new quote
 | 
				
			||||||
 | 
					        yield {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # task cancellation won't kill the channel
 | 
				
			||||||
 | 
					        with stream.shield():
 | 
				
			||||||
 | 
					            async for quotes in stream:
 | 
				
			||||||
 | 
					                for symbol, quotes in quotes.items():
 | 
				
			||||||
 | 
					                    if symbol == sym:
 | 
				
			||||||
 | 
					                        yield quotes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out_stream = func(
 | 
				
			||||||
 | 
					        filter_by_sym(symbol, feed.stream),
 | 
				
			||||||
 | 
					        feed.shm,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO: XXX:
 | 
				
			||||||
 | 
					    # THERE'S A BIG BUG HERE WITH THE `index` field since we're
 | 
				
			||||||
 | 
					    # prepending a copy of the first value a few times to make
 | 
				
			||||||
 | 
					    # sub-curves align with the parent bar chart.
 | 
				
			||||||
 | 
					    # This likely needs to be fixed either by,
 | 
				
			||||||
 | 
					    # - manually assigning the index and historical data
 | 
				
			||||||
 | 
					    #   seperately to the shm array (i.e. not using .push())
 | 
				
			||||||
 | 
					    # - developing some system on top of the shared mem array that
 | 
				
			||||||
 | 
					    #   is `index` aware such that historical data can be indexed
 | 
				
			||||||
 | 
					    #   relative to the true first datum? Not sure if this is sane
 | 
				
			||||||
 | 
					    #   for incremental compuations.
 | 
				
			||||||
 | 
					    dst._first.value = src._first.value
 | 
				
			||||||
 | 
					    dst._last.value = src._first.value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Conduct a single iteration of fsp with historical bars input
 | 
				
			||||||
 | 
					    # and get historical output
 | 
				
			||||||
 | 
					    history_output = await out_stream.__anext__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # build a struct array which includes an 'index' field to push
 | 
				
			||||||
 | 
					    # as history
 | 
				
			||||||
 | 
					    history = np.array(
 | 
				
			||||||
 | 
					        np.arange(len(history_output)),
 | 
				
			||||||
 | 
					        dtype=dst.array.dtype
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    history[fsp_func_name] = history_output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # check for data length mis-allignment and fill missing values
 | 
				
			||||||
 | 
					    diff = len(src.array) - len(history)
 | 
				
			||||||
 | 
					    if diff >= 0:
 | 
				
			||||||
 | 
					        print(f"WTF DIFF SIGNAL to HISTORY {diff}")
 | 
				
			||||||
 | 
					        for _ in range(diff):
 | 
				
			||||||
 | 
					            dst.push(history[:1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # compare with source signal and time align
 | 
				
			||||||
 | 
					    index = dst.push(history)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await ctx.send_yield(index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # setup a respawn handle
 | 
				
			||||||
 | 
					    with trio.CancelScope() as cs:
 | 
				
			||||||
 | 
					        task_status.started(cs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # rt stream
 | 
				
			||||||
 | 
					        async for processed in out_stream:
 | 
				
			||||||
 | 
					            log.debug(f"{fsp_func_name}: {processed}")
 | 
				
			||||||
 | 
					            index = src.index
 | 
				
			||||||
 | 
					            dst.array[-1][fsp_func_name] = processed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # stream latest shm array index entry
 | 
				
			||||||
 | 
					            await ctx.send_yield(index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@tractor.stream
 | 
					@tractor.stream
 | 
				
			||||||
async def cascade(
 | 
					async def cascade(
 | 
				
			||||||
    ctx: tractor.Context,
 | 
					    ctx: tractor.Context,
 | 
				
			||||||
| 
						 | 
					@ -85,84 +179,24 @@ async def cascade(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert src.token == feed.shm.token
 | 
					        assert src.token == feed.shm.token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async def fsp_compute(
 | 
					 | 
				
			||||||
            task_status: TaskStatus[None] = trio.TASK_STATUS_IGNORED,
 | 
					 | 
				
			||||||
        ) -> None:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # TODO: load appropriate fsp with input args
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            async def filter_by_sym(
 | 
					 | 
				
			||||||
                sym: str,
 | 
					 | 
				
			||||||
                stream,
 | 
					 | 
				
			||||||
            ):
 | 
					 | 
				
			||||||
                # task cancellation won't kill the channel
 | 
					 | 
				
			||||||
                with stream.shield():
 | 
					 | 
				
			||||||
                    async for quotes in stream:
 | 
					 | 
				
			||||||
                        for symbol, quotes in quotes.items():
 | 
					 | 
				
			||||||
                            if symbol == sym:
 | 
					 | 
				
			||||||
                                yield quotes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            out_stream = func(
 | 
					 | 
				
			||||||
                filter_by_sym(symbol, feed.stream),
 | 
					 | 
				
			||||||
                feed.shm,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # TODO: XXX:
 | 
					 | 
				
			||||||
            # THERE'S A BIG BUG HERE WITH THE `index` field since we're
 | 
					 | 
				
			||||||
            # prepending a copy of the first value a few times to make
 | 
					 | 
				
			||||||
            # sub-curves align with the parent bar chart.
 | 
					 | 
				
			||||||
            # This likely needs to be fixed either by,
 | 
					 | 
				
			||||||
            # - manually assigning the index and historical data
 | 
					 | 
				
			||||||
            #   seperately to the shm array (i.e. not using .push())
 | 
					 | 
				
			||||||
            # - developing some system on top of the shared mem array that
 | 
					 | 
				
			||||||
            #   is `index` aware such that historical data can be indexed
 | 
					 | 
				
			||||||
            #   relative to the true first datum? Not sure if this is sane
 | 
					 | 
				
			||||||
            #   for incremental compuations.
 | 
					 | 
				
			||||||
            dst._first.value = src._first.value
 | 
					 | 
				
			||||||
            dst._last.value = src._first.value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Conduct a single iteration of fsp with historical bars input
 | 
					 | 
				
			||||||
            # and get historical output
 | 
					 | 
				
			||||||
            history_output = await out_stream.__anext__()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # build a struct array which includes an 'index' field to push
 | 
					 | 
				
			||||||
            # as history
 | 
					 | 
				
			||||||
            history = np.array(
 | 
					 | 
				
			||||||
                np.arange(len(history_output)),
 | 
					 | 
				
			||||||
                dtype=dst.array.dtype
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            history[fsp_func_name] = history_output
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # check for data length mis-allignment and fill missing values
 | 
					 | 
				
			||||||
            diff = len(src.array) - len(history)
 | 
					 | 
				
			||||||
            if diff >= 0:
 | 
					 | 
				
			||||||
                print(f"WTF DIFF SIGNAL to HISTORY {diff}")
 | 
					 | 
				
			||||||
                for _ in range(diff):
 | 
					 | 
				
			||||||
                    dst.push(history[:1])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # compare with source signal and time align
 | 
					 | 
				
			||||||
            index = dst.push(history)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await ctx.send_yield(index)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # setup a respawn handle
 | 
					 | 
				
			||||||
            with trio.CancelScope() as cs:
 | 
					 | 
				
			||||||
                task_status.started(cs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # rt stream
 | 
					 | 
				
			||||||
                async for processed in out_stream:
 | 
					 | 
				
			||||||
                    log.debug(f"{fsp_func_name}: {processed}")
 | 
					 | 
				
			||||||
                    index = src.index
 | 
					 | 
				
			||||||
                    dst.array[-1][fsp_func_name] = processed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    # stream latest shm array index entry
 | 
					 | 
				
			||||||
                    await ctx.send_yield(index)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        last_len = new_len = len(src.array)
 | 
					        last_len = new_len = len(src.array)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fsp_target = partial(
 | 
				
			||||||
 | 
					            fsp_compute,
 | 
				
			||||||
 | 
					            ctx=ctx,
 | 
				
			||||||
 | 
					            symbol=symbol,
 | 
				
			||||||
 | 
					            feed=feed,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            src=src,
 | 
				
			||||||
 | 
					            dst=dst,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fsp_func_name=fsp_func_name,
 | 
				
			||||||
 | 
					            func=func
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async with trio.open_nursery() as n:
 | 
					        async with trio.open_nursery() as n:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cs = await n.start(fsp_compute)
 | 
					            cs = await n.start(fsp_target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Increment the underlying shared memory buffer on every
 | 
					            # Increment the underlying shared memory buffer on every
 | 
				
			||||||
            # "increment" msg received from the underlying data feed.
 | 
					            # "increment" msg received from the underlying data feed.
 | 
				
			||||||
| 
						 | 
					@ -176,7 +210,7 @@ async def cascade(
 | 
				
			||||||
                        # respawn the signal compute task if the source
 | 
					                        # respawn the signal compute task if the source
 | 
				
			||||||
                        # signal has been updated
 | 
					                        # signal has been updated
 | 
				
			||||||
                        cs.cancel()
 | 
					                        cs.cancel()
 | 
				
			||||||
                        cs = await n.start(fsp_compute)
 | 
					                        cs = await n.start(fsp_target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        # TODO: adopt an incremental update engine/approach
 | 
					                        # TODO: adopt an incremental update engine/approach
 | 
				
			||||||
                        # where possible here eventually!
 | 
					                        # where possible here eventually!
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue