今天来看一下如何使用Shioaji问回历史交易资料,不过在此先提醒一下,上一篇有讲到的永丰讲师的Youtube课程,其中讲师特别提醒大家,这个用法仅仅是方便永丰API用户测试用途使用,因为资源有限,并不建议大家过度使用这个方式进行历史资料的大量捞取,若在非正常情况下的使用,永丰是有权将使用者的帐号锁定。所以大家要使用的前提下,若仅进行资料测试时,尽量设定资料存取范围,以及爱惜资源。
以下仅列出程序片段要点,前面该做login的步骤,请还是要按照方式使用,可参考前一篇的介绍。
就拿护国神山2330台积电为例。
import pandas as pd
TSE2330 = api.Contracts.Stocks.TSE.TSE2330
ticks = api.ticks(
contract=TSE2330,
date="2021-10-13",
query_type=sj.constant.TicksQueryType.RangeTime,
time_start="13:24:00",
time_end="13:24:30"
)
pd.set_option('display.max_columns', None)
df = pd.DataFrame({**ticks})
df.ts = pd.to_datetime(df.ts)
print(df.tail(10))
这里使用了api.ticks()
取回我们指定的Contract的历史资料,可指定某天的当日tick资料,为了缩小资料量,可透过query_type
设定为TicksQueryType.RangeTime
的方式,我们可设定取回的起迄时间。
例如我这里设定了取回收盘前逐笔搓合时间段的某30秒的资料。
并且使用pandas的DataFrame进行资料整理,记得把ts的时间作转换。我们印出最末端10笔资料。
Output如下:
ts ask_price close bid_volume bid_price \
9 2021-10-13 13:24:09.456604 572.0 571.0 121 571.0
10 2021-10-13 13:24:11.003873 572.0 572.0 121 571.0
11 2021-10-13 13:24:14.429999 572.0 571.0 122 571.0
12 2021-10-13 13:24:18.495718 572.0 572.0 124 571.0
13 2021-10-13 13:24:22.597406 572.0 572.0 125 571.0
14 2021-10-13 13:24:23.154184 572.0 572.0 125 571.0
15 2021-10-13 13:24:23.206579 573.0 572.0 126 571.0
16 2021-10-13 13:24:27.416942 573.0 572.0 129 571.0
17 2021-10-13 13:24:27.782836 573.0 572.0 130 571.0
18 2021-10-13 13:24:28.055190 573.0 573.0 130 571.0
ask_volume volume
9 71 41
10 69 1
11 67 3
12 65 2
13 65 1
14 64 1
15 116 64
16 120 1
17 120 1
18 119 2
我们接着可以使用api.kbars()
来问回K线资料,一样指定contract与起迄日期,并会以每分钟的K线资料逐笔回传。而K线资料会有每个区间的Open、High、Low、Close四个价格数值,K线图也是以此作为最重要的依据。
kbars = api.kbars(
contract=TSE2330,
start="2021-10-12",
end="2021-10-12"
)
df = pd.DataFrame({**kbars})
df.ts = pd.to_datetime(df.ts)
我们印出部份的Kbars值如下:
Volume Close Open Low High Amount
ts
2021-10-12 09:01:00 4493 570.0 570.0 570.0 571.0 2.561513e+09
2021-10-12 09:02:00 415 570.0 570.0 569.0 571.0 2.364880e+08
2021-10-12 09:03:00 184 570.0 570.0 569.0 571.0 1.048730e+08
2021-10-12 09:04:00 636 569.0 570.0 568.0 570.0 3.616940e+08
2021-10-12 09:05:00 276 568.0 568.0 568.0 569.0 1.567810e+08
... ... ... ... ... ... ...
2021-10-12 13:22:00 84 573.0 573.0 573.0 574.0 4.815400e+07
2021-10-12 13:23:00 48 573.0 573.0 573.0 574.0 2.753100e+07
2021-10-12 13:24:00 128 573.0 574.0 573.0 574.0 7.336300e+07
2021-10-12 13:25:00 88 573.0 573.0 572.0 574.0 5.037700e+07
2021-10-12 13:30:00 3792 575.0 575.0 575.0 575.0 2.180400e+09
接下来,介绍一个画金融资料的好用图表工具:mplfinance
可透过conda的安装路径进行安装,可参考conda-forge package网址。
conda install -c conda-forge mplfinance
接着就可以使用简单的方法,来绘制K线图。
import mplfinance as mpf
df.set_index('ts', inplace=True)
df.index.name = "ts"
df.index = pd.DatetimeIndex(df.index)
color = mpf.make_marketcolors(up='r', down='g', inherit=True)
style = mpf.make_mpf_style(base_mpf_style='charles', marketcolors=color)
mpf.plot(df, **dict(type='candle', volume=True, style=style))
上面需设定DatetimeIndex,请参考上述方式,才能正确透过malfinance画图。
接着是定义其线图的颜色与型态,需要将上涨与下跌的重新指定台湾股市特有的红涨绿跌。(国外是相反过来的)
style有几种可以指定,可透过mplfinance.available_styles()
印出:
['blueskies', 'brasil', 'charles', 'checkers', 'classic', 'default', 'mike', 'nightclouds', 'sas','starsandstripes', 'yahoo']
再来就是指定plot的参数,我们需要画的是candle图,以及把上述的设定放入。
如此一来,我们的1分钟期的K线图就产生了。
官方文件的Snapshot定义如下:
Snapshot is a present stock, future, option info. It contain open, high, low, close, change price, average price, volume, total volume, buy price, buy volume, sell price, sell volume and yesterday volume.
也就是股票、期货、选择权的当下资讯的摘要整理资讯。
直接使用范例程序看结果比较有感觉,Snapshot可一次放入多笔Contracts。
我们将2330台积电与2409友达光电放入作快照:
contracts = [api.Contracts.Stocks['2330'], api.Contracts.Stocks['2409']]
snapshots = api.snapshots(contracts)
df = pd.DataFrame(snapshots)
df.ts = pd.to_datetime(df.ts)
print(df)
Output结果如下:
amount average_price buy_price buy_volume change_price change_rate \
0 25695000 571.26 570.0 2289.0 -4.00 -0.70
1 6014400 16.95 16.8 3349.0 -0.25 -1.47
change_type close code exchange high low open sell_price \
0 ChangeType.Down 571.0 2330 TSE 575.0 570.0 572.00 571.00
1 ChangeType.Down 16.8 2409 TSE 17.2 16.8 17.15 16.85
sell_volume tick_type total_amount total_volume ts \
0 19 TickType.Buy 10804728928 18914 2021-10-13 14:30:00
1 218 TickType.Sell 997845168 58854 2021-10-13 14:30:00
volume volume_ratio yesterday_volume
0 45 0.71 26522.0
1 358 0.83 71159.0
证券的买卖交易是投资人最关心的行为,当然程序交易必定终究还是要出手的。
当然交易不是买,就是卖,但其中有不少的交易特性是需要了解的,其Order的属性如下:
price (float or int): the price of order
quantity (int): the quantity of order
action (str): order action to buy or sell
{Buy, Sell}
price_type (str): pricing type of order
{LMT, MKT, MKP} (限价、市价、范围市价)
order_type (str): the type of order
{ROD, IOC, FOK}
order_cond (str): order condition stock only
{Cash, MarginTrading, ShortSelling} (现股、融资、融券)
order_lot (str): the type of order
{Common, Fixing, Odd, IntradayOdd} (整股、定盘、盘後零股、盘中零股)
first_sell {str}: the type of first sell
{true, false}
account (:obj:Account): which account to place this order
ca (binary): the ca of this order
价格与数量这不用多说,但当然你是买一般整股还是零股交易,数量的单位数是不同的需要特别注意。
再来就是会影响成交行为的挂单类型:ROD
, IOC
, FOK
而以上的Order物件,是纯粹就「不含Contract的交易资讯」进行设定,设定好了後,才和你要的标的物Contract一起送到api.place_order()
中。
我们就进行针对友达光电2409进行小买5张的挂单实例:
TSE2409 = api.Contracts.Stocks.TSE.TSE2409
order = api.Order(
price=17.0,
quantity=5,
action=sj.constant.Action.Buy,
price_type=sj.constant.StockPriceType.LMT,
order_type=sj.constant.TFTOrderType.ROD,
account=api.stock_account
)
trade_2409 = api.place_order(TSE2409, order)
print(trade_2409)
上述先准备好目标Contract,然後是要挂单的Order设定。
我们以17.0元,下5张委买,使用限价模式以及ROD挂单。接着就可以把呼叫place_order()挂单。
以下是挂单的资讯:
contract=Stock(exchange=<Exchange.TSE: 'TSE'>, code='2409', symbol='TSE2409', name='友达', category='26', unit=1000, limit_up=18.75, limit_down=15.35, reference=17.05, update_date='2021/10/13', margin_trading_balance=99098, short_selling_balance=1344, day_trade=<DayTrade.Yes: 'Yes'>) order=Order(action=<Action.Buy: 'Buy'>, price=17.0, quantity=5, id='e40a1458', seqno='100538', ordno='00000', account=Account(account_type=<AccountType.Stock: 'S'>, person_id='PAPIUSER02', broker_id='9A95', account_id='0504486', signed=True), price_type=<StockPriceType.LMT: 'LMT'>, order_type=<FuturesOrderType.ROD: 'ROD'>) status=OrderStatus(id='e40a1458', status=<Status.PendingSubmit: 'PendingSubmit'>, status_code='0', order_datetime=datetime.datetime(2021, 10, 13, 22, 45, 23), deals=[])
上面的资讯,分为contract
、order
以及status
三部份,而status中我们可看到目前状态为PendingSubmit
(传送中)。
共有以下的状态:
可使用api.update_status()
对帐户进行状态更新,里面传入的参数是证券帐户。
例如:
api.update_status(api.stock_account)
而我们可以把刚刚上面使用变数接值下来的交易trade,进行其他的操作,例如取消单或修改单量或价格等。
我们以下面的取消单实例,看一下status的变化:
trade_2409 = api.place_order(TSE2409, order)
print(trade_2409)
api.update_status(api.stock_account)
print(trade_2409)
api.cancel_order(trade_2409)
api.update_status(api.stock_account)
print(trade_2409)
我们只列出上面三次print(trade_2409)的status的部份:
status=OrderStatus(id='e0d6ad73', status=<Status.PendingSubmit: 'PendingSubmit'>, status_code='0', order_datetime=datetime.datetime(2021, 10, 13, 22, 57, 2), deals=[])
status=OrderStatus(id='e0d6ad73', status=<Status.PreSubmitted: 'PreSubmitted'>, status_code='R', order_datetime=datetime.datetime(2021, 10, 13, 22, 57, 2), deals=[])
status=OrderStatus(id='e0d6ad73', status=<Status.Cancelled: 'Cancelled'>, status_code='X', order_datetime=datetime.datetime(2021, 10, 13, 22, 57, 2), cancel_quantity=5, deals=[])
可看到上述的status历经了:PendingSubmit(传送中) → PreSubmitted(预约单) → Cancelled(已删除)
都看了股票了,也把期货与选择权的Order属性也一并确认一下:
price (float or int): the price of order
quantity (int): the quantity of order
action (str): order action to buy or sell
{Buy, Sell}
price_type (str): pricing type of order
{LMT, MKT, MKP} (限价、市价、范围市价)
order_type (str): the type of order
{ROD, IOC, FOK}
octype (str): the type or order to open new position or close position future only
{Auto, NewPosition, Cover, DayTrade} (自动、新仓、平仓、当冲)
account (:obj:Account): which account to place this order
ca (binary): the ca of this order
上述在octype
和股票不太相同。
我们针对大台指TXF 2021年10月份期货进行新仓挂买进2口。
TXF202110 = api.Contracts.Futures.TXF.TXF202110
order = api.Order(action="Buy",
price=16355,
quantity=2,
price_type=sj.constant.StockPriceType.LMT,
order_type=sj.constant.FuturesOrderType.ROD,
octype=sj.constant.FuturesOCType.Auto,
account=api.futopt_account)
trade_txf202110 = api.place_order(TXF202110, order)
print(trade_txf202110)
取得的trade内容如下:
contract=Future(code='TXFJ1', symbol='TXF202110', name='台股期货10', category='TXF', delivery_month='202110', delivery_date='2021/10/20', underlying_kind='I', unit=1, limit_up=18060.0, limit_down=14778.0, reference=16419.0, update_date='2021/10/13') order=Order(action=<Action.Buy: 'Buy'>, price=16355, quantity=2, id='91242c54', seqno='980237', account=Account(account_type=<AccountType.Future: 'F'>, person_id='PAPIUSER02', broker_id='F002000', account_id='9100295', signed=True), price_type=<StockPriceType.LMT: 'LMT'>, order_type=<FuturesOrderType.ROD: 'ROD'>) status=OrderStatus(id='91242c54', status=<Status.PendingSubmit: 'PendingSubmit'>, status_code=' ', order_datetime=datetime.datetime(2021, 10, 13, 23, 5, 38), deals=[])
除了以前要买卖零股只能於盘後进行交易,现在在盘中也可以方便投资人进行零股交易。在这边我们就需要在Order中设定order_lot=sj.constant.TFTStockOrderLot.IntradayOdd
,不特别指定的话是预设的Common
(一般整股交易)。
TSE2409 = api.Contracts.Stocks.TSE.TSE2409
order = api.Order(
price=17.0,
quantity=300,
action=sj.constant.Action.Buy,
price_type=sj.constant.StockPriceType.LMT,
order_type=sj.constant.TFTOrderType.ROD,
order_lot=sj.constant.TFTStockOrderLot.IntradayOdd,
account=api.stock_account
)
而交易资讯Output如下:
contract=Stock(exchange=<Exchange.TSE: 'TSE'>, code='2409', symbol='TSE2409', name='友达', category='26', unit=1000, limit_up=18.75, limit_down=15.35, reference=17.05, update_date='2021/10/13', margin_trading_balance=99098, short_selling_balance=1344, day_trade=<DayTrade.Yes: 'Yes'>) order=Order(action=<Action.Buy: 'Buy'>, price=17.0, quantity=300, id='204dbcd3', seqno='100540', ordno='00000', account=Account(account_type=<AccountType.Stock: 'S'>, person_id='PAPIUSER02', broker_id='9A95', account_id='0504486', signed=True), price_type=<StockPriceType.LMT: 'LMT'>, order_type=<FuturesOrderType.ROD: 'ROD'>, order_lot=<TFTStockOrderLot.IntradayOdd: 'IntradayOdd'>) status=OrderStatus(id='204dbcd3', status=<Status.PendingSubmit: 'PendingSubmit'>, status_code='0', order_datetime=datetime.datetime(2021, 10, 13, 23, 14, 58), deals=[])
取消单我们先前说过了就不再写一次,但我们可以针对盘中零售进行改量的委托设定修改。(盘中零股仅可改量,不可改价)
api.update_order(trade=trade_2409, qty=250)
api.update_status(api.stock_account)
print(trade_2409)
除了昨天文章提到的报价(Quote) Callback,在Order相关的状态变更时,也会有对应的callback可设定。
def place_cb(stat, msg):
print('my_place_callback')
print(stat, msg)
api.set_order_callback(place_cb)
而相关的内容可以直接参考Shioaji官网API文件内容。
我们的Shioaji超入门系列,就到这边告一段落了!希望能快速帮助到相入门的人可以用最短的时间理解。
果然还是有达到自己的承诺(虽然搞的很累),把两个主题都带到了!
明天就是我的铁人赛最後一天了,终於!! 最後一篇,敬请期待罗。(当然,并没有人在期待…)
<<: [Android Studio 30天自我挑战] Timer计时器练习
21 - Draper 上篇 Design Pattern(1) - Decorator 简单的介绍...
合菜玳瑁是有名的北方菜,刚好看到读书会书友外带了好吃的合菜戴帽,把合菜戴帽比喻成蛋皮界的星海罗盘让我...
如果你也有跟着教程做的话,第10节有个练习,可以来跟我交流一下答案,我也不知道我的写法是不是好的,但...
231. Power of Two 今天我们一起挑战leetcode第231题Power of Tw...
在 SMTP Mail 之後,今天要跟大家介绍第二种通知方式 Custom alertscripts...