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
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
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.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 _________________________________________________________________
model.load_weights('simplecnn.h5')
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')
api = sj.Shioaji(simulation=False)
with open('login.json', 'r') as f:
kw_login = json.loads(f.read())
api.login(**kw_login)
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))
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,
)
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,))
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
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
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)
data_in = np.random.rand(2)
data_inner = np.random.rand(2)
data_outer = np.random.rand(4)
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
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
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
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
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
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]
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
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
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
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])
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()
def update_ohlc_chart():
global ohlc_chart
ohlc_chart.x = df_min.dropna().index
ohlc_chart.y = df_min.dropna().values
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]
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()
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`.
@sj.on_event
def event_callback(resp_code, event_code, event):
print("Respone Code: {} | Event Code: {} | Event: {}".format(resp_code, event_code, event))
api.quote.set_callback(quote_callback)
api.quote.set_event_callback(event_callback)
pie_in.label_color = '#fff'
pie_inner.label_color = '#fff'
pie_outer.label_color = '#fff'
VBox([init_model_chart(),
HBox([init_ohlc_chart(), init_tickchart(),]),
HBox([pies_chart(), bidask_bar_chart(), ]),
])
VBox(children=(Figure(axes=[Axis(scale=LinearScale()), Axis(orientation='vertical', scale=LinearScale(), tick_…
TXFR1 = api.Contracts.Futures.TXF.TXF201904
TSE2330 = api.Contracts.Stocks.TSE.TSE2330
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
@sj.on_quote
def quote_callback(topic, quote_msg):
global api
%%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)
%%timeit
model.predict(input_x)
2.91 ms ± 98.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
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)
! 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)
import redis
import msgpack
rs = redis.Redis(host='localhost', port=6666)
@sj.on_quote
def quote_callback(topic, quote_msg):
rs.rpush(topic, msgpack.dumps(quote_msg))
api.quote.set_callback(quote_callback)