Add optional uppx log scaling to m4 sampler
We were previously ad-hoc scaling up the px count/width to get more detail at lower uppx values. Add a log scaling sigmoid that range scales between 1 < px_width < 16. Add in a flag to use the mxmn OH tracer in `ohlc_flatten()` if desired.mkts_backup
							parent
							
								
									7f350569df
								
							
						
					
					
						commit
						96182c37f1
					
				| 
						 | 
					@ -20,6 +20,7 @@ limits on the display device.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
'''
 | 
					'''
 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import numpy as np
 | 
					import numpy as np
 | 
				
			||||||
from numpy.lib import recfunctions as rfn
 | 
					from numpy.lib import recfunctions as rfn
 | 
				
			||||||
| 
						 | 
					@ -34,11 +35,7 @@ from ..log import get_logger
 | 
				
			||||||
log = get_logger(__name__)
 | 
					log = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def hl2mxmn(
 | 
					def hl2mxmn(ohlc: np.ndarray) -> np.ndarray:
 | 
				
			||||||
    ohlc: np.ndarray,
 | 
					 | 
				
			||||||
    # downsample_by: int = 0,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
) -> np.ndarray:
 | 
					 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    Convert a OHLC struct-array containing 'high'/'low' columns
 | 
					    Convert a OHLC struct-array containing 'high'/'low' columns
 | 
				
			||||||
    to a "joined" max/min 1-d array.
 | 
					    to a "joined" max/min 1-d array.
 | 
				
			||||||
| 
						 | 
					@ -50,12 +47,6 @@ def hl2mxmn(
 | 
				
			||||||
        'high',
 | 
					        'high',
 | 
				
			||||||
    ]]
 | 
					    ]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # XXX: don't really need this any more since we implemented
 | 
					 | 
				
			||||||
    # the "tracer" routine, `numba`-style..
 | 
					 | 
				
			||||||
    # create a "max and min" sequence from ohlc datums
 | 
					 | 
				
			||||||
    # hl2d = structured_to_unstructured(hls)
 | 
					 | 
				
			||||||
    # hl1d = hl2d.flatten()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mxmn = np.empty(2*hls.size, dtype=np.float64)
 | 
					    mxmn = np.empty(2*hls.size, dtype=np.float64)
 | 
				
			||||||
    x = np.empty(2*hls.size, dtype=np.float64)
 | 
					    x = np.empty(2*hls.size, dtype=np.float64)
 | 
				
			||||||
    trace_hl(hls, mxmn, x, index[0])
 | 
					    trace_hl(hls, mxmn, x, index[0])
 | 
				
			||||||
| 
						 | 
					@ -63,18 +54,6 @@ def hl2mxmn(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return mxmn, x
 | 
					    return mxmn, x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # if downsample_by < 2:
 | 
					 | 
				
			||||||
    #     return mxmn, x
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # dsx, dsy = downsample(
 | 
					 | 
				
			||||||
    #     y=mxmn,
 | 
					 | 
				
			||||||
    #     x=x,
 | 
					 | 
				
			||||||
    #     bins=downsample_by,
 | 
					 | 
				
			||||||
    # )
 | 
					 | 
				
			||||||
    # log.info(f'downsampling by {downsample_by}')
 | 
					 | 
				
			||||||
    # print(f'downsampling by {downsample_by}')
 | 
					 | 
				
			||||||
    # return dsy, dsx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@jit(
 | 
					@jit(
 | 
				
			||||||
    # TODO: the type annots..
 | 
					    # TODO: the type annots..
 | 
				
			||||||
| 
						 | 
					@ -176,6 +155,7 @@ def downsample(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ohlc_flatten(
 | 
					def ohlc_flatten(
 | 
				
			||||||
    ohlc: np.ndarray,
 | 
					    ohlc: np.ndarray,
 | 
				
			||||||
 | 
					    use_mxmn: bool = False,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
) -> tuple[np.ndarray, np.ndarray]:
 | 
					) -> tuple[np.ndarray, np.ndarray]:
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
| 
						 | 
					@ -186,15 +166,18 @@ def ohlc_flatten(
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    index = ohlc['index']
 | 
					    index = ohlc['index']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    flat = rfn.structured_to_unstructured(
 | 
					    if use_mxmn:
 | 
				
			||||||
        ohlc[['open', 'high', 'low', 'close']]
 | 
					        flat, x = hl2mxmn(ohlc)
 | 
				
			||||||
    ).flatten()
 | 
					    else:
 | 
				
			||||||
 | 
					        flat = rfn.structured_to_unstructured(
 | 
				
			||||||
 | 
					            ohlc[['open', 'high', 'low', 'close']]
 | 
				
			||||||
 | 
					        ).flatten()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    x = np.linspace(
 | 
					        x = np.linspace(
 | 
				
			||||||
        start=index[0] - 0.5,
 | 
					            start=index[0] - 0.5,
 | 
				
			||||||
        stop=index[-1] + 0.5,
 | 
					            stop=index[-1] + 0.5,
 | 
				
			||||||
        num=4*len(ohlc),
 | 
					            num=len(flat),
 | 
				
			||||||
    )
 | 
					        )
 | 
				
			||||||
    return x, flat
 | 
					    return x, flat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,16 +185,33 @@ def ohlc_to_m4_line(
 | 
				
			||||||
    ohlc: np.ndarray,
 | 
					    ohlc: np.ndarray,
 | 
				
			||||||
    px_width: int,
 | 
					    px_width: int,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uppx: Optional[float] = None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
) -> tuple[np.ndarray, np.ndarray]:
 | 
					) -> tuple[np.ndarray, np.ndarray]:
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    Convert an OHLC struct-array to a m4 downsampled 1-d array.
 | 
					    Convert an OHLC struct-array to a m4 downsampled 1-d array.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    xpts, flat = ohlc_flatten(ohlc)
 | 
					    xpts, flat = ohlc_flatten(ohlc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if uppx:
 | 
				
			||||||
 | 
					        # optionally log-scale down the "supposed pxs on screen"
 | 
				
			||||||
 | 
					        # as the units-per-px (uppx) get's large.
 | 
				
			||||||
 | 
					        scaler = round(
 | 
				
			||||||
 | 
					            max(
 | 
				
			||||||
 | 
					                # NOTE: found that a 16x px width brought greater
 | 
				
			||||||
 | 
					                # detail, likely due to dpi scaling?
 | 
				
			||||||
 | 
					                # px_width=px_width * 16,
 | 
				
			||||||
 | 
					                32 / (1 + math.log(uppx, 2)),
 | 
				
			||||||
 | 
					                1
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        px_width *= scaler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bins, x, y = ds_m4(
 | 
					    bins, x, y = ds_m4(
 | 
				
			||||||
        xpts,
 | 
					        xpts,
 | 
				
			||||||
        flat,
 | 
					        flat,
 | 
				
			||||||
        px_width=px_width * 16,
 | 
					        px_width=px_width,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    x = np.broadcast_to(x[:, None], y.shape)
 | 
					    x = np.broadcast_to(x[:, None], y.shape)
 | 
				
			||||||
    x = (x + np.array([-0.43, 0, 0, 0.43])).flatten()
 | 
					    x = (x + np.array([-0.43, 0, 0, 0.43])).flatten()
 | 
				
			||||||
| 
						 | 
					@ -313,6 +313,7 @@ def ds_m4(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@jit(
 | 
					@jit(
 | 
				
			||||||
    nopython=True,
 | 
					    nopython=True,
 | 
				
			||||||
 | 
					    nogil=True,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def _m4(
 | 
					def _m4(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue