专栏名称: quantOS
quantOS 量化开源系统的一站式解决方案
目录
相关文章推荐
51好读  ›  专栏  ›  quantOS

数字货币量化交易基础(2)

quantOS  · 公众号  ·  · 2018-04-09 21:06

正文

请到「今天看啥」查看全文


在上篇文章里,我简单介绍了一下如何获取火币网的数字货币行情,得到了很多的读者反馈:

  • 火币在国内被封了,无法访问

  • 太简单了,没有更加深入的介绍数字货币的量化交易

为了响应大家的要求,我趁着假期严肃的研究了一下,先将相关的研究成果分享出来。

本文包括如下几个部分:

  • 我应该关注那些数字货币?

  • 为什么需要数字货币交易所?

  • 数字货币的交易所是怎么运作的?

  • 如何选择交易所?

  • 如何进行数字货币的程序化交易?

  • 有哪些量化交易策略可以使用?

我应该关注哪些数字货币?

这个问题其实非常简单,只需要关注目前最主流的几个就行。

  1. 比特币(BTC)

  2. 莱特币(LTC)

  3. 以太币(ETH)

  4. 比特币现金(BCH)

  5. 瑞波币(XPR)

为什么需要数字货币交易所?

这是一个非常好的问题。数字货币的重大创新中,最核心的一条就是去中心化。即数字货币的运行,不需要一个中心化的组织来管理。

但为了达到去中心化的目的,数字货币也必须要付出一定的代价。这个代价包括:

1、转账确认的速度慢

  • 比特币的安全确认时间大约是1小时

  • 莱特币的安全确认时间大约是10-15分钟

  • 以太币的安全确认时间大约是5-10分钟

这样的速度是没有办法满足很多用户需求的。

2、对用户使用的要求高

要能安全的使用数字货币进行支付和转账,你需要了解好多好多的概念,比如钱包地址、区块、确认、分叉等。 这些都增加了普通用户使用数字货币的复杂度。

数字货币交易所可以部分的解决这些问题,后面我细细讲解。

数字货币的交易所是怎么运作的?

数字货币交易所是一个完全中心化运作的组织,方便用户进行数字货币的交易,是一个交易中介。

交易所提供的服务主要包括:

  1. 资产的管理,包括账户管理,账户下面的资金及数字货币资产管理。

  2. 交易服务,包括订单的管理、交易撮合、交易的结算等。

  3. 资讯服务,包括提供市场的行情、成交信息、订单队列等。

有一些交易所还会提供一些增值的服务,比如数字货币的期货交易,杠杆交易,融资或融币的交易。

交易所提供的交易品种包括:

1、法币与数字货币之间的交易,如:

  • BTC-USD,比特币与美元间的交易

  • BTC-EUR,比特币与欧元间的交易

2、数字货币之间的交易,如:

  • BTC-BCH,比特币与比特币现金之间的交易

  • BTC-LTC,比特币与莱特币之间的交易

对于一个普通用户而言,交易所会为用户创建一个虚拟账户,用户可以

  1. 把资金从银行转账到交易所账户,简称充值。或者将资金从交易所账户转账到银行,简称提现。

  2. 将数字货币从钱包转账到交易所账户,简称充币。或者将数字货币从交易所账户转账到钱包,简称提币。

  3. 在交易所内部,使用交易所账户买卖数字货币。

为什么很多国家要加强对数字货币交易所的监管呢? 道理非常简单,你往交易所转入的都是真金白银,交易所只是给你开了个虚拟账户。交易所全凭着自己的信誉来获得你的信任。 如果交易所突然跑路了,网站打不开了,你的资金和数字货币资产可就都没有了。

在中国为了防范金融风险,直接取缔了所有私营的数字货币交易所,也是非常合理的。

如何选择交易所?

我的选择标准是,选择主流的、交易量大的、流动性好的交易所。

由于国内的交易所已经全部取缔了,因此你只能使用境外交易所提供的服务。推荐几个:

  1. Bitfinex(www.bitfinex.com)

  2. Bitstamp(www.bitstamp.net)

  3. Bittrex(www.bittrex.com)

  4. GDAX(www.gdax.com)

  5. OKCoin(www.OKCoin.com)

如何进行数字货币的程序化交易?

数字货币的交易和股票很类似,交易所也提供非常丰富的市场信息,包括行情、成交信息、订单簿等等。


交易所同时提供API接入,可以直接进行程序化交易。(目前国内的股票交易是禁止API接入的)

我简单梳理了一下,交易所目前都提供两种类型的API接口:

  1. REST风格的API,用于查询市场数据、账户信息和提交交易订单。

  2. WebSocket风格的API,用于推送实时的市场数据、成交信息。

推送接口主要包括:

  • 实时行情ticker

  • 实时订单队列orderbook(eg. top 100bids/asks)

  • 实时订单信息orders

Rest查询和交易接口包括:

  • 实时行情ticker

  • 实时订单队列orderbook

  • 实时成交信息transaction

  • 历史K线信息history candlstick

  • 可交易标的信息symbols

  • 账户余额信息account balance

  • 订单请求BUY/SELL Limit/Market Order

  • 撤销订单Cancel Order

  • 查询订单状态Query Order Status

  • 充值提现WithDraw/Deposit

在接口使用上,建议:

  • 用户可以根据自己的业务场景,使用不同的API,也可以组合起来使用。

  • 各个交易所都会提供python接口,对一般的程序化交易用户,应该能满足要求。如果对性能要求特别高,也可以自己用高级语言写。

  • 如果访问的不是市场数据,需要使用数字签名。交易所会给用户生成相应的API Key,用户要保管好Key。

如果使用python接口,github上有一个开源的项目bitex,对通信做了比较好的封装。

有哪些量化交易策略可以使用?

数字货币的交易是一个很新的东西,具体哪些量化策略可以使用,我觉得是一个见仁见智的问题。

作者猜测:

  1. 传统基于统计的量化策略,由于历史数据的时间太短,不好进行回测,只能实盘边跑边修正。

  2. 目前应该还是数字货币交易的早期,比较高频的策略应该会有好的效果。

  3. 由于信息的不对称、流动性等问题,一些无风险的套利策略,看看有没有有机会。

光说不练假把式,不动手的量化都是耍流氓。下面我们就练一下,看看那些套利策略可能有机会。

策略准备,开发获取交易所实时行情的接口(REST接口)

  1. import urllib

  2. import requests

  3. import hashlib

  4. import hmac

  5. import base64

  6. import json

  7. import datetime

  8. from collections import OrderedDict

  9. import pandas as pd

  10. '''

  11. symbol:

  12. 数字货币美元:btcusd, bchusd, ltcusd, ethusd

  13. 数字货币欧元:btceur, bcheur, ltceur, etheur

  14. 币币交易:bchbtc

  15. 汇率交易:eurusd

  16. '''

  17. class Quote():

  18.    def __init__(self):

  19.        self.askprice  = 0.0

  20.        self.askvolume = 0.0

  21.        self.bidprice  = 0.0

  22.        self.bidvolume = 0.0

  23.        self.open      = 0.0

  24.        self.high      = 0.0

  25.        self.low       = 0.0

  26.        self .close     = 0.0

  27.        self.vol       = 0.0

  28.        self.amount    = 0.0

  29.        self.symbol    = ''

  30.        self.exchange  = ''

  31.        self.time      = ''

  32.    def to_string(self):

  33.        print('ask : %.6f %.8f' % (self.askprice, self.askvolume))

  34.        print( 'bid : %.6f %.8f' % (self.bidprice, self.bidvolume))

  35.        print('open: %.6f high : %.6f' % (self.open, self.high))

  36.        print('low : %.6f close: %.6f' % (self.low, self.close))

  37.        print('vol : %.6f amount: %.6f'% (self.vol, self.amount))

  38.        print(self.time)

  39.        print(self.symbol , self.exchange)

  40. class BaseAgent:

  41.    def __init__(self):

  42.        self.BaseUrl = ''

  43.    # do rest request

  44.    def do_request(self, api, param):

  45.        # prepare request data

  46.        URL  = self.BaseUrl + api

  47.        # request header

  48.        USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) " \

  49.                     "AppleWebKit/537.36 (KHTML, like Gecko) " \

  50.                     "Chrome/57.0.2987.133 Safari/537.36 "

  51.        proxies = { "http": "http://127.0.0.1:1080", "https": "http://127.0.0.1:1080", }      

  52.        # simulate http request

  53.        session = requests.Session()

  54.        session.headers['User-Agent'] = USER_AGENT

  55.        session.headers['Content-Type'] = 'application/x-www-form-urlencoded'

  56.        res = session.get(URL, params=param, proxies=proxies)

  57.        if res.status_code != 200:

  58.            print("query_error, status_code = ", res.status_code)

  59.            return None, res.status_code

  60.        # return http response

  61.        rsp = res.text

  62.        return rsp, ''

  63.     def make_param(self, req_api, req_dict):

  64.        param = OrderedDict()

  65.        param.update(req_dict)

  66.        return param

  67.    '''

  68.    get realtime market quote return df, msg

  69.    '''

  70.    def get_quote(self, symbol):

  71.        pass

  72. class OKCoinAgent(BaseAgent):

  73.     def __init__(self):

  74.        self.BaseUrl          = 'https://www.okcoin.com'

  75.        self.SymbolMap = {

  76.            'btcusd' : 'btc_usd',

  77.            'bchusd' : 'bch_usd',

  78.            'ltcusd' : 'ltc_usd',

  79.            'ethusd' : 'eth_usd'

  80.        }

  81.    # get realtime market quote

  82.     def get_quote(self, symbol):

  83.        req_api  = '/api/v1/ticker.do'

  84.        req_dict = {'symbol' : self.SymbolMap[symbol]}

  85.        param = self.make_param(req_api, req_dict)

  86.        response, msg = self.do_request(req_api, param)

  87.        if response is None:

  88.            return None, msg

  89.        # 载入数据并记录

  90.        rsp_json = json.loads(response)

  91.        if 'error_code' in rsp_json:

  92.            msg = rsp_json['error_code']

  93.            return None, msg

  94.        time = float(rsp_json['date'])

  95.        raw_records = rsp_json['ticker']

  96.        dt = datetime.datetime.fromtimestamp(time)

  97.        quote = Quote()

  98.        quote.time = datetime.datetime.strftime(dt, '%Y-%m-%d %H:%M:%S')

  99.        quote.askprice = float(raw_records['sell'])

  100.        quote.bidprice = float(raw_records['buy'])

  101.        quote.close    = float(raw_records['last'])

  102.        quote.vol      = float(raw_records['vol'])

  103.        quote .high     = float(raw_records['high'])

  104.        quote.low      = float(raw_records['low'])

  105.        quote.symbol   = symbol

  106.        quote.exchange = 'OKCoin'

  107.        return quote, ''

  108. class CoinBaseAgent(BaseAgent):

  109.    def __init__(self):

  110.        self.BaseUrl          = 'https://api.gdax.com'

  111.        self.SymbolMap = {

  112.            'btcusd' : 'BTC-USD',

  113.            'btceur' : 'BTC-EUR',

  114.            'bchusd' : 'BCH-USD',

  115.            'bcheur' : 'BCH-EUR',

  116.            'ltcusd' : 'LTC-USD',

  117.            'ltceur' : 'LTC-EUR',

  118.            'ethusd' : 'ETH-USD',

  119.            'etheur' : 'ETH-EUR'

  120.        }

  121.    # get realtime market quote

  122.    def get_quote(self, symbol):

  123.        req_api  = '/products/%s/ticker' % self.SymbolMap[symbol]

  124.        req_dict = {}

  125.        param = self.make_param(req_api, req_dict)

  126.        response, msg = self.do_request (req_api, param)

  127.        if response is None:

  128.            return None, msg

  129.        raw_records = json.loads(response)

  130.        utcTime = datetime.datetime.strptime(raw_records['time'], '%Y-%m-%dT%H:%M:%S.%fZ')

  131.        localtime = utcTime + datetime.timedelta(hours=8)

  132.        quote = Quote()

  133.        quote .time = datetime.datetime.strftime(localtime, '%Y-%m-%d %H:%M:%S')

  134.        quote.askprice = float(raw_records['ask'])

  135.        quote.bidprice = float(raw_records['bid'])

  136.        quote.close    = float(raw_records['price'])

  137.        quote.vol      = float(raw_records['size'])

  138.        quote.amount   = float(raw_records['volume'])

  139.        quote.symbol   = symbol

  140.        quote.exchange = 'CoinbaseGDAX'

  141.        return quote, ''

  142. class BitstampAgent(BaseAgent):

  143.    def __init__(self):

  144.        self.BaseUrl          = 'https://www.bitstamp.net'

  145.    # get realtime market quote

  146.    def get_quote(self, symbol):

  147.        req_api  = '/api/v2/ticker/%s' % symbol

  148.        req_dict = {}

  149.        param = self.make_param(req_api, req_dict)

  150.        response, msg = self.do_request(req_api, param)

  151.        if response is None:

  152.            return None, msg

  153.        raw_records = json.loads(response)

  154.        localtime = datetime.datetime.fromtimestamp(int(raw_records['timestamp']))

  155.        quote = Quote()

  156.        quote.time = datetime.datetime.strftime(localtime, '%Y-%m-%d %H:%M:%S')

  157.        quote.open     = float(raw_records['open'])

  158.        quote.high     = float(raw_records['high'])

  159.        quote.low      = float(raw_records['low' ])

  160.        quote.askprice = float(raw_records['ask'])

  161.        quote.bidprice = float(raw_records['bid'])

  162.        quote.close    = float(raw_records['last'])

  163.        quote.vol      = float(raw_records['volume'])

  164.        quote.symbol   = symbol

  165.        quote.exchange = 'Bitstamp'

  166.        return quote, ''        

套利策略1:跨交易所间的套利


思路非常简单,交易所之间交易相同的比特币,但价格有比较明显的差距,就可以在交易所之间套利。

  1. 在交易所A低价买入数字货币

  2. 在交易所A提取数字货币

  3. 将数字货币存入交易所B

  4. 将数字货币在交易所B高价卖出

  5. 在交易所B提取资金

这个策略的问题在于:

  1. 交易所提币的时间很长,一般超过1个小时,存在时间上的风险

  2. 提币和提取资金都需要手续费,有些交易所是按照每次收费的,所以金额和数量太小,费用占比会很高。

所以这个策略的改进版是:

  1. 用户手里持有一些数字货币,存入交易所B

  2. 每在交易所B高价卖出一些数字货币后,在交易所A低价买入等量的数字货币。

  3. 交易所B里面的币卖光后,再把交易所A里面的币提取出来,转入交易所B。

这个方法规避了时间风险和手续费的问题,但引入了一个更大的风险:数字货币的持仓风险。

我们来看一看各个交易所的情况:

  1. agent = OKCoinAgent()

  2. quote, msg = agent.get_quote('btcusd')

  3. quote.to_string()

  4. agent = CoinBaseAgent()

  5. quote, msg = agent.get_quote( 'btcusd')

  6. quote.to_string()

  7. agent = BitstampAgent()

  8. quote, msg = agent.get_quote('btcusd')

  9. quote.to_string()

output:

  1. ask : 7498.880000 0.00000000

  2. bid : 7342.810000 0.00000000

  3. open: 0.000000 high : 7950.240000

  4. low : 7382.010000 close: 7499.850000

  5. vol : 92.590000 amount: 0.000000

  6. 2018-04-09 18:56:07

  7. btcusd OKCoin

  8. ask : 6794.050000 0.00000000

  9. bid : 6794.040000 0.00000000

  10. open: 0.000000 high : 0.000000

  11. low : 0.000000 close: 6793.500000

  12. vol : 0.219400 amount: 9875.275370

  13. 2018-04-09 18:55:59

  14. btcusd CoinbaseGDAX

  15. ask : 6796.660000 0.00000000

  16. bid : 6793.220000 0.00000000

  17. open: 7027.260000 high : 7175.830000

  18. low : 6760.000000 close: 6793.220000

  19. vol : 7082.738112 amount: 0.000000

  20. 2018-04-09 18:56:08

  21. btcusd Bitstamp

不难发现:

  • OKCoin上比特币美元的价格比Bitstamp和CoinbaseGDAX都要贵500多美元,因此理论上存在跨交易所套利的可能。

  • Bitstamp和CoinbaseGDAX上的价格就非常接近,不存在套利空间。

为什么OKCoin上会有这么大的差异呢?仔细看很容易发现,OKCoin上的交易量很小,只有其他交易所的百分之一。 因此我判断可能的原因是:

  1. OKCoin是中国人开的交易所,欧美用户参与不多。

  2. OKCoin的量太小,即使套利也没有多少量,但交易所关门的风险很大,所以也就没有大的玩家参与套利。

套利策略2:利用价差和汇率进行套利


利涉及三个品种,步骤如下:

  1. 先用美元买入比特币BTC-USD

  2. 再将比特币用欧元卖出BTC-EUR

  3. 最后将欧元兑换成美元EUR-USD

这个过程涉及三次买卖,因此也需要考虑手续费和交易的冲击成本。

这个策略要想成立,就需要满足 P(BTC-USD) < P(BTC-EUR) * P(EUR-USD) - 手续费

我们可以简单看一下Bitstamp上这三者的关系:

  1. agent = BitstampAgent()

  2. quote, msg = agent.get_quote('btcusd')

  3. quote.to_string()

  4. agent = BitstampAgent()

  5. quote, msg = agent.get_quote('btceur')

  6. quote.to_string()

  7. agent = BitstampAgent()

  8. quote, msg = agent.get_quote('eurusd')

  9. quote .to_string()

output:

  1. ask : 6765.120000 0.00000000

  2. bid : 6759.140000 0.00000000

  3. open: 7027.260000 high : 7175.830000

  4. low : 6708.010000 close: 6759.140000

  5. vol : 7413.376477 amount: 0.000000

  6. 2018-04-09 19:07:46

  7. btcusd Bitstamp

  8. ask : 5524.860000 0.00000000

  9. bid : 5517.900000 0.00000000

  10. open: 5728.500000 high : 5842.000000

  11. low : 5465.000000 close: 5519.430000

  12. vol : 1296.930089 amount: 0.000000

  13. 2018-04-09 19:07:45

  14. btceur Bitstamp

  15. ask : 1.228490 0.00000000

  16. bid : 1.225120 0.00000000

  17. open: 1.226150 high : 1.230660

  18. low : 1.225000 close: 1.228490

  19. vol : 368447.295920 amount: 0.000000

  20. 2018-04-09 19:07:47

  21. eurusd Bitstamp

result:

  • P(BTC-USD) = 6765.12

  • P(BTC-EUR) * P(EUR-USD) = 5517.90 * 1.225120 = 6760.08

这个时候,已经没有套利空间了。

套利策略3:利用币币交易进行套利


利涉及三个品种,步骤如下:

  1. 先用美元买入比特币(BTC-USD)

  2. 再通过币币交易将比特币换成BCH(BCH-BTC)

  3. 最后将BCH卖出成美元BCH-USD

这个过程涉及三次买卖,因此也需要考虑手续费和交易的冲击成本。

这个策略要想成立,就需要满足 P(BTC-USD) < 1 / P(BCH-BTC) * P(BCH-USD) - 手续费

我们可以简单看一下Bitstamp上这三者的关系:

  1. agent = BitstampAgent()

  2. quote, msg = agent.get_quote('btcusd')

  3. quote.to_string()

  4. agent = BitstampAgent()

  5. quote, msg = agent.get_quote('bchbtc')

  6. quote.to_string()

  7. agent = BitstampAgent()

  8. quote, msg = agent.get_quote('bchusd')

  9. quote.to_string()

output:

  1. ask : 6755.120000 0.00000000

  2. bid : 6755.110000 0.00000000

  3. open: 7027.260000 high : 7175.830000

  4. low : 6708.010000 close: 6750.160000

  5. vol : 7507.565418 amount: 0.000000

  6. 2018-04-09 19:15:54

  7. btcusd Bitstamp

  8. ask : 0.094685 0.00000000

  9. bid : 0.094287 0.00000000

  10. open: 0.093190 high : 0.095580

  11. low : 0.092340 close: 0.094315

  12. vol : 272.738682 amount: 0.000000

  13. 2018-04-09 19:15:54

  14. bchbtc Bitstamp

  15. ask : 638.210000 0.00000000

  16. bid : 636.150000 0.00000000

  17. open: 653.830000 high : 680.000000

  18. low : 633.590000 close: 638.430000

  19. vol : 1458.992858 amount: 0.000000

  20. 2018-04-09 19:15:56

  21. bchusd Bitstamp

result:

  • P(BTC-USD) = 6755.12

  • 1 / P(BCH-BTC) * P(BCH-USD) = 1 / 0.094685 * 636.15 = 6718.60

这种方式也没有套利机会

看来在Bitstamp上,比特币交易的流动性足够好,已经没有这种简单的公式套利的机会了。

更高级的量化策略

我觉得最有可能的就是高频策略了。这个需要认真研究订单簿,做细致的微观结构研究。

这已经超出了一个量化基础文章的范畴了,就需要读者你自己去摸索。

总之:炒币有风险,投资需谨慎。预祝大家好运!








请到「今天看啥」查看全文