有感兴趣量化技术,特别是大数据、人工智能在量化中的应用的童鞋们可以关注我的公众号:
馨视野(datahomex):
最近朋友圈铺天盖地全是数币,之前都是谈币色变,现在是不谈个币就表明你是非人类。
数据君好好研究了一下币圈交易,发现币圈量化大有可为啊(诡异一笑),币圈真是纯数字,天空中飘的都是一排排数字。
今天开始数据君就带大家开启量化技术的大门,本系列文章会介绍币圈交易数据的获取,量化策略的开发、评估,以及如何在量化交易中应用人工智能技术等等。
本篇主要介绍:
1、如何获取自己开发程序需要的交易平台的APP KEY
2、实时获取交易平台K线数据
3、如何通过平台接口实现程序化的下单操作
一、APP KEY 获取
数据君以火币平台举例,其他平台操作都大同小异。
第一步、注册平台账户(直接省略,太简单直白了)
注册地址:https://www.huobi.li/zh-cn/register/ ,支持手机或邮箱注册,量化君就直接手机注册了
第二步、创建API KEY
用上面注册的账号登陆成功后,点击右上角个人头像,选择API管理菜单(如图),进入API创建页面
在创建API页面需要关注的是权限设置和绑定IP端操作(如下图):
-
安全起见,权限只选读取和交易,限制程序自动提币的功能
-
量化君只是测试用,IP段只设置本机地址,正式上线的程序一般都会部署在云上(比如阿里云),这里可以填写阿里云的对外地址
点击创建按钮后,会弹出消息框,上面的Access key 和 Secret Key 一定要记住,后续程序化交易这个就是你账号的凭证,当然数据君这里也提醒一下:
千万不能泄露这2个key!
千万不能泄露这2个key!
千万不能泄露这2个key!
到这里万里长征第一步完成了
二、K线数据获取
完成注册账号和APP key获取后,就可以通过平台提供的API 接口获取实时历史交易数据,也可以通过接口实现下单交易等操作,数据君个人认为平台提供的接口还是很详细规范,地址如下:
https://huobiapi.github.io/docs/spot/v1/cn/#185368440e
今天数据君先介绍历史K线数据的获取和保存,数据君一直习惯用python,所以这次也是使用python调用平台的 API接口,大家要有一点python基础(当然其他语言操作基本一样)
先看下平台提供的接口文档里的说明:
火币提供的API接口都是Restful接口,对于有点开发经验的童鞋使用起来都非常友好,这是用python封装的通用的接口调用函数,如果是用python的童鞋可以直接拿去用,当然key需要改成自己的,代码如下:
# -*- coding: utf-8 -*-
import base64import datetime
import hashlib
import hmac
import json
import urllib
import urllib.parse
import urllib.request
import requests
# 此处填写APIKEY
ACCESS_KEY = "***********" //填写自己的key
SECRET_KEY = "***********" //填写自己的key
# API 请求地址
TRADE_URL = "https://api.huobipro.com"
# 首次运行可通过get_accounts()获取acct_id,然后直接赋值,减少重复获取。
ACCOUNT_ID = None
#'Timestamp': '2017-06-02T06:13:49'
def http_get_request(url, params, add_to_headers=None):
headers = {
"Content-type": "application/x-www-form-urlencoded",
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
}
if add_to_headers:
headers.update(add_to_headers)
postdata = urllib.parse.urlencode(params)
response = requests.get(url, postdata, headers=headers, timeout=5)
try:
if response.status_code == 200:
return response.json()
else:
return
except BaseException as e:
print("httpGet failed, detail is:%s,%s" %(response.text,e))
return
def http_post_request(url, params, add_to_headers=None):
headers = {
"Accept": "application/json",
'Content-Type': 'application/json'
}
if add_to_headers:
headers.update(add_to_headers)
postdata = json.dumps(params)
response = requests.post(url, postdata, headers=headers, timeout=10)
try:
if response.status_code == 200:
return response.json()
else:
return
except BaseException as e:
print("httpPost failed, detail is:%s,%s" %(response.text,e))
return
def api_key_get(params, request_path):
method = 'GET'
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
params.update({'AccessKeyId': ACCESS_KEY,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp})
host_url = TRADE_URL
host_name = urllib.parse.urlparse(host_url).hostname
host_name = host_name.lower()
params['Signature'] = createSign(params, method, host_name, request_path, SECRET_KEY)
url = host_url + request_path
return http_get_request(url, params)
def api_key_post(params, request_path):
method = 'POST'
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
params_to_sign = {'AccessKeyId': ACCESS_KEY,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp}
host_url = TRADE_URL
host_name = urllib.parse.urlparse(host_url).hostname
host_name = host_name.lower()
params_to_sign['Signature'] = createSign(params_to_sign, method, host_name, request_path, SECRET_KEY)
url = host_url + request_path + '?' + urllib.parse.urlencode(params_to_sign)
return http_post_request(url, params)
def createSign(pParams, method, host_url, request_path, secret_key):
sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False)
encode_params = urllib.parse.urlencode(sorted_params)
payload = [method, host_url, request_path, encode_params]
payload = '\n'.join(payload)
payload = payload.encode(encoding='UTF8')
secret_key = secret_key.encode(encoding='UTF8')
digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest)
signature = signature.decode()
return signature
调用/market/history/kline接口,获取历史数据,代码如下:
# 获取KLine
def get_kline(symbol, period, size=150):
"""
:param symbol
:param period: 可选值:{1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year }
:param size: 可选值:[1,2000]
:return:
"""
params = {'symbol': symbol,
'period': period,
'size': size}
url = MARKET_URL + '/market/history/kline'
return http_get_request(url, params)
数据君以获取dogeusdt交易对、5分钟级别的K线数据为例,单次最多可以获取2000条记录:
jsonData = get_kline(symbol='dogeusdt',period='5min',size=2000)
df=parse_json(jsonData.get('data'))
火币restful接口不能按指定时间段获取,如需要按指定时间段获取,需要用websocket API接口,数据君考虑后面测试评估量化策略,所以获取的数据会做持久化保留到本地数据库,详细保留的数据如下图:
看到这里的童鞋,离长征成功不远了~~~
三、实现程序化下单
激动人心的时刻来了,数据君来实现一次程序化的下单,平台的下单接口如下图,
下单接口可以进行买入和卖出的操作,参数众多,说明如下:
参数名称 | 是否必需 | 默认值 | 描述 |
account-id | TRUE | NA | 账户 ID,取值参考 GET /v1/account/accounts。现货交易使用 ‘spot’ 账户的 account-id;逐仓杠杆交易,请使用 ‘margin’ 账户的 account-id;全仓杠杆交易,请使用 ‘super-margin’ 账户的 account-id |
symbol | TRUE | NA | 交易对,即btcusdt, ethbtc...(取值参考GET /v1/common/symbols) |
type | TRUE | NA | 订单类型,包括buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker(说明见下文), buy-stop-limit, sell-stop-limit, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok |
amount | TRUE | NA | 订单交易量(市价买单为订单交易额) |
price | FALSE | NA | 订单价格(对市价单无效) |
source | FALSE | spot-api | 现货交易填写“spot-api”,逐仓杠杆交易填写“margin-api”,全仓杠杆交易填写“super-margin-api”, C2C杠杆交易填写"c2c-margin-api" |
client-order-id | FALSE | NA | 用户自编订单号(最大长度64个字符,须在24小时内保持唯一性) |
stop-price | FALSE | NA | 止盈止损订单触发价格 |
operator | FALSE | NA | 止盈止损订单触发价运算符 gte – greater than and equal (>=), lte – less than and equal (<=) |
下单接口调用代码如下:
//获取账户信息的接口
//通常在程序启动时候初始化一次就行
def get_accounts():
"""
:return:
"""
path = "/v1/account/accounts"
params = {}
return api_key_get(params, path)
def send_order(amount, source, symbol, _type, price=0):
"""
:param amount:
:param source: 如果使用借贷资产交易,请在下单接口,请求参数source中填写'margin-api' 现货交易填写“spot-api”,逐仓杠杆交易填写“margin-api”,全仓杠杆交易填写“super-margin-api”, C2C杠杆交易填写"c2c-margin-api"
:param symbol:
:param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖}
:param price:
:return:
"""
try:
accounts = get_accounts()
acct_id = accounts['data'][0]['id']
except BaseException as e:
print ('get acct_id error.%s' % e)
acct_id = ACCOUNT_ID
params = {"account-id": acct_id,
"amount": amount,
"symbol": symbol,
"type": _type,
"source": source}
if price:
params["price"] = price
url = '/v1/order/orders/place'
return api_key_post(params, url)
现在数据君就通过程序,以市价的方式买入一笔doge测试看看:
print(send_order(amount='10',source='spot-api',symbol='dogeusdt',_type='buy-market'))
出师不利,报错信息看IP地址不合法,我们要把IP地址加到前面在创建API Key页面中的绑定IP一栏里面!
加入IP地址后,再买一次发现买入成功了,撒花一下(如下图)
登陆平台,在订单页面可以看到确实买入成功,如下图:
接着量化君通过程序实现把刚才买入的doge以市价直接卖出,结果如下:
登陆平台订单页面,再次确认一下:
为啥有3笔呢,刚数据君程序里忘了把买入改成卖出,所以又多买了一笔,看看手续费,扎心了,所以程序化交易得谨慎再谨慎再谨慎,乌龙指都这么来的!!!
总结
今天数据君终于在量化的路上迈出了坚实的一步,成功实现程序化买了一次和卖了一次!
当然这和真正的量化差的很远,准确的说根本不是量化,不过万事开头难,这也算是有了好的开头,链路都走通了,二次撒花庆祝!!
后面数据君会为童鞋们带来真正的量化技术,仿佛看到了不远的将来一堆金币在向数据君招手(诡异二笑)!!!