Day28 - [Shioaji] 超入门!永丰证券程序交易API快速上手 (1)

一晃眼,铁人赛就进入了尾声,先前一直说有时间要来写Shioaji的,总是不能食言。我想就用最後两篇的篇幅快速的导读一下Shioaji的基础入门功能。我发现自己之前每篇单日文章都希望写的丰富一点,因此每天下班後几乎都把时间贡献在铁人写测试、研究与撰文上面,真的是有一点吃不消。而现在希望自己两个主题都有参与到,贪心的结果就是每天弄的压力很大,搞的下班比上班时还累。不过终究还是咬牙撑过来了。

这两天刚好连假+特休假,所以在收尾丰收款线上支付API - 丰收款 的专题系列文时,也边准备打算用最後几篇来把Shioaji作个旋风式介绍。就当作是我的铁人赛的番外篇吧,能写多少算多少,除了希望有机会帮助到别人,自己也一直想研究这个主题。

什麽是Shioaji

Shioaji在我Day1的文章就有介绍过,就不再赘述,总之你对於股票、期货、选择权等的交易,除了使用人为方式透过App也好网站也好的方式下单外,永丰提供了一个给程序使用的API,名为Shioaji,让会写程序的你可以进行更多灵活的运用,自己有独到的交易策略,以及想要严守自己的交易纪律与规则的人,可以透过这个方式帮你圆梦。
(然後迈向财务自由..... 嗯!!癌大主委在讲都没在听,没有什麽财务自由这种事!)

由於永丰官网以及活动页面有提供了几个教学,建议大家都乖乖的先去把课程上完一轮,会比我在这里写清楚的多。
不确定这个活动页面会不会一直都在,还是先帮大家把网址整理出来:
永丰官方Shioaji API内部专业讲师教学:Youtube播放清单

播放清单如下,不过里面的顺序没有编的很正确,上面1~6可以先看。然後下面的7~10还有另4篇,请照影片档名的顺序读。
https://ithelp.ithome.com.tw/upload/images/20211012/20130354t7GQSQMYB6.png

其中有一篇,是针对股票交易谈到比较细的 交易搓合制度等,和程序技术无关,但对於细节运作厘清相当重要。
可以多看个两次:2.逐笔搓合制度、API新增功能及β风控机器人介绍

其他的部份就都和程序比较有关,但必须说一下:

  1. 讲师解释很清楚,但顾及学员可能有些刚接触程序技术,因此谈的深度不深 → 不要期待看完影片你就会懂整套API怎麽使用
  2. 因为是先前永丰有开实体课程直接录制,声音有一点小,而且拍摄後画面看不太清楚
    但可以快速作为一个入门,以及理解API是什麽。

讲师在影片中,有一个网址,帮大家列在下面,讲师一再强调Basic Overview一定要看!
https://sinotrade.github.io/slides/

https://ithelp.ithome.com.tw/upload/images/20211012/20130354HEaZeqD8vy.png

而正式的Shioaji官网文件在这里:https://sinotrade.github.io/
若真的想要透过Shioaji作程序交易的读者,必需详读与实际跟着操作。

然後,很不幸的,因为国际化是我们未来双语国家的目标(咦?),上面的文件都是英文的,学Shioaji也可以顺便练英文,一举双得呀!相信难不倒大家。

来吧!先架设环境

在其实最早开赛前,我就已经先研究一下Shioaji,当时是使用Anaconda配合JupyterNotebook的环境,有先测试过一些程序运作。不过因为先前的丰收款,写了库米狗屋KummyShop的情境案例,使用了PyCharm Community环境 (搭配Django Web框架),想说为了一致性,也在PyCharm上面来写Shioaji的测试验证案例吧。

但万万没想到,一开始就卡住了,花了我好久的时间,一直出现DLL无法正确载入的问题,等一下我会说明,怎麽解决。

开设一个新专案,安装Shioaji套件

在PyCharm中,先开设一个Python新专案。一开始我们通常都会使用PyCharm的venv建一个新的环境,开完专案後,依照永丰金的说明,我们使用Terminal来进行pip安装

> pip install shioaji

安装是成功了没错,目前我安装的版本是:shioaji-0.3.3.dev4

於是,很开心的把先前在Jupyter Notebook测试的code赶紧开一个test.py档案来测试,怎麽样都是过不了。

code不用多,光第一行就错误了。

import shioaji as sj

https://ithelp.ithome.com.tw/upload/images/20211012/20130354EXBjVyYu5r.png

ImportError: DLL load failed while importing solclient: 动态连结程序库 (DLL) 初始化例行程序失败。

怎麽解决DLL初始化问题呢?

後来查了Shioaji相关的讨论区,後来有人提到Shioaji目前支援的Python环境,若在Windows中建议使用Anaconda。如果非Anaconda的虚拟环境会有一些不相容的异常,对,我就遇到了。

所以解决方法是,使用PyCharm时,还是将Python Interpreter由原先的venv环境改成Anaconda的虚拟环境。

https://ithelp.ithome.com.tw/upload/images/20211012/20130354zFkz4Te5vk.png

改为:
https://ithelp.ithome.com.tw/upload/images/20211012/20130354P7j6LNKxBJ.png
注:上面是Python 3.7或3.8并不是造成错误的原因,Shioaji目前撰文时是支援3.6~3.8。

改完後,要记得在Anaconda虚拟环境中确认是否有安装Shioaji,若没有,要再使用pip安装一次。

再执行一次,原先的DLL初始化错误就消失了。

开始来尝试第一支最简单的范例

如果你本身有永丰证券帐户,也申请了API使用凭证,那你可以尝试使用自己的帐号与密码(当然要小心使用,责任需自负)。若没有,你可以使用永丰提供的测试帐号,但无论是哪一种,在资源有限的情况下,在使用上都有连线上限以及与些资料取用的限制,详情请看这里。因此,请於程序login後,记得养成logout的好习惯。

  • 测试帐号:PAPIUSER01 ~ PAPIUSER08
  • 测试密码:2222

STEP1: 登入(Login)

使用Shioaji的第一个步骤,除了import Shioaji的套件外,就要先确认整个程序交易的使用者是谁,这和你使用App要进行下单是一样的。

import shioaji as sj
api = sj.Shioaji()
api.login(
    person_id="YOUR_ID", 
    passwd="YOUR_PASSWORD", 
    contracts_cb=lambda security_type: print(f"{repr(security_type)} fetch done.")
)

如果,你使用的是测试帐号进行模拟(Simulation)用途,请在Shioaji()里加上参数simulation=True

import shioaji as sj

api = sj.Shioaji(simulation=True)
person_id = 'PAPIUSER05'
passwd = '2222'

api.login(person_id=person_id, passwd=passwd)

STEP2: 确认Accounts

而这里的login指的是对於永丰而言的证券用户的登入资讯,而一个永丰的用户底下会开设不同的证券、期货等的交易帐户。因此一个用户底下的股票、期货等,是分开且不同的交易帐户。

因此在登入後,可使用list_accounts()方法取回此用户底下的各种帐户:

accounts = api.list_accounts()
print(accounts)

# Output: [FutureAccount(person_id='PAPIUSER05', broker_id='F002000', account_id='9101663', signed=True, username='PAPIUSER05'), StockAccount(person_id='PAPIUSER05', broker_id='9A95', account_id='0506206', signed=True, username='PAPIUSER05')]

以上资讯可看到这个用户底下有:

  • 一个期货(Future)帐户:account_id='9101663'
  • 一个股票(Stock)帐户:account_id='0506206'

但一个用户是可拥有超过一个以上的证券户,可以列出预设Future或Stock的帐户:

print("Default Stock Account: {}".format(api.stock_account))
print("Default Future Account: {}".format(api.futopt_account))

# Output: Default Stock Account: person_id='PAPIUSER05' broker_id='9A95' account_id='0506206' signed=True username='PAPIUSER05'
# Output: Default Future Account: person_id='PAPIUSER05' broker_id='F002000' account_id='9101663' signed=True username='PAPIUSER05'

其中重要的是signed是指是否已签署证券或期货的API签署书

若你有多个帐户,可以透过下面的指令修改预设帐户:

api.set_default_account(accounts[-1])
print(api.stock_account)

api.set_default_account(accounts[1])
print(api.futopt_account)

STEP3: 重要的交易凭证CA

有关下单的凭证,请参考永丰官网以及相关凭证软件工具下载

基本上,你若成功於Windows电脑下载凭证後,会放置在C:\ekey\底下的目录,中间会有一些路径以及身份证字号等目录就由各位自行确认。档名会是Sinopac.pfx

api.activate_ca(
    ca_path=r'C:\ekey\xxx\xxxxxxxxxx\x\Sinopac.pfx',
    ca_passwd='YOUR_PASSWORD',
    person_id='YOUR_PERSON_ID'
)

STEP4: 查询前先立Contracts

我们的程序想要取得标的的金融资讯,会先需要指定相关的Contract(契约)。透过Shioaji instance物件下Contracts底下又能区分出Stocks以及Futures。

我指若直接将Contracts列出来,如下:

print(api.Contracts)

# Output: Indexs=(OTC, TSE) Stocks=(OES, OTC, TSE) Futures=(BRF, BTF, CAF, CBF, CCF, CDF, CE1, CEF, CFF, CGF, CHF, CJ1, CJF, CKF, CLF, CM1, CMF, CNF, CQF, CRF, CSF, CUF, CWF, CYF, CZF, DC1, DCF, DD1, DDF, DEF, DFF, DGF, DHF, DIF, DJF, DKF, DLF, DN1, DNF, DOF, DP1, DPF, DQF, DSF, DVF, DW1, DWF, DXF, DYF, DZ1, DZF, E4F, EEF, EGF, EHF, EMF, EP1, EPF, ERF, EXF, EYF, EZF, F1F, FBF, FE1, FEF, FF1, FFF, FGF, FKF, FNF, FQF, FRF, FTF, FVF, FWF, FXF, FYF, FZF, G2F, GAF, GCF, GDF, GHF, GIF, GJF, GLF, GMF, GNF, GOF, GRF, GTF, GUF, GWF, GXF, GZF, HAF, HBF, HCF, HHF, HI1, HIF, HLF, HOF, HSF, HYF, IA1, IAF, IHF, IIF, IJF, IMF, IOF, IPF, IQF, IRF, ITF, IXF, IYF, IZF, JBF, JF1, JFF, JMF, JNF, JPF, JSF, JWF, JZF, KAF, KBF, KCF, KDF, KEF, KFF, KGF, KIF, KKF, KLF, KOF, KP1, KPF, KSF, KUF, KWF, LBF, LCF, LEF, LIF, LMF, LO1, LOF, LQF, LRF, LTF, LUF, LV1, LVF, LWF, LXF, LYF, MAF, MBF, MJF, MK1, MKF, MPF, MQF, MVF, MX2, MXF, MYF, NAF, NBF, NCF, NDF, NEF, NGF, NI1, NIF, NJF, NLF, NMF, NOF, NQF, NSF, NUF, NVF, NWF, NXF, NYF, NZF, OAF, OBF, OCF, ODF, OEF, OHF, OJF, OKF, OLF, OMF, OOF, OPF, OQF, ORF, OSF, OTF, OUF, OVF, OWF, OXF, OYF, OZF, PAF, PBF, PCF, PDF, PEF, PFF, PGF, PHF, PIF, PJF, PKF, PL1, PLF, PMF, PNF, POF, PPF, PQF, PRF, PSF, PTF, PUF, PVF, PWF, PXF, PYF, PZF, QAF, QBF, QCF, QDF, QEF, QFF, QGF, QHF, QIF, QJF, QKF, QLF, QM1, QMF, QNF, QOF, QPF, QQF, QRF, QSF, RHF, RTF, SPF, T5F, TGF, TJF, TXF, UDF, UNF, XAF, XBF, XEF, XIF, XJF, ZEF) Options=(CAO, CBO, CCO, CDA, CDO, CEA, CEO, CFO, CGO, CHO, CJO, CKO, CLO, CMO, CNO, CQO, CRO, CSO, CZO, DCO, DEO, DFO, DGO, DHO, DJO, DKO, DLO, DNO, DOO, DPO, DQO, DSO, DVO, DWO, DXO, GIO, GXO, HCO, IJO, LOO, NYO, NZO, OAO, OBO, OCO, OJO, OKO, OOO, OZO, QBO, RHO, RTO, TEO, TFO, TGO, TX2, TXO)

从输出结果可看出有三种Contracts:

  • Indexes
  • Stocks
  • Futures

Indexes指的是大盘的指数,TSE是有关上市大型股的台湾加权指数,而OTC则为有关中小型股的柜买指数
Stocks指的就是股票,TSE指的就是在证交所上市的股票,而OTC是在柜买市场上柜的股票,而OES,老实说我不确定是不是兴柜市场(通常一般兴柜会是ROTC),这个如果有了解的朋友再麻烦指正。

找档股票或期货的基本资讯来看看

Stocks
我们就找一档和永丰有关的ETF股票(写永丰专题找元X的0050像话吗! 其实也不是不行啦 哈),永丰台湾加权的股票代号为006204,所以要取得这档股票的Contract就是用以下的取得方法:
api.Contracts.Stocks.TSE.TSE006204

stk_006204 = api.Contracts.Stocks.TSE.TSE006204
print(stk_006204)

# Output: exchange=<Exchange.TSE: 'TSE'> code='006204' symbol='TSE006204' name='永丰台湾加权' category='00' unit=1000 limit_up=97.65 limit_down=79.95 reference=88.8 update_date='2021/10/12' margin_trading_balance=22893 day_trade=<DayTrade.Yes: 'Yes'>

其中的参考价reference为前一日收盘价,以此例为88.8元。

文件中说明的各属性,请自行参考

exchange (Exchange): Attributes of industry.
    {OES, OTC, TSE ...etc}
code (str): Id.
symbol (str): Symbol.
name (str): Name.
category (str): Category.
limit_up (float): Limit up.
limit_down (float): Limit down.
reference (float): Reference price.
update_date (str): Update date.
margin_trading_balance (int): Margin trading balance.
short_selling_balance (int): Short selling balance.
day_trade (DayTrade): Day trade.
    {Yes, No, OnlyBuy}

Futures
接着,我们来看看期货资讯。

print("TXF: {}".format(api.Contracts.Futures.TXF))

#Output: TXF: TXF(TXF202203, TXF202206, TXF202209, TXF202111, TXF202112, TXF202110, TXFR1, TXFR2)

若我们将期货的Contracts底下的TXF (大台指) 列出来,看有哪些Contract可操作。
以目前撰文时间为2021年10月中来看,会看到远月期货:TXF202203、TXF202206、TXF202209
以及近三个月期货:TXF202110、TXF202111、TXF202112
还有所价的近一、近二:TXFR1、TXFR2

我们可以列出观察一下内容:

print("台指期近一: {}".format(api.Contracts.Futures.TXF.TXFR1))
print("台指期近二: {}".format(api.Contracts.Futures.TXF.TXFR2))
print("台指期202112: {}".format(api.Contracts.Futures.TXF.TXF202112))
print("台指期202206: {}".format(api.Contracts.Futures.TXF.TXF202206))

# Output: 台指期近一: code='TXFR1' symbol='TXFR1' name='台股期货近月' category='TXF' delivery_month='202110' delivery_date='2021/10/20' underlying_kind='I' unit=1 limit_up=18287.0 limit_down=14963.0 reference=16625.0 update_date='2021/10/12'
# Output: 台指期近二: code='TXFR2' symbol='TXFR2' name='台股期货次月' category='TXF' delivery_month='202111' delivery_date='2021/11/17' underlying_kind='I' unit=1 limit_up=18256.0 limit_down=14938.0 reference=16597.0 update_date='2021/10/12'
# Output: 台指期202112: code='TXFL1' symbol='TXF202112' name='台股期货12' category='TXF' delivery_month='202112' delivery_date='2021/12/15' underlying_kind='I' unit=1 limit_up=18225.0 limit_down=14913.0 reference=16569.0 update_date='2021/10/12'
# Output: 台指期202206: code='TXFF2' symbol='TXF202206' name='台股期货06' category='TXF' delivery_month='202206' delivery_date='2022/06/15' underlying_kind='I' unit=1 limit_up=17974.0 limit_down=14706.0 reference=16340.0 update_date='2021/10/12'

上面所取得的资讯,其中delivery_date指的则是结算日期,为当月份的第三个星期三

订阅(Subscribe)期货来看看即时报价(Quote)资料

台股只有白天上时间有即时交易资料,下班後如果要测试验证即时的订阅资料,只能看期货的夜盘资料。

期货的一般交易时间,为8:40~13:45,而盘後交易时间为15:00~5:00(凌晨)。
刚刚介绍完Contracts後,下一个动作可以试着将一个Contract进行**报价(Quote)**的订阅,并指定我们要订阅的是Ticks每档成交资料,或是BidAsk委买委卖的委托资料。

下面只列出较重要的程序段:

import time
from shioaji import TickFOPv1, BidAskFOPv1, Exchange

@api.on_tick_fop_v1()
def quote_callback(exchange: Exchange, tick: TickFOPv1):
    print(f"Exchange: {exchange}, Tick: {tick}")


@api.on_bidask_fop_v1()
def quote_callback(exchange: Exchange, bidask: BidAskFOPv1):
    print(f"Exchange: {exchange}, BidAsk: {bidask}")


@api.quote.on_event
def event_callback(resp_code: int, event_code: int, info: str, event: str):
    print(f'Response code: {resp_code} | Event code: {event_code} | Event: {event}')

### 使用decorator方式,以下两行都不需要再定义了
# api.quote.set_callback(quote_callback)
# api.quote.set_event_callback(event_callback)

ftu_txf2110 = api.Contracts.Futures.TXF.TXF202110

api.quote.subscribe(
    ftu_txf2110,
    quote_type=sj.constant.QuoteType.Tick,
    version=sj.constant.QuoteVersion.v1
)

api.quote.subscribe(
    ftu_txf2110,
    quote_type=sj.constant.QuoteType.BidAsk,
    version=sj.constant.QuoteVersion.v1
)

i = 0
while i < 10:
    time.sleep(1)
    i += 1



api.logout()

这边有几个需要特别说明的程序,首先我们先了解一下quote和subscribe。我们想要针对一个Contracts去取得其即时报价资料,则需使用api.quote.subscribe()告知我们要订阅报价讯息,而参数则需明白指名要订阅的:

  1. 哪一个Contract (可事先放入变数)
  2. 报价型态:可以是每档成交Ticks,或者是委买Bid、委卖Ask的即时资讯。
  3. 报价格式版本:目前官网有v0v1两种版本,预设是v0

官方格式说明如下,如果是股票的话还可指定是否要特别看**盘中零股 (intraday_odd)**的报价

quote_type: tick price or bid/ask price to subscribe.
    {'tick', 'bidask'}
intraday_odd: 盘中零股
    {True, False}
version: version of quote format.
    {'v1', 'v0'}

所以以我们的例子,我们选定了contract为大台指2021年10月份,然後订阅了他的Tick以及BidAsk的报价。(大人任性地都要!小孩才作选择呀~)

接着要说明的是,Shioaji采用的方式是订阅时,需要指定其callback回呼函式。分别以event和实际quote报价结果都需要接收callback函式。

而以官网写有提供的方式,可采用python function decorator @api.on_tick_fop_v1()@api.on_bidask_fop_v1()以及@api.quote.on_event来直接定义这个函式就是作为报价的回呼函式,相较传统作法,就无需再额外定义callback的设定:

from shioaji import TickFOPv1, BidAskFOPv1, Exchange

@api.on_tick_fop_v1()
def quote_callback(exchange: Exchange, tick: TickFOPv1):
    print(f"Exchange: {exchange}, Tick: {tick}")


@api.on_bidask_fop_v1()
def quote_callback(exchange: Exchange, bidask: BidAskFOPv1):
    print(f"Exchange: {exchange}, BidAsk: {bidask}")


@api.quote.on_event
def event_callback(resp_code: int, event_code: int, info: str, event: str):
    print(f'Response code: {resp_code} | Event code: {event_code} | Event: {event}')

记得使用前,要引入TickFOPv1, BidAskFOPv1, Exchange

如此一来,只要有即时资料进来,就会自动跑进那几个callback functions。
我们程序码里,简单每秒钟睡一下,让程序可以跑个10秒钟再进行logout。

所以我们就可以看到以下的台指期202110的即时Tick与BidAsk资料疯狂涌入!好开心呀!
篇幅问题,我们就简单列个几笔:

Response code: 200 | Event code: 16 | Event: Subscribe or Unsubscribe ok
Response code: 200 | Event code: 16 | Event: Subscribe or Unsubscribe ok

Exchange: TAIFEX, BidAsk: BidAsk(code='TXFJ1', datetime=datetime.datetime(2021, 10, 12, 23, 42, 18, 759000), bid_total_vol=63, ask_total_vol=71, bid_price=[Decimal('16496'), Decimal('16495'), Decimal('16494'), Decimal('16493'), Decimal('16492')], bid_volume=[1, 6, 9, 25, 22], diff_bid_vol=[0, -1, 0, 0, 0], ask_price=[Decimal('16497'), Decimal('16498'), Decimal('16499'), Decimal('16500'), Decimal('16501')], ask_volume=[4, 16, 15, 13, 23], diff_ask_vol=[0, 0, 1, 0, 0], first_derived_bid_price=Decimal('0'), first_derived_ask_price=Decimal('16501'), first_derived_bid_vol=0, first_derived_ask_vol=1, underlying_price=Decimal('16462.84'), simtrade=0)

Exchange: TAIFEX, Tick: Tick(code='TXFJ1', datetime=datetime.datetime(2021, 10, 12, 23, 42, 18, 770000), open=Decimal('16389'), underlying_price=Decimal('16462.84'), bid_side_total_vol=22359, ask_side_total_vol=23121, avg_price=Decimal('16500.63133'), close=Decimal('16496'), high=Decimal('16563'), low=Decimal('16378'), amount=Decimal('16496'), total_amount=Decimal('583231315'), volume=1, total_volume=35346, tick_type=2, chg_type=2, price_chg=Decimal('77'), pct_chg=Decimal('0.468969'), simtrade=0)

Exchange: TAIFEX, BidAsk: BidAsk(code='TXFJ1', datetime=datetime.datetime(2021, 10, 12, 23, 42, 18, 884000), bid_total_vol=72, ask_total_vol=73, bid_price=[Decimal('16495'), Decimal('16494'), Decimal('16493'), Decimal('16492'), Decimal('16491')], bid_volume=[3, 7, 15, 31, 16], diff_bid_vol=[-3, -2, -10, 9, 0], ask_price=[Decimal('16497'), Decimal('16498'), Decimal('16499'), Decimal('16500'), Decimal('16501')], ask_volume=[6, 16, 15, 13, 23], diff_ask_vol=[2, 0, 0, 0, 0], first_derived_bid_price=Decimal('0'), first_derived_ask_price=Decimal('16501'), first_derived_bid_vol=0, first_derived_ask_vol=1, underlying_price=Decimal('16462.84'), simtrade=0)

Exchange: TAIFEX, BidAsk: BidAsk(code='TXFJ1', datetime=datetime.datetime(2021, 10, 12, 23, 42, 19, 9000), bid_total_vol=61, ask_total_vol=72, bid_price=[Decimal('16496'), Decimal('16495'), Decimal('16494'), Decimal('16493'), Decimal('16492')], bid_volume=[2, 4, 8, 25, 22], diff_bid_vol=[1, 1, 1, 10, -9], ask_price=[Decimal('16497'), Decimal('16498'), Decimal('16499'), Decimal('16500'), Decimal('16501')], ask_volume=[5, 16, 15, 13, 23], diff_ask_vol=[-1, 0, 0, 0, 0], first_derived_bid_price=Decimal('0'), first_derived_ask_price=Decimal('16501'), first_derived_bid_vol=0, first_derived_ask_vol=1, underlying_price=Decimal('16462.84'), simtrade=0)

顺带一提的是,在永丰讲师课程中有提到,目前操作上可允许的订阅数为100则,像上面我们刚刚的例子,即使是同一个期货标的,订阅了Tick和BidAsk的报价,就用掉了2则订阅数。

所以要作好订阅的资源管理,若有不需使用的订阅则可用unsubscribe()进行解除订阅。

以上先以期货作为例子,股票的结果相信是差不多的,但上班时间没办法操作,所以先以期货来作代表!

补充一下

说明一下,目前永丰的文件,如同前面有提到一个Basic Overview的讲师叮咛必看内容,由於其版本较旧,因此里面有一些用法和最新的Shioaji的规格文件会稍有不同,请以最新的规格内容来撰写较恰当。

另外,如果你要订阅即时报价,例如,本月份是202110,请使用TXF202110;虽然TXFR1台指期近一也是202110 (2021年10月),但订阅近一这个是没办法取得即时报价的。


<<:  [Day 28] Android Studio 七日陨石开发:把 tflite 模型放进 app

>>:  【Day 28】NumPy (5):sum(), power(), transpose()

Day 33 | 常见 Livewire 问题:解决 Livewire.on() 没有作用的问题

这个问题其实在 Day8 的文章有稍微提到过,但大多数人看文件时都大致看一下而会忽略一些小细节,包含...

[Day11] 介面篇 - 显示血条

在RPG Maker里面 已经有一个原生写好的Class可以做出血调了喔 名称叫做Sprite_Ga...

Day 21 | 状态管理套件 MobX - 到底什麽是状态管理

状态管理? 在介绍 MobX 以前我想先来说一下什麽是「状态管理」 究竟为什麽我们需要「状态管理」,...

Dungeon Mizarka 005

UI版面配置 几近年的FPDC游戏,单角色的控制多以First Preson Shooter玩法为主...

初学者跪着学JavaScript Day13 : 物件加字串?物件加物件?

想不到吧~加法还可以写第二篇 延续昨天相加:{}+{},{}+[],[]+{},[]+[] 到底是什...