iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
永豐金融APIs

深入解析 Shioaji API系列 第 22

Day 22 - Blocking & Non-blocking Mode

本篇重點

官方說明文件:https://sinotrade.github.io/tutor/advanced/nonblock/

  • Blocking & Non-blocking Mode說明
  • Order event callback
  • Non-blocking place order callback
  • Blocking & Non-blocking Mode執行時間差異

Blocking & Non-blocking Mode 說明

一般我們執行的function都是屬於Blocking,也就是要等到function執行結束並return執行結果,程式才會繼續往下執行,而Non-blocking Mode就是在call function時,不等function完全執行結束,就直接往下執行。

import shioaji as sj
from shioaji.constant import Action, StockPriceType, FuturesOrderType, FuturesOCType

api = sj.Shioaji(simulation=True)
api.login(
    person_id='PAPIUSER06', 
    passwd='2222'
)

contract = api.Contracts.Futures.MXF['MXF202110']
order = api.Order(action=Action.Buy,
    price=16700,
    quantity=1,
    price_type=StockPriceType.LMT,
    order_type=FuturesOrderType.ROD,
    octype=FuturesOCType.Auto,
    account=api.futopt_account
)

trade = api.place_order(contract, order, timeout=0) #timeout=0,即使用Non-blocking Mode執行
print(trade)

api.logout()

執行結果如下

contract=Future(code='MXFJ1', symbol='MXF202110', name='小型臺指10', category='MXF', delivery_month='202110', delivery_date='2021/10/20', underlying_kind='I', unit=1, limit_up=17998.0, limit_down=14726.0, reference=16362.0, update_date='2021/10/07') order=Order(action=<Action.Buy: 'Buy'>, price=16700, quantity=1, account=FutureAccount(person_id='PAPIUSER06', broker_id='F002000', account_id='9102620', signed=True, username='PAPIUSER06'), price_type=<StockPriceType.LMT: 'LMT'>, order_type=<FuturesOrderType.ROD: 'ROD'>) status=OrderStatus(status=<Status.Inactive: 'Inactive'>)

可以看到trade的資訊中並沒有委託單號,且OrderStatus為Status.Inactive,因為在Non-blocking Mode下並不會等到place_order完全執行結束後才往下執行,而是不等function回傳完整的結果,就直接往下執行下面的程式。在Non-blocking Mode下執行place_order,若要取得委託單完整資訊,可以透過callback方式回傳並取得。
在Non-blocking Mode下,callback方式有兩種,一種是order callback,可參考Day 15 - Order & Deal Event中的說明,而另一個是Non-blocking place order callback

Non-blocking place order callback

order callback跟Non-blocking place order callback的不同點在於,order callback在宣告完後,呼叫api.set_order_callback將callback程式指定為自行定義的callback function;而Non-blocking place order callback則是在呼叫api.place_order時,傳入自行定義的callback function,且此參數只有在Non-blocking Mode才有作用
程式範例說明如下:

import shioaji as sj
from shioaji.constant import Action, StockPriceType, FuturesOrderType, FuturesOCType
from shioaji.order import Trade #匯入Trade物件
import threading #匯入threading模組

api = sj.Shioaji(simulation=True)
api.login(
    person_id='PAPIUSER06', 
    passwd='2222'
)

event = threading.Event() #建立一個Event

def non_blocking_cb(trade:Trade):
    print('__non_blocking_callback__')
    print(trade) #將trade資訊輸出
    event.set() #執行set()讓主程式繼續執行

contract = api.Contracts.Futures.MXF['MXF202110']
order = api.Order(action=Action.Buy,
    price=16700,
    quantity=1,
    price_type=StockPriceType.LMT,
    order_type=FuturesOrderType.ROD,
    octype=FuturesOCType.Auto,
    account=api.futopt_account
)

trade = api.place_order(contract, order, timeout=0, cb=non_blocking_cb) #傳入上面定義的callback function
print(trade)
print('wait for callback...')
event.wait() #讓主程式進入等待而不直接結束

api.logout()

為了讓程式執行後,等待Non-blocking place order callback執行後才結束程式,在這裡我們透過Event().wait()來進行等待,並當callback function執行時,呼叫Event().set()讓原本的程式繼續執行。

Blocking & Non-blocking Mode 執行時間差異

最後,比較一下Blocking & Non-blocking Mode執行時間上的差異,請注意,這個執行時間會受到網路通訊品質及交易時段影響,以下時間僅供參考。
測試的程式內容如下:

import shioaji as sj
from shioaji.constant import Action, StockPriceType, FuturesOrderType, FuturesOCType
from shioaji.order import Trade
import threading, time

api = sj.Shioaji(simulation=True)
api.login(
    person_id='PAPIUSER06', 
    passwd='2222'
)
contract = api.Contracts.Futures.MXF['MXF202110']
order = api.Order(action=Action.Buy,
    price=16750,
    quantity=1,
    price_type=StockPriceType.LMT,
    order_type=FuturesOrderType.ROD,
    octype=FuturesOCType.Auto,
    account=api.futopt_account
)

event = threading.Event()

def non_blocking_cb(trade:Trade):
    print('__non_blocking_callback__')
    # print(trade)
    event.set()

print('start place_order with non-blocking...')
start_time = time.time() #抓取non-blocking開始時間
def non_blocking_cb(trade:Trade):
    print('__non_blocking_callback__')
    # print(trade) #因測試執行時間,所以不做輸出動作
    print(f'time of non blocking callback:{time.time()-start_time}') #callback執行時,計算所要花費的時間
    event.set()
trade = api.place_order(contract, order, timeout=0, cb=non_blocking_cb)
# print(trade) #因測試執行時間,所以不做輸出動作
print(f'time after place_order:{time.time()-start_time}') #計算api.place_order回傳結果所花費的時間
print('wait for non blocking callback...')
event.wait()

print('start place_order with blocking...')
start_time = time.time() #抓取blocking開始時間
def blocking_cb(stat, msg):
    print('__blocking_callback__')
    # print(stat, msg) #因測試執行時間,所以不做輸出動作
    print(f'time of blocking callback:{time.time()-start_time}') #callback執行時,計算所要花費的時間
    event.set()

api.set_order_callback(blocking_cb)
trade = api.place_order(contract, order)
# print(trade) #因測試執行時間,所以不做輸出動作
print(f'time after place_order:{time.time()-start_time}') #計算api.place_order回傳結果所花費的時間
print('wait for blocking callback...')

api.logout()

執行結果如下:

Response Code: 0 | Event Code: 0 | Info: host '218.32.76.102:80', hostname '218.32.76.102:80' IP 218.32.76.102:80 (host 1 of 1) (host connection attempt 1 of 1) (total connection attempt 1 of 1) | Event: Session up
start place_order with non-blocking...
time after place_order:0.015607357025146484
wait for non blocking callback...
__non_blocking_callback__
time of non blocking callback:3.3490772247314453
OrderState.FOrder {'operation': {'op_type': 'New', 'op_code': '00', 'op_msg': ''}, 'order': {'id': 'b0cb8b7d', 'seqno': '980069', 'ordno': 'kY01c', 'account': {'account_type': 'F', 'person_id': '', 'broker_id': 'F002000', 'account_id': '9102620', 'signed': True}, 'action': 'Buy', 'price': 16750.0, 'quantity': 1, 'order_type': 'ROD', 'market_type': 'Night', 'oc_type': 'New', 'subaccount': ''}, 'status': {'id': 'b0cb8b7d', 'exchange_ts': 1633621393, 'modified_price': 0.0, 'cancel_quantity': 0, 'order_quantity': 1}, 'contract': {'security_type': 'FUT', 'code': 'MXF', 'exchange': 'TIM', 'delivery_month': '202110', 'delivery_date': '', 'strike_price': 0.0, 'option_right': 'Future'}}
start place_order with blocking...
OrderState.FOrder {'operation': {'op_type': 'Cancel', 'op_code': '00', 'op_msg': '超過動態價格穩定標準'}, 'order': {'id': 'b0cb8b7d', 'seqno': '980069', 'ordno': 'kY01c', 'account': {'account_type': 'F', 'person_id': '', 'broker_id': 'F002000', 'account_id': '9102620', 'signed': True}, 'action': 'Buy', 'price': 16750.0, 'quantity': 1, 'order_type': 'ROD', 'market_type': 'Day', 'oc_type': 'New', 'subaccount': ''}, 'status': {'id': 'b0cb8b7d', 'exchange_ts': 1633621394, 'modified_price': 0.0, 'cancel_quantity': 1, 'order_quantity': 1}, 'contract': {'security_type': 'FUT', 'code': 'MXF', 'exchange': 'TIM', 'delivery_month': '202110', 'delivery_date': '', 'strike_price': 0.0, 'option_right': 'Future'}}
__blocking_callback__
time of blocking callback:0.0009984970092773438
__blocking_callback__
time of blocking callback:0.0019960403442382812
__blocking_callback__
time of blocking callback:0.0019960403442382812
__blocking_callback__
time of blocking callback:0.0029954910278320312
time after place_order:0.05179405212402344
wait for blocking callback...

從執行結果中,可以看到在non-blocking mode下,place_order會先回傳trade資訊,之後才會執行callback;但是在blocking mode下,是先會執行callback,然後place_order才會回傳trade資訊。
時間差異比較如下:

time.time()-start_time blocking non-blocking
place_order 0.05179405212402344 0.015607357025146484
callback 0.0029954910278320312 3.3490772247314453

而以下所列出的function,都支援以non-blocking mode的方式執行:

  • ticks
  • place_order
  • update_order
  • cancel_order
  • update_status
  • list_positions

上一篇
Day 21 - Shioaji Docker 環境介紹
下一篇
Day 23 - 重覆呼叫shioaji.Shioaji()產生的記憶體問題
系列文
深入解析 Shioaji API30

尚未有邦友留言

立即登入留言