FMZ研究平台Python入门指南
FMZ最新加入了流行的jupyter notebook,帮助用户熟悉平台API以及进行策略研究,支持
Python3
C++11/17
以及Javascript
的学习环境。本教程将简单介绍如何使用。jupyter入口
<img src=“https://www.fmz.com/upload/asset/2e6eb68de35b266eaf1.png” />
jupyter使用
jupyter将代码分成单元块,按住 shift+enter 可单独执行光标所在的单元格代码,程序的全局变量也会保存,不用每次都从头执行,非常方便调试。点击工具栏的加号可以添加单元格。jupyter功能强大灵活,具体使用可根据帮助自行摸索。采用Docker技术隔离,资源独立、安全性更高、性能更好。注意:不支持联网。
支持数据
商品期货:所有品种,格式如MA910,指数合约以000结尾,如MA000,主力连续合约以888结尾,如MA888。
数字货币现货:支持OKcoin和Bitfinex常见币种现货,如BTC_USD_OKCOIN、LTC_USD_OKCOIN、BTC_USD_BITFINEX、LTC_USD_BITFINEX、ETH_USD_BITFINEX
数字货币期货和永续合约:OKEX期货:BTC_FUTURES_THIS_WEEK_OKEX、BTC_FUTURES_NEXT_WEEK_OKEX、BTC_FUTURES_QUARTER_OKEX、LTC_FUTURES_THIS_WEEK_OKEX、LTC_FUTURES_NEXT_WEEK_OKEX、LTC_FUTURES_QUARTER_OKEX。BitMEX永续: XBTUSD_FUTURES_BITMEX。
具体参考FMZ的行情工具:https://www.quantinfo.com/Tools/View/4/chart.html 。也可以在回测界面添加交易所时看到支持的数据。
支持的常用的Python库
具体学习网上的资源很多,本教程不会详细介绍:
Python回测需要安装fmz包,研究平台已经自带。如果自己在本地使用,可参考 https://github.com/fmzquant/backtest_python 。
回测设置和初始化代码如下个单元格,回测参数以注释的形式放在代码开头,会自动识别。具体格式可在FMZ策略编辑界面设置好参数后,点击保存参数,复制即可。如果默认参数没有修改不会展示。
''' start: 2019-08-28 00:00:00 end: 2019-09-26 00:00:00 period: 1h exchanges: [{"eid":"OKEX","currency":"BTC_USDT","stocks":0}] ''' #上面注释是回测设置 from fmz import * # 导入所有FMZ函数 task = VCtx(__doc__) # 初始化
经过初始化后,直接在单元格中就可以正常调用FMZ所有回测可用的API了,首先,先获取一下账户信息。
exchange.GetAccount()
{'Balance': 20000.0, 'FrozenBalance': 0.0, 'Stocks': 0.0, 'FrozenStocks': 0.0}
此时处于回测环境,时间点还在设置的起始时间点,2019-08-28 00:00:00。可以用_D()获取时间字符串,Unix()获取时间戳:
print(_D()) print(Unix())
2019-08-28 00:00:00 1566950400
此时可以获取ticker,K线等行情数据。模拟级回测无法获取真实的深度和成交历史,具体回测的细节参考:https://www.fmz.com/digest-topic/4009 以及FMZ文档。
ticker = exchange.GetTicker() print(ticker) print('最新成交价:', ticker['Last'])
{'Time': 1566950400000, 'High': 10185.20000001, 'Low': 10185.19999999, 'Sell': 10185.20000001, 'Buy': 10185.19999999, 'Last': 10185.2, 'Volume': 0.0, 'OpenInterest': 0.0} 最新成交价: 10185.2
再一次获取同样的行情接口,回测时间点会自动前移,不需要Sleep(和实盘不同)。
ticker = exchange.GetTicker() print(ticker) print('最新成交价:', ticker['Last']) print('当前时间 :', _D())
{'Time': 1566950460000, 'High': 10185.20000002, 'Low': 10185.2, 'Sell': 10185.20000002, 'Buy': 10185.2, 'Last': 10185.20000001, 'Volume': 15.4926, 'OpenInterest': 0.0} 最新成交价: 10185.20000001 当前时间 : 2019-08-28 00:01:00
除了重复获取行情外,Sleep也会推动回测时间点移动。
print(_D()) Sleep(10*60*1000) #休眠 单位ms print(_D())
2019-08-28 00:31:02 2019-08-28 00:41:02
# K线只可获取当前回测时间点之前的数据 records = exchange.GetRecords() print('历史K线长度:', len(records)) print('最新K线: ', records[len(records)-1]) print('最新K线时间: ', _D(records[len(records)-1]['Time']/1000))
历史K线长度: 201 最新K线: {'Time': 1566950400000, 'Open': 10185.2, 'High': 10186.3, 'Low': 10148.2, 'Close': 10159.23333334, 'Volume': 1754.5989999999997} 最新K线时间: 2019-08-28 00:00:00
获取了行情,可以测试一下交易。撮合系统会根据实际情况撮合,不能成交会出现挂单。
# 测试一下未成交订单情况 id = exchange.Buy(ticker['Last']-10,0.1) print(exchange.GetAccount()) print(exchange.GetOrder(id)) exchange.CancelOrder(id) print(exchange.GetAccount())
{'Balance': 18981.66598399, 'FrozenBalance': 1018.334016, 'Stocks': 0.0, 'FrozenStocks': 0.0} {'Id': 1, 'Price': 10175.20000001, 'Amount': 0.1, 'DealAmount': 0.0, 'AvgPrice': 0.0, 'Type': 0, 'Offset': 0, 'Status': 0, 'ContractType': b'BTC_USDT_OKEX'} {'Balance': 20000.0, 'FrozenBalance': 0.0, 'Stocks': 0.0, 'FrozenStocks': 0.0}
# 测试一下订单成交的情况 id = exchange.Buy(ticker['Last']+10,0.1) print(exchange.GetAccount()) print(exchange.GetOrder(id)) print(exchange.GetAccount())
{'Balance': 18980.66518399, 'FrozenBalance': 0.0, 'Stocks': 0.1, 'FrozenStocks': 0.0} {'Id': 2, 'Price': 10195.20000001, 'Amount': 0.1, 'DealAmount': 0.1, 'AvgPrice': 10185.20000002, 'Type': 0, 'Offset': 0, 'Status': 1, 'ContractType': b'BTC_USDT_OKEX'} {'Balance': 18980.66518399, 'FrozenBalance': 0.0, 'Stocks': 0.1, 'FrozenStocks': 0.0}
简单写一个每10小时下一次单的定投策略。
def main(): ticker = exchange.GetTicker() exchange.Buy(ticker['Last']+10,0.1) Sleep(10*60*60*1000)
回测结束后,调用exchange接口会报EOF错误,一般需要在死循环里用try…except容错来写策略,为了调试方便,我们可以不用等到回测结束。
task = VCtx(__doc__) # 重新初始化 while _D()< '2019-09-25 00:00:00': main()
# 回测结束后的账户信息 exchange.GetAccount()
{'Balance': 20000.0, 'FrozenBalance': 0.0, 'Stocks': 0.0, 'FrozenStocks': 0.0}
调用task.Join()将结束回测任务,返回净值数据。Join参数不传True指返回原始未分析的回测结果,结束后不能再调用交易或行情相关函数。
ret = task.Join(True) ret
close balance stocks net 2019-08-28 08:00:00+08:00 10185.2 20000.000000 0.0 20000.000000 2019-08-28 09:00:00+08:00 10150.1 18980.665184 0.1 19995.675184 2019-08-28 10:00:00+08:00 10150.1 18980.665184 0.1 19995.675184 2019-08-28 11:00:00+08:00 10150.1 18980.665184 0.1 19995.675184 2019-08-28 12:00:00+08:00 10150.1 18980.665184 0.1 19995.675184 ... ... ... ... ... 2019-09-26 04:00:00+08:00 8350.1 131.177648 2.0 16831.377648 2019-09-26 05:00:00+08:00 8492.5 131.177648 2.0 17116.177648 2019-09-26 06:00:00+08:00 8566.8 131.177648 2.0 17264.777648 2019-09-26 07:00:00+08:00 8432.3 131.177648 2.0 16995.777648 2019-09-26 08:00:00+08:00 8430.9 131.177648 2.0 16992.977648 [697 rows x 4 columns]
获取历史数据
在回测环境中exchange.GetRecords()只能获取回测时间点之前的历史数据,如果想要完整获取一段时间的历史K线数据用于,需要用到 get_bars 函数。返回的是pandas的dataframe数据结构。具体合约名称参数参考开头介绍。
kline = get_bars('BTC_USD_BITFINEX', '1h', start='2019-05-01', end='2019-10-01') kline
open high low close volume 2019-05-01 08:00:00+08:00 5599.523 5655.000 5589.7200 5620.000 540.429200 2019-05-01 09:00:00+08:00 5620.411 5633.200 5615.5977 5619.400 99.421220 2019-05-01 10:00:00+08:00 5619.370 5643.000 5619.2583 5620.600 374.486400 2019-05-01 11:00:00+08:00 5622.300 5629.700 5591.9000 5619.100 280.237520 2019-05-01 12:00:00+08:00 5619.100 5625.100 5615.7000 5624.900 234.429860 ... ... ... ... ... ... 2019-10-01 04:00:00+08:00 8312.000 8312.100 8186.0000 8260.196 369.500460 2019-10-01 05:00:00+08:00 8260.100 8317.300 8213.0000 8284.259 212.844500 2019-10-01 06:00:00+08:00 8284.200 8328.138 8252.5000 8270.200 92.114075 2019-10-01 07:00:00+08:00 8270.200 8339.800 8270.2000 8331.135 122.889496 2019-10-01 08:00:00+08:00 8337.348 8357.400 8235.5040 8246.500 182.973080 [3642 rows x 5 columns]
# 调用收盘价 kline.close
2019-05-01 08:00:00+08:00 5620.000 2019-05-01 09:00:00+08:00 5619.400 2019-05-01 10:00:00+08:00 5620.600 2019-05-01 11:00:00+08:00 5619.100 2019-05-01 12:00:00+08:00 5624.900 ... 2019-10-01 04:00:00+08:00 8260.196 2019-10-01 05:00:00+08:00 8284.259 2019-10-01 06:00:00+08:00 8270.200 2019-10-01 07:00:00+08:00 8331.135 2019-10-01 08:00:00+08:00 8246.500 Name: close, Length: 3642, dtype: float64
# 简单一行代码计算MA kline['MA'] = kline.close.rolling(50).mean()
指标计算
支持talib,调用方法如下:
import talib kline['EMA'] = talib.EMA(kline.close,50)
画图
画图可用matplotlib,seaborn等库,这里只做简单演示。
import matplotlib.pyplot as plt
fig = plt.subplots(figsize=(16,8)) plt.plot(kline.close,'b') plt.plot(kline.EMA,'r') plt.grid(True)
<Figure size 1152x576 with 1 Axes>
# 季度和当周差价 this_week = get_bars('BTC_FUTURES_THIS_WEEK_OKEX', '5m', start='2019-10-05', end='2019-10-10') quarter = get_bars('BTC_FUTURES_QUARTER_OKEX', '5m', start='2019-10-05', end='2019-10-10')
diff = quarter.close - this_week.close
fig = plt.subplots(figsize=(16,8)) plt.plot(diff,'b') plt.grid(True)
<Figure size 1152x576 with 1 Axes>
小草 /upload/asset/29cd534a29206ac4bbd.png
小草 /upload/asset/2e6eb68de35b266eaf1.png
小草 /upload/asset/1493b4d31638504eb04.dat