FMZ研究平台Python入门指南

Author: 小草, Created: 2019-10-11 17:27:15, Updated: 2020-04-16 12:04:51

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库

具体学习网上的资源很多,本教程不会详细介绍:

  • 科学计算:pandas,numpy
  • 画图:matplotlib,seaborn
  • 机器学习与统计:scipy,statsmodels,scikit-learn
  • 金融相关:arch,talib

回测环境

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>


Related

More

小草 /upload/asset/29cd534a29206ac4bbd.png

小草 /upload/asset/2e6eb68de35b266eaf1.png

小草 /upload/asset/1493b4d31638504eb04.dat