如何做高级韭菜:交易所搬砖

640?wx_fmt=png 点击上方 “蓝色字” 可关注我们!







640?wx_fmt=jpeg

作者:Cybex&HashBang

量化简易入门


量化交易是一个高度复杂的金融领域。投资决策的过程从传统的人脑分析过渡到运用各种公开资讯与数据形成的策略模型。量化交易可以降低人为非理性的干预,极大得扩展交易处理量,并且更有有效的控制风险, 为市场提供了更合理的定价和更佳的流动性,在现代金融中扮演非常重要的角色。


伴随计算机技术的发展和基础设置的完善,量化交易中蕴含的机会和潜力吸引了越来越多人的注意,从大型机构,基金,到小型的团队直到无数的个人。


为了便于阐述,本文粗略将量化交易分为几个部分,包括寻找策略,回测评估,执行系统,风险管理,本篇会把重点放在执行系统上,便于已经有自己模型的用户尝试接入cybex。我们在文末会附上一些关于量化模型的参考资料,有兴趣的读者可以自行扩展。


寻找策略


量化策略的开发是非常多样化的,从开始所涵盖的交易领域,选用的资讯数据,到模型所用的计算方式,和交易频率都有很大的差异。比较常见的有基于数学的统计模型和和基于计算机学的机器学习/数据挖掘。按照交易的频率,量化交易可以大致分为,低频,高频和超高频。低频交易(LFT)通常指任何持有资产超过一个交易日的策略。相应地, 高频交易(HFT)通常指的是日内持有资产的策略。超高频交易 (UHFT) 是指以秒和毫秒为单位持有资产的策略。


值得注意的是,一些策略在历史回测中表现良好但实际运行中在短时间便严重亏损,另外很常见的情况是,许多可以获得较好收益的策略不能实现长期的盈利,表现随着时间而逐渐下降,需要对策略进行调整优化,或者启用新的策略。新的监管环境,投资者情绪的改变和宏观经济现象, 都可能导致市场行为的差异, 从而使你的策略获利,因此开发寻找量化策略一般是个长期的工程。


衡量表现


当评定衡量一个策略模型时, 必须能够量化它的表现,包括收益与风险。现今普遍的“行业标准”是最大回撤(max dropdown) 和夏普比率(Sharpe Ratio)。


最大回撤,即在一个时段内(如月度,年度)的账户净值曲线中最大的峰谷比。这是最常引用的百分比。历史回测可以显示过去的最大回撤, 这是这个策略未来回撤的一个很好的参考。 


夏普比率, 即超额收益的平均值除以那些超额收益的风险(波动性)。量化策略的理想状态是以尽可能低的风险赢取相对更高的收益。

640?wx_fmt=png

为了对策略模型的回报有一个大概的评估,人们通常会用历史数据进行回测(back test),即将模型放在历史数据中评估表现,尽管这个回测的结果并不总是可靠,不过是一项非常重要的参考。


执行策略


执行系统是由策略生成的交易指示,通过某种形式发送到市场执行,可以是手动,半自动或全自动。全自动的执行有巨大的处理能力,可以减少人为因素,完全自动化的执行机制十分常见于中高频的交易策略, 这将经常与交易生成器紧密结合(由于策略和技术的相互依存)。创建执行系统时的主要考虑的因素是交易成本(包括佣金、滑点和价差) 以及实时系统性能。

成为一个高级高级韭菜

当具备了量化模型/策略的概念,并且掌握了自动交易的知识与技能,就可以将两者结合,创造自己的自动化执行的量化程序。


这里,我们实现一个十分简单的基于MACD的策略,当MACD向上交叉的时候下单购买,当MACD向下交叉的时候出售,交叉就是唯一依据的信号。


这里用火币网的行情数据为例,换成其他交易所亦可,需要安装ccxt。此例我们使用python做展示。Cybex Python API库请点击

×

为了更简洁得阐述基本概念,这里的示范做了简化,去掉了订单管理等实际操作中十分必要的部分,完整版范例请参考我们的 github。


import os, sys

from time import sleep, time

import ccxt

from cybexapi import SignerConnector, CybexRestful


HUOBI_KEY = 'xxxx'

HUOBI_SECRET = 'xxxxx'

SYMBOL = 'ETH/USDT'


class BarData:

def __init__(self, bar_array):

self.start_time = datetime.fromtimestamp(bar_array[0] / 1000.0)

self.px_open = float(bar_array[1])

self.px_high = float(bar_array[2])

self.px_low = float(bar_array[3])

self.px_close = float(bar_array[4])

self.volume = float(bar_array[5])


self.end_time = datetime.fromtimestamp(0)

self.sma_fast = 0

self.sma_slow = 0

self.ema_fast = 0

self.ema_slow = 0

self.macd = 0

self.macd_signal = 0

self.finalized = False


class MarketDataManager:

def __init__(self):

self.bars = []

self.order_book = {"bids":[],"asks":[]}

# self.best_bid = 0

# self.best_ask = 0


def get_order_book(self):

return self.order_book


def get_best_bid(self):

if len(self.order_book['bids']) > 0:

return self.bids[0][0]

return None


def get_best_ask(self):

if len(self.order_book['asks']) > 0:

return self.asks[0][0]

return None


def is_new_bar(self, bar):

if len(self.bars) == 0:

return True

return self.bars[-1].start_time < bar.start_time


def update_bar_data(self, bar):

if self.is_new_bar(bar):

self.bars.append(bar)

# print('insert a bar, current length', len(self.bars))

elif self.bars[-1].start_time == bar.start_time:

self.bars[-1] = bar


def redo_sma(self):

if SLOW_PERIOD > len(self.bars):

return


for i in range(SLOW_PERIOD, len(self.bars)):

self.calc_sma(i)


def redo_ema(self):

for i in range(0, len(self.bars)):

self.calc_ema(i)


def calc_ema(self, index):

if index == 0:

return


this_bar = self.bars[index]

last_bar = self.bars[index - 1]


fast_k = 2 / (FAST_PERIOD + 1)

slow_k = 2 / (SLOW_PERIOD + 1)

signal_k = 2 / (SIGNAL_PERIOD + 1)


this_bar.ema_fast = (this_bar.px_close - last_bar.ema_fast) * fast_k + last_bar.ema_fast

this_bar.ema_slow = (this_bar.px_close - last_bar.ema_slow) * slow_k + last_bar.ema_slow

this_bar.macd = this_bar.ema_fast - this_bar.ema_slow

this_bar.macd_signal = (this_bar.macd - last_bar.macd_signal) * signal_k + last_bar.macd_signal


# print('calc_ema {0} {1:.4f} {2:.4f} {3:.4f} {4:.4f}'

#       .format(index, this_bar.macd, this_bar.macd_signal, this_bar.ema_fast, this_bar.ema_slow))


def calc_sma(self, index):

if len(self.bars) <= SLOW_PERIOD or index > len(self.bars):

return


fast_sum = 0

for i in range(index - FAST_PERIOD, index):

fast_sum += self.bars[i].px_close


fast_val = fast_sum / FAST_PERIOD


slow_sum = 0

for i in range(index - SLOW_PERIOD, index):

slow_sum += self.bars[i].px_close


slow_val = slow_sum / SLOW_PERIOD


self.bars[index].sma_fast = fast_val

self.bars[index].sma_slow = slow_val


def get_bar_count(self):

return len(self.bars)


def check_signal(self, at_index):

if len(self.bars) > SLOW_PERIOD and at_index > SLOW_PERIOD:


last_bar_diff = self.bars[at_index - 1].macd - self.bars[at_index - 1].macd_signal

this_bar_diff = self.bars[at_index].macd - self.bars[at_index].macd_signal

print('{0}, check_signal_index {1}, last {2:.4f}, this {3:.4f}'.format(datetime.now(), at_index, last_bar_diff, this_bar_diff))


if last_bar_diff < 0 < this_bar_diff:

# Target position is 1 unit

print('cross up')

return 1


if this_bar_diff < 0 < last_bar_diff:

# Target position is -1 unit

print('cross down')

return -1


return None



def handle_signal(signal, mdb):


side = 'buy'

price = mdb.get_best_bid()

quantity = 1


if signal == -1:

side = 'sell'

price = mdb.get_best_ask()


order_msg = signer.prepare_order_message(asset_pair=SYMBOL, price=price, quantity=quantity, side=side)

trx_id = order_msg['transactionId']

order_result = api_server.send_transaction(order_msg)

return trx_id, order_result


if __name__ == '__main__':


huobi_api = ccxt.huobipro({'apiKey': HUOBI_KEY, 'secret': HUOBI_SECRET, 'enableRateLimit': True})

huobi_api.load_markets()


mdb = MarketDataManager()


_start, _end = (int(time()) - 60 * 1000) * 1000, int(time()) * 1000


mdb.order_book = huobi_api.fetch_order_book(SYMBOL, limit = 10)

ohlcv_data = huobi_api.fetch_ohlcv(SYMBOL, _start, _end)


for bar_data in ohlcv_data:

bar = BarData(bar_data)

mdb.update_bar_data(bar)


mdb.redo_ema()


signal_slots = {}


while True:

# calculate a few time related number

start, end = (int(time()) - 120) * 1000, int(time()) * 1000

now_m = int(time() / 60)


# Get market data udpates

process_huobi_data(mdb, start, end)


# -1 is the current bar

mdb.calc_ema(-2)

mdb.calc_ema(-1)


# Add 2 seconds delay for signal checking to allow all data to come in.

cut_off = now_m * 60 + 3


if now_m not in signal_slots and now_s > cut_off:

# If there is no action in this time slot, calculate to_trade and send order

signal = mdb.check_signal(-2)


if signal is not None:

try:

result = handle_signal(signal, mdb)

if result is not None:

signal_slots[now_m] = signal


except Exception as e:

print('handle_signal encounter error', e)



自动化搬砖


除了基于价格预测的策略,一些策略会依靠做市,也就是提供流动性的服务,赚取买卖价格的差值来实现获益,也就是俗称的"搬砖"。此类策略一般情况下是非常高频的,每秒处理的order从上百到上万不等。通常会连接多个交易所进行对冲,来实现风险中性。


这里,我们实现一个非常简单的,完全按照币安交易对的价格在CYBEX下单,作为入门参考。需要注意的是,实际上真正可以运行的自动化做市是高度复杂的,对于所有的状态的order都需要监控,涉及到较为精密和复杂的订单管理系统,和实时的盈亏计算。此例我们使用javascript做展示。


const WebSocketClient = require('websocket').client;

const request = require('request');


var wsClient = new WebSocketClient();


var openOrder = 0;


function handleMarketDataTick(json) {

var bestBid = json.bids[0];

var bestBidPx = Number(bestBid[0]);


var bestAsk = json.asks[0];

var bestAskPx = Number(bestAsk[0]);


// Skew the price for 50bps to ensure they won't be crossed

bestBidPx = bestBidPx - (bestBidPx * 50 / 10000);

bestAskPx = bestAskPx + (bestAskPx * 50 / 10000);


// Round to 2 decimal places to fulfill the min tick to 0.01

bestBidPx = Math.round(bestBidPx * 100) / 100;

bestAskPx = Math.round(bestAskPx * 100) / 100;


if (openOrder < 2) {

createNewOrderPayload(true, bestBidPx, 0.01, 'ETH/USDT', handleNewOrderPayload);        // bid order

createNewOrderPayload(false, bestAskPx, 0.01, 'ETH/USDT', handleNewOrderPayload);       // ask order

}

}


function createCancelOrderByTxIdPayload(txId, callback) {

var options = {

uri: 'http://localhost:8090/signer/v1/cancelOrder',

method: 'POST',

json: {

"originalTransactionId": txId

}

};


request(options, function (error, response, body) {

if (!error && response.statusCode === 200) {

console.log('Sending cancel   : ' + txId);

callback(body);

}

});

}


function handleCancelOrderPayload(payload) {

var options = {

uri: 'https://api.cybex.io/v1/transaction',

method: 'POST',

json: payload

};


request(options, function (error, response, body) {

if (!error && response.statusCode === 200) {

if (body.Status === 'Successful') {

console.log('Order Canceled   : ' + payload.originalTransactionId);

openOrder--;

}

}

});

}


function handleNewOrderPayload(body) {

var txId = body.transactionId;

var options = {

uri: 'https://api.cybex.io/v1/transaction',

method: 'POST',

json: body

};


request(options, function (error, response, body) {

if (!error && response.statusCode === 200) {

if (body.Status === "Successful") {

openOrder++;


setTimeout(createCancelOrderByTxIdPayload, 2000, txId, handleCancelOrderPayload);

}

}

});

}


function createNewOrderPayload(isBid, px, qty, sym, callback) {

var side = isBid ? 'buy' : 'sell';

var options = {

uri: 'http://localhost:8090/signer/v1/newOrder',

method: 'POST',

json: {

"assetPair": sym,

"price": px,

"quantity": qty,

"side": side

}

};


request(options, function (error, response, body) {

if (!error && response.statusCode === 200) {

console.log('Sending new order: ' + side + ' ' + sym + ' ' + qty + '@' + px + ' - txId: ' + body.transactionId);

callback(body);

}

});

}


// WebSocket client to connect to Binance API for OrderBook

wsClient.on('connect', function (connection) {

connection.on('message', function (message) {

if (message.type === 'utf8') {

handleMarketDataTick(JSON.parse(message.utf8Data));

}

});


connection.on('close', function () {

console.log('Connect closed from Binance...');

});

});


wsClient.connect('wss://stream.binance.com:9443/ws/ethusdt@depth5');


以上就是本次的依一些简单介绍啦!赶紧报名参加Cybex联合HashBang主办全球第一届去中心化交易大赛,也是全球第一次在去中心化交易所上进行的交易大赛,所有量化团队、操盘手的挂单吃单操作都公开透明,借此来决出名副其实的交易之王。


报名时间:2.25-3.10

报名申请表:

http://cybexexchange.mikecrm.com/C6aKW0a

或点击下方“阅读原文”直达报名链接。


为了方便参赛的团队更加深入的了解Cybex的交易环境,我们会持续更新对Cybex的介绍:


第一期:数币量化交易团队/个人如何选择合适的交易所

第二期:自动交易程序介绍——普通个人和团队如何快速上手量化

第三期:量化小白入门级手册、如何做高级韭菜——如何在交易所搬砖以及交易对搬砖

第四期:一切上链,打造透明交易,实时订单撮合引擎(ROME)——Cybex如何支持交易团队

第五期:交易大赛规则发布


Cybex也是一个开放的社区平台,大家可以访问

https://github.com/CybexDex/cybex-node-doc/wiki获取相关信息。


关于主办方:

CYBEX:是一个旨在提高加密资产流动性的去中心化交易所。相较于如今市场中的中心化交易所,CYBEX为加密资产提供了更多的透明性与安全性。此外,CYBEX还是一个由全球志同道合的合作伙伴在去中心化网络中共同构建并运营的生态系统。其核心以石墨烯区块链引擎为基础,提供了更高的效率与安全性,并且能够通过权益证明机制,扩展到每秒10万笔的交易速度。


HashBang定位全球区块链知识服务商,生产最新区块链知识的专栏和精品课,设计陪伴式、沉浸式、有趣的学习模式,传递创新科技价值的同时,连接全球区块链爱好者,让创新科技成为一种认知潮流;也让更多人了解区块链,认识区块链,并为行业培养高认知人才,从而提高整个行业的整体认知水平,为推动区块链真正发展做出贡献。


640?wx_fmt=jpeg

扫码添加Bang姐微信,进群了解更多信息。


本文仅代表作者个人观点,不代表区块链铅笔的立场,不构成投资建议,内容仅供参考。

640?wx_fmt=png

3分钟了解什么是区块链?(中文动画)|(英文动画教程

3分钟了解什么是DAO?(中文动画)|(英文动画教程)

3分钟看懂以太坊和智能合约?(中文动画)|(英语动画教程)

3分钟了解比特币挖矿和区块链共识机制?(中文动画)|(英文动画教程)

3分钟了解公有链和私有链(中文动画教程)|(英文动画教程)

3分钟了解区块链的六层模型(中文动画教程)|(英文动画教程)

什么是区块链英文版(动画教程)

肖风:区块链与另类资产配置

逐鹿区块链!详解三大巨头BAT区块链战略布局

麦肯锡报告:区块链对保险行业的影响

八张图表解读区块链的未来发展

震惊全球的The DAO黑客事件全程回顾

Circle获得包括IDG、百度、万向在内6000万美元融资


关注本公众号后,进入公众号

回复关键词可以查阅资料,以下是部分关键词


回复 WEF ,查看《WEF:世界经济论坛认为区块链是互联网金融行业的未来报告》

回复 智能合约 ,查看《巴克莱银行报告》

回复 moody ,查看《穆迪120个区块链项目报告》

回复 SWIFT ,查看SWIFT《区块链对证券交易全流程产生的影响及潜力》报告

回复 论文11 ,查看论文《可扩展的去中心区块链》

回复 埃森哲2 ,查看埃森哲《区块链每年可以为投资银行节省120亿美元》报告

回复 联合国报告 ,查看联合国报告《数字货币和区块链技术在构建社会和可信金融之间扮演的角色》

回复 用户特性 ,查看普林斯顿大学首本比特币教科书初稿《比特币用户的特性(Characteristics of Bitcoin Users)》

回复 普林斯顿 ,查看普林斯顿大学首本比特币教科书初稿《比特币和数字货币技术(Bitcoin and Cryptocurrency Technologies)》

回复 IMF,查看国际货币基金组织报告《Virtual Currencies and Beyond: Initial Considerations》

回复 DTCC ,查看美国存管信托清算公司报告《DTCC: 拥抱分布式》

回复 广发 ,查看报告《科技前沿报告:区块链:正快速走进公众和政策视野》

回复 川财1 ,查看报告《川财证券:区块链技术调研报告之一:具有颠覆所有行业的可能性》

回复 川财2 ,查看报告《川财证券:区块链技术调研报告之二:区块链技术进化论-区块链技术的国内实践和展望》

回复 桑坦德 ,查看桑坦德银行报告《The Fintech 2.0 Paper: rebooting financial services》

回复 拜占庭 ,查看《拜占庭将军问题详解》

回复 论文1 ,查看论文《比特币闪电网络:可扩展的离线即时支付》

回复 论文2 ,查看论文《比特币骨干协议》

回复 论文3 ,查看论文《数字货币是否应该进入Barbados央行国际储备货币组合中》

回复 帮助 ,查看本公众号全部关键词列表

640?wx_fmt=jpeg

点击下方“阅读原文”查看更多页面出现后再点击“来源”可以查看译文原文链接  ↓↓↓


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值