Shioaji Quote Api with Tensorflow

shioaji-logotensorflow-logo

In [1]:
import json
import datetime as dt
import numpy as np
import pandas as pd
import bqplot as bq
import shioaji as sj
from ipywidgets import HBox, VBox
In [2]:
import tensorflow as tf
from tensorflow.keras.layers import Conv2D, MaxPool2D, Dropout, Dense, Input, Flatten
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import categorical_crossentropy

Build simple convolution neural network model that convolution through time

In [4]:
input_layer = Input(shape=(500, 6, 1), name='input')
conv1 = Conv2D(filters=8, kernel_size=(4, 1), activation='relu', name='conv1')(input_layer)
conv2 = Conv2D(filters=16, kernel_size=(4, 1), activation='relu', name='conv2')(conv1)
maxp1 = MaxPool2D(pool_size=(4, 1), name='maxp1')(conv2)
conv3 = Conv2D(filters=32, kernel_size=(4, 1), activation='relu', name='conv3')(maxp1)
conv4 = Conv2D(filters=64, kernel_size=(4, 1), activation='relu', name='conv4')(conv3)
maxp2 = MaxPool2D(pool_size=(4, 1), name='maxp2')(conv4)
conv5 = Conv2D(filters=64, kernel_size=(4, 1), activation='relu', name='conv5')(maxp2)
conv6 = Conv2D(filters=128, kernel_size=(4, 1), activation='relu', name='conv6')(conv5)
maxp3 = MaxPool2D(pool_size=(4, 1), name='maxp3')(conv6)
conv7 = Conv2D(filters=256, kernel_size=(2, 1), activation='relu', name='conv7')(maxp3)
conv8 = Conv2D(filters=256, kernel_size=(2, 1), activation='relu', name='conv8')(conv7)
maxp4 = MaxPool2D(pool_size=(2, 1), name='maxp4')(conv8)
flat = Flatten(name='flat')(maxp4)
dropout1 = Dropout(rate=0.4, name='dropout1')(flat)
fc1 = Dense(256, activation='relu', name='fc1')(dropout1)
dropout2 = Dropout(rate=0.4, name='dropout2')(fc1)
fc2 = Dense(3, activation='softmax', name='fc2')(dropout2)

The Model Structure

In [5]:
model = Model(inputs=[input_layer], outputs=[fc2])
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input (InputLayer)           (None, 500, 6, 1)         0         
_________________________________________________________________
conv1 (Conv2D)               (None, 497, 6, 8)         40        
_________________________________________________________________
conv2 (Conv2D)               (None, 494, 6, 16)        528       
_________________________________________________________________
maxp1 (MaxPooling2D)         (None, 123, 6, 16)        0         
_________________________________________________________________
conv3 (Conv2D)               (None, 120, 6, 32)        2080      
_________________________________________________________________
conv4 (Conv2D)               (None, 117, 6, 64)        8256      
_________________________________________________________________
maxp2 (MaxPooling2D)         (None, 29, 6, 64)         0         
_________________________________________________________________
conv5 (Conv2D)               (None, 26, 6, 64)         16448     
_________________________________________________________________
conv6 (Conv2D)               (None, 23, 6, 128)        32896     
_________________________________________________________________
maxp3 (MaxPooling2D)         (None, 5, 6, 128)         0         
_________________________________________________________________
conv7 (Conv2D)               (None, 4, 6, 256)         65792     
_________________________________________________________________
conv8 (Conv2D)               (None, 3, 6, 256)         131328    
_________________________________________________________________
maxp4 (MaxPooling2D)         (None, 1, 6, 256)         0         
_________________________________________________________________
flat (Flatten)               (None, 1536)              0         
_________________________________________________________________
dropout1 (Dropout)           (None, 1536)              0         
_________________________________________________________________
fc1 (Dense)                  (None, 256)               393472    
_________________________________________________________________
dropout2 (Dropout)           (None, 256)               0         
_________________________________________________________________
fc2 (Dense)                  (None, 3)                 771       
=================================================================
Total params: 651,611
Trainable params: 651,611
Non-trainable params: 0
_________________________________________________________________

Load the trained model weights

In [6]:
model.load_weights('simplecnn.h5')
In [3]:
def load_model():
    global model, graph
    graph = tf.get_default_graph()
    with graph.as_default():
        input_layer = Input(shape=(500, 6, 1), name='input')
        conv1 = Conv2D(filters=8, kernel_size=(4, 1), activation='relu', name='conv1')(input_layer)
        conv2 = Conv2D(filters=16, kernel_size=(4, 1), activation='relu', name='conv2')(conv1)
        maxp1 = MaxPool2D(pool_size=(4, 1), name='maxp1')(conv2)
        conv3 = Conv2D(filters=32, kernel_size=(4, 1), activation='relu', name='conv3')(maxp1)
        conv4 = Conv2D(filters=64, kernel_size=(4, 1), activation='relu', name='conv4')(conv3)
        maxp2 = MaxPool2D(pool_size=(4, 1), name='maxp2')(conv4)
        conv5 = Conv2D(filters=64, kernel_size=(4, 1), activation='relu', name='conv5')(maxp2)
        conv6 = Conv2D(filters=128, kernel_size=(4, 1), activation='relu', name='conv6')(conv5)
        maxp3 = MaxPool2D(pool_size=(4, 1), name='maxp3')(conv6)
        conv7 = Conv2D(filters=256, kernel_size=(2, 1), activation='relu', name='conv7')(maxp3)
        conv8 = Conv2D(filters=256, kernel_size=(2, 1), activation='relu', name='conv8')(conv7)
        maxp4 = MaxPool2D(pool_size=(2, 1), name='maxp4')(conv8)
        flat = Flatten(name='flat')(maxp4)
        dropout1 = Dropout(rate=0.4, name='dropout1')(flat)
        fc1 = Dense(256, activation='relu', name='fc1')(dropout1)
        dropout2 = Dropout(rate=0.4, name='dropout2')(fc1)
        fc2 = Dense(3, activation='softmax', name='fc2')(dropout2)
        model = Model(inputs=[input_layer], outputs=[fc2])
        model.load_weights('simplecnn.h5')

Init shioaji api and login

In [4]:
api = sj.Shioaji(simulation=False)
In [5]:
with open('login.json', 'r') as f:
    kw_login = json.loads(f.read())
api.login(**kw_login)

Init Charts dataset

In [6]:
tick_plot_length = 150
df_norm = pd.read_hdf('normalizer.h5')
norm_min = df_norm.loc['min'].values
norm_range = df_norm.loc['max'].values - norm_min

model_input_tick_num = 500
features_num = 6
np_features = np.zeros((model_input_tick_num, features_num))
pred_data = np.zeros((tick_plot_length, 3))
In [7]:
today = dt.date.today()
df_min = pd.DataFrame(index=pd.date_range(start=dt.datetime(*(today.year, today.month, today.day, 8, 45)),
                                          end=dt.datetime(*(today.year, today.month, today.day, 13, 45)),
                                          freq='1T'),
                      columns=['open', 'high', 'low', 'close'], dtype=float,
                     )
In [8]:
last_close = 10200

x_tick_index = np.arange(tick_plot_length)
y_price = np.zeros((tick_plot_length,)) + last_close
y_vol = np.zeros((tick_plot_length,))
updown_color = np.zeros((tick_plot_length,)) + 1 
askbid_color_data = np.zeros((tick_plot_length,))
In [9]:
new_index = 0
new_deal_price = 1
new_vol = 1
new_updown_color = 1
new_askbid_color_data = 1
ask_price = 0
bid_price = 0
In [10]:
ask_bid_period_length = 500
ask_bid_static = np.zeros((6, ask_bid_period_length))
large_ask_deal_volsum = 0
small_ask_deal_volsum = 0
large_bid_deal_volsum = 0
small_bid_deal_volsum = 0
ask_deal_count = 0
bid_deal_count = 0
In [11]:
bid_color = '#ff0000'
ask_color = '#03ba00'
x_bar_data = np.zeros(10)
y_bar_data = np.zeros(10, dtype=np.int64)
color_bar_data = np.array(['#000000' for i in range(10)])
y_bar_data_diff = np.zeros(10, dtype=np.int64)
In [12]:
data_in = np.random.rand(2)
data_inner = np.random.rand(2)
data_outer = np.random.rand(4)

define init charts function

In [13]:
def init_model_chart():
    global model_bar
    x_ord = bq.LinearScale()
    y_sc = bq.LinearScale()

    model_bar = bq.Bars(x=x_tick_index, y=pred_data.T[:, :], 
                  scales={'x': x_ord, 'y': y_sc},
                  colors=['#848484', '#f44242', '#00d100'], type='stacked')
    ax_x = bq.Axis(scale=x_ord)
    ax_y = bq.Axis(scale=y_sc, tick_format='0.2f', orientation='vertical')

    fig = bq.Figure(marks=[model_bar], axes=[ax_x, ax_y],
                   fig_margin= {"top":60, "bottom":30, "left":60, "right":0},)
    fig.layout.height = '200px'
    fig.layout.width = '1200px'
    return fig
In [14]:
def init_ohlc_chart():
    global ohlc_chart
    sc = bq.LinearScale()
    dt_scale = bq.DateScale()
    ax_x = bq.Axis(label='datetime', scale=dt_scale)
    ax_y = bq.Axis(label='price', scale=sc, orientation='vertical', tick_format='0.0f')

    # Construct the marks
    ohlc_chart = bq.OHLC(x=df_min.index, y=df_min.dropna().values,#df_tick_nosim_min.values[:10], 
                   marker='candle', scales={'x': dt_scale, 'y': sc}, format='ohlc',
                   colors=['red', 'limegreen'], stroke='gray', 
                   opacities=[0.85 for _ in df_min.index],
                   display_legend=True, labels=['TXFD9'])

    fig = bq.Figure(axes=[ax_x, ax_y], marks=[ohlc_chart],
                   fig_margin= {"top":30, "bottom":30, "left":60, "right":60},)
    fig.layout.height = '450px'
    fig.layout.width = '600px'
    return fig
In [15]:
def init_tickchart():
    global line_chart, scatter_chart
    global x_tick_index, y_price, y_vol, updown_color, askbid_color_data
    x_sc = bq.LinearScale()
    y_sc = bq.LinearScale()
    sc_size = bq.LinearScale()
    color_line = bq.ColorScale(colors=['red', 'gray', 'green'])
    c_ord = bq.ColorScale(colors=['gray', 'HotPink', 'SeaGreen'])

    line_chart = bq.FlexLine(x=x_tick_index, y=y_price, color=updown_color,
                             scales= {'x': x_sc, 'y': y_sc, 'color': color_line}, 
                             display_legend=True, labels=["TXFD9"])

    def_tt = bq.Tooltip(fields=['x', 'y', 'size'], formats=['', '.0f', '.0f'], 
                        lables=['tick', 'price', 'vol'])

    scatter_chart = bq.Scatter(x=x_tick_index, y=y_price, size=y_vol, color=askbid_color_data, 
                               scales= {'x': x_sc, 'y': y_sc, 'size': sc_size, 'color': c_ord}, 
                               #colors=['dodgerblue',],
                               tooltip=def_tt, lables=['tick', 'price', 'vol'],
                               selected_style={'opacity': 1.0,}, #'fill': 'DarkOrange', 'stroke': 'Red'}, 
                               unselected_style={'opacity': 0.7})

    ax_x = bq.Axis(scale=x_sc)
    ax_y = bq.Axis(scale=y_sc, orientation='vertical', tick_format='0.0f')
    ax_color = bq.ColorAxis(label='updown', scale=color_line, tick_format='.0f')

    fig = bq.Figure(marks=[line_chart, scatter_chart], axes=[ax_x, ax_y, ax_color],
                    fig_margin= {"top":30, "bottom":60, "left":60, "right":60},
                    #min_aspect_ratio=1, max_aspect_ratio=3,
                    #fig_margin= {"top":0, "bottom":60, "left":60, "right":0},
                   )
    fig.layout.height = '450px'
    fig.layout.width = '600px'
    return fig
In [16]:
def bidask_bar_chart():
    global x_bar_data, y_bar_data, color_bar_data, y_data_diff
    global bar_bidask, bar_bidask_diff
    x_ord = bq.OrdinalScale()
    y_sc = bq.LinearScale()
    stroke_color = '#000000'
    bar_bidask = bq.Bars(x=x_bar_data, y=y_bar_data,
                         scales={'x': x_ord, 'y': y_sc}, 
                         stroke=stroke_color, color_mode='group',
                         padding=0, colors=color_bar_data.tolist(),
                         type='stacked', orientation='horizontal',
                         opacities=[0.7 for _ in x_bar_data],
                        )
    bar_bidask_diff = bq.Bars(x=x_bar_data, y=y_bar_data_diff,
                         scales={'x': x_ord, 'y': y_sc}, 
                         stroke=stroke_color, color_mode='group',
                         padding=0, colors=color_bar_data.tolist()[::-1],
                         type='stacked', orientation='horizontal',
                         opacities=[0.7 for _ in x_bar_data],
                        )
    ax_x = bq.Axis(scale=x_ord, orientation='vertical')
    ax_y = bq.Axis(scale=y_sc, tick_format='0.0f')

    fig = bq.Figure(marks=[bar_bidask, bar_bidask_diff], axes=[ax_x, ax_y],
                    #min_aspect_ratio=1, max_aspect_ratio=1,
                    fig_margin= {"top":30, "bottom":60, "left":60, "right":60},
                   )
    fig.layout.height = '450px'
    fig.layout.width = '600px'
    return fig
In [17]:
def pies_chart():
    global data_in, data_inner, data_outer
    global pie_outer, pie_inner, pie_in
    outer_pie_redius = [180, 150]
    inner_pie_redius = [100, 70]
    in_pie_redius = [50, 10]
    pie_outer = bq.Pie(sizes=data_outer, display_labels='outside', apply_clip=False,
              radius=outer_pie_redius[0], inner_radius=outer_pie_redius[1], 
              display_values=True, values_format='.0%', colors=['red', 'limegreen', 'red', 'limegreen'],
              labels=['大單委賣成交', '大單委買成交', '小單委賣成交', '小單委買成交'])

    pie_inner = bq.Pie(sizes=data_inner, display_labels='inside', apply_clip=False,
                    radius=inner_pie_redius[0], inner_radius=inner_pie_redius[1], 
                    display_values=True, values_format='.0%', colors=['red', 'limegreen'],
                    labels=['賣成筆', '買成筆'])

    pie_in = bq.Pie(sizes=data_in, display_labels='inside', #apply_clip=False,
                    radius=in_pie_redius[0], inner_radius=in_pie_redius[1], 
                    display_values=True, values_format='.0%', colors=['limegreen', 'red'],
                    labels=['賣成口', '買成口'])

    fig = bq.Figure(marks=[pie_outer, pie_inner, pie_in], animation_duration=1000,
                    #min_aspect_ratio=1, max_aspect_ratio=1.5,
                    fig_margin= {"top":30, "bottom":60, "left":60, "right":60},
                   )
    fig.layout.height = '450px'
    fig.layout.width = '600px'
    return fig

define processing data function

In [18]:
def split_data(rec, index):
    sp_rec = {}
    [sp_rec.update({k: v[index] if isinstance(v, (list, tuple)) else v}) 
     for k, v in rec.items()]
    sp_rec['idx'] = index
    return sp_rec

def proc_model_data(quote_msg):
    global np_features, pred_data
    global model, graph
    quote_msgs = [split_data(quote_msg, ind) for ind, _ in enumerate(quote_msg['Close'])]
    for msg in quote_msgs:
        if msg.get('TickType', 0) == 1:
            ask_vol, bid_vol = (msg.get('Volume', 0), 0)
        elif msg.get('TickType', 0) == 2:
            ask_vol, bid_vol = (0, msg.get('Volume', 0))
        else:
            ask_vol, bid_vol = (0, 0)
        np_features[:-1] = np_features[1:]
        np_features[-1] = [msg.get('Close', 0), msg.get('DiffPrice', 0), 
                           msg.get('Volume', 0), msg.get('TargetKindPrice', 0),
                           ask_vol, bid_vol,]
        input_x = ((np_features - norm_min) / norm_range)[np.newaxis,:,:,np.newaxis]
        pred_data[:-1] = pred_data[1:]
        with graph.as_default():
            model.load_weights('simplecnn.h5')
            pred_data[-1] = model.predict(input_x)[0]
In [19]:
def proc_ohlc_data(quote_msg, new_deal_price):
    global df_min
    ts = pd.Timestamp("{Date} {Time}" .format(**quote_msg)).replace(second=0, microsecond=0)
    df_min.loc[ts, 'open'] = new_deal_price if np.isnan(df_min.loc[ts, 'open']) else df_min.loc[ts, 'open'] 
    df_min.loc[ts, 'high'] = new_deal_price if np.isnan(df_min.loc[ts, 'high']) or new_deal_price > df_min.loc[ts, 'high'] else df_min.loc[ts, 'high'] 
    df_min.loc[ts, 'low'] = new_deal_price if np.isnan(df_min.loc[ts, 'low']) or new_deal_price < df_min.loc[ts, 'low'] else df_min.loc[ts, 'low']
    df_min.loc[ts, 'close'] = new_deal_price
In [20]:
def proc_ask_bid_bardata(topic, quote_msg):
    global x_bar_data, y_bar_data, color_bar_data, y_data_diff
    ask_price = quote_msg.get('AskPrice', [0])[0]
    bid_price = quote_msg.get('BidPrice', [0])[0]

    x_bar_data[:5] = quote_msg['BidPrice'][::-1]
    x_bar_data[5:] = quote_msg['AskPrice']
    y_bar_data[:5] = quote_msg['BidVolume'][::-1]
    y_bar_data[5:] = quote_msg['AskVolume']
    y_bar_data[5:] *= -1
    y_bar_data_diff[5:] = quote_msg["DiffAskVol"]
    y_bar_data_diff[:5] = quote_msg['DiffBidVol'][::-1]
    color_bar_data[:5] = bid_color
    color_bar_data[5:] = ask_color
In [21]:
def proc_tick_chartdata(topic, quote_msg):
    global new_index, new_deal_price, new_vol, new_updown_color, new_askbid_color_data
    global x_tick_index, y_price, y_vol, updown_color, askbid_color_data
    new_index += 1
    new_deal_price = quote_msg.get('Close', [0,])[-1]
    new_vol = quote_msg.get('Volume', [0,])[-1]
    ask_volsum = quote_msg.get('TradeAskVolSum', 0)
    bid_volsum = quote_msg.get('TradeBidVolSum', 0)
    new_askbid_color_data = quote_msg.get('TickType', [0,])[-1]# 0: -, 1: ask, 2, bid
    if new_deal_price > y_price[-1]:
        new_updown_color = 0
    elif new_deal_price < y_price[-1]:
        new_updown_color = 2
    else:
        new_updown_color = 1
        
    x_tick_index[:-1] = x_tick_index[1:]
    x_tick_index[-1] = new_index
    y_price[:-1] = y_price[1:]
    y_price[-1] = new_deal_price
    y_vol[:-1] = y_vol[1:]
    y_vol[-1] = new_vol
    updown_color[:-1] = updown_color[1:]
    updown_color[-2] = new_updown_color
    askbid_color_data[:-1] = askbid_color_data[1:]
    askbid_color_data[-1] = new_askbid_color_data
In [22]:
def proc_ask_bid_staticdata(topic, quote_msg, vol_threshold):
    global ask_bid_static
    global small_bid_deal_volsum, ask_deal_count, bid_deal_count
    ask_volsum = quote_msg.get('TradeAskVolSum', 0)
    bid_volsum = quote_msg.get('TradeBidVolSum', 0)
    ask_bid_static[:, :-1] = ask_bid_static[:, 1:]
    if new_askbid_color_data == 1:
        ask_deal_count += 1
        ask_bid_static[4][-1] = 1
        ask_bid_static[5][-1] = 0
        if new_vol >= vol_threshold:
            ask_bid_static[0][-1] = new_vol
        else:
            ask_bid_static[1][-1] = new_vol
    elif new_askbid_color_data == 2:
        bid_deal_count += 1
        ask_bid_static[5][-1] = 1
        ask_bid_static[4][-1] = 0
        if new_vol >= vol_threshold:
            ask_bid_static[2][-1] = new_vol
        else:
            ask_bid_static[3][-1] = new_vol
    
    ask_bid_static_sum = ask_bid_static.sum(axis=1)
    data_outer = ask_bid_static_sum[:4]
    data_inner = ask_bid_static_sum[4:]
    data_in = np.array([ask_volsum, bid_volsum])

define update chart function

In [23]:
def update_model_chart():
    global model_bar
    with model_bar.hold_sync():
        model_bar.x = x_tick_index.copy()
        model_bar.y = pred_data.T.copy()
In [24]:
def update_ohlc_chart():
    global ohlc_chart
    ohlc_chart.x = df_min.dropna().index
    ohlc_chart.y = df_min.dropna().values
In [25]:
def update_barchart(x_bar_data, y_bar_data, 
                    color_bar_data, y_bar_data_diff):
    global bar_bidask, bar_bidask_diff
    with bar_bidask.hold_sync() and bar_bidask_diff.hold_sync():
        bar_bidask.x = x_bar_data.copy()
        bar_bidask.y = y_bar_data.copy()
        bar_bidask.colors = color_bar_data.tolist()
        bar_bidask_diff.x = x_bar_data.copy()
        bar_bidask_diff.y = y_bar_data_diff.copy()
        bar_bidask_diff.colors = color_bar_data.tolist()[::-1]
In [26]:
def update_tickandpie_chart(update_freq):
    global line_chart, scatter_chart
    global pie_outer, pie_inner, pie_in
    
    if (new_index % update_freq) == 0 and new_index >= tick_plot_length:
        with line_chart.hold_sync() and scatter_chart.hold_sync():
            line_chart.x = x_tick_index.copy()
            line_chart.y = y_price.copy()
            line_chart.color = updown_color.copy()
            scatter_chart.x = x_tick_index.copy()
            scatter_chart.y = y_price.copy()
            scatter_chart.size = y_vol.copy() * 10
            scatter_chart.color = askbid_color_data.copy()
        with pie_outer.hold_sync() and pie_inner.hold_sync() and pie_in.hold_sync():
            pie_outer.sizes = data_outer / data_outer.sum()
            pie_inner.sizes = data_inner / data_inner.sum()
            pie_in.sizes = data_in / data_in.sum()

define on quote callback

In [27]:
load_model()
@sj.on_quote
def quote_callback(topic, quote_msg):
    global tick_plot_length
    global line_chart, scatter_chart
    global pie_outer, pie_inner, pie_in
    global x_tick_index, y_price, y_vol, updown_color, askbid_color_data
    global new_index, new_deal_price, new_vol, new_updown_color, new_askbid_color_data
    global ask_price, bid_price
    global large_ask_deal_volsum, small_ask_deal_volsum, large_bid_deal_volsum
    global small_bid_deal_volsum, ask_deal_count, bid_deal_count
    global ask_bid_static
    global bar_bidask, bar_bidask_diff
    global x_bar_data, y_bar_data, color_bar_data, y_data_diff
    global ohlc_chart, df_min
    global model, graph, init_model
    vol_threshold = 10
    #print(topic, quote_msg)
    if topic.startswith('Q') and 'TXFD9' in topic:
        proc_ask_bid_bardata(topic, quote_msg)
        update_barchart(x_bar_data, y_bar_data, 
                        color_bar_data, y_bar_data_diff)
        
    elif topic.startswith('L') and 'TXFD9' in topic:
        proc_tick_chartdata(topic, quote_msg)
        proc_ask_bid_staticdata(topic, quote_msg, vol_threshold)
        proc_ohlc_data(quote_msg, new_deal_price)
        proc_model_data(quote_msg)
        update_ohlc_chart()
        update_tickandpie_chart(update_freq=1)
        update_model_chart()
WARNING:tensorflow:From /Users/Tanni/.pyenv/versions/miniconda3-latest/lib/python3.7/site-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
WARNING:tensorflow:From /Users/Tanni/.pyenv/versions/miniconda3-latest/lib/python3.7/site-packages/tensorflow/python/keras/layers/core.py:143: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.
Instructions for updating:
Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.

define event callback

In [28]:
@sj.on_event
def event_callback(resp_code, event_code, event):
    print("Respone Code: {} | Event Code: {} | Event: {}".format(resp_code, event_code, event))

set callback function

In [29]:
api.quote.set_callback(quote_callback)
api.quote.set_event_callback(event_callback)
In [14]:
pie_in.label_color = '#fff'
pie_inner.label_color = '#fff'
pie_outer.label_color = '#fff'

Chart

In [30]:
VBox([init_model_chart(),
      HBox([init_ohlc_chart(), init_tickchart(),]),
      HBox([pies_chart(), bidask_bar_chart(), ]),
     ])

gif

Subscribe Quote

In [31]:
TXFR1 = api.Contracts.Futures.TXF.TXF201904
TSE2330 = api.Contracts.Stocks.TSE.TSE2330
In [32]:
api.quote.subscribe(TXFR1)
api.quote.subscribe(TXFR1, quote_type='bidask')
api.quote.subscribe(TSE2330)
api.quote.subscribe(TSE2330, quote_type='bidask')
Respone Code: 200 | Event Code: 16 | Event: Subscribe or Unsubscribe ok
Respone Code: 200 | Event Code: 16 | Event: Subscribe or Unsubscribe ok
Respone Code: 200 | Event Code: 16 | Event: Subscribe or Unsubscribe ok
Respone Code: 200 | Event Code: 16 | Event: Subscribe or Unsubscribe ok

Place order in quote callback?

  • use global to let Shioaji Object work fine
  • c++ call quote callback will borrow python interpreter only when interact with python
  • when the process back to c++ thread will release the blocking
  • It is not recommended to do that the thing spend too much time in the callback function
In [112]:
@sj.on_quote
def quote_callback(topic, quote_msg):
    global api

CNN model inference performance

In [100]:
%%timeit
input_x = ((np_features - norm_min) / norm_range)[np.newaxis,:,:,np.newaxis]
28 µs ± 353 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [102]:
%%timeit
model.predict(input_x)
2.91 ms ± 98.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Basic way for simplify the callback job for just push the quote msg to queue

In [114]:
storage_quote = {}
@sj.on_quote
def quote_callback(topic, quote_msg):
    global storage_quote
    if topic not in storage_quote:
        storage_quote[topic] = [quote_msg]
    else:
        storage_quote[topic].append(quote_msg)

api.quote.set_callback(quote_callback)

Use redis for cross instance and cross language to get the quote

In [111]:
! pip install redis msgpack
Requirement already satisfied: redis in /Users/Tanni/.pyenv/versions/miniconda3-latest/lib/python3.7/site-packages (3.2.1)
Requirement already satisfied: msgpack in /Users/Tanni/.pyenv/versions/miniconda3-latest/lib/python3.7/site-packages (0.6.1)
In [109]:
import redis
import msgpack
rs = redis.Redis(host='localhost', port=6666)
In [110]:
@sj.on_quote
def quote_callback(topic, quote_msg):
    rs.rpush(topic, msgpack.dumps(quote_msg))
    
api.quote.set_callback(quote_callback)

Some advanced alternative