比特帝国

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫描二维码登录本站

比特帝国 首页 赛贝CYBEX CYBEX资讯 查看内容

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

2019-3-4 15:40| 发布者: CYBEX赛贝社区| 查看: 101| 评论: 0|原作者: CYBEX赛贝社区


量化简易入门

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


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


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


寻找策略


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


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


衡量表现


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


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


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


为了对策略模型的回报有一个大概的评估,人们通常会用历史数据进行回测(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定位全球区块链知识服务商,生产最新区块链知识的专栏和精品课,设计陪伴式、沉浸式、有趣的学习模式,传递创新科技价值的同时,连接全球区块链爱好者,让创新科技成为一种认知潮流;也让更多人了解区块链,认识区块链,并为行业培养高认知人才,从而提高整个行业的整体认知水平,为推动区块链真正发展做出贡献。


更多大赛信息,也可以联系Bang姐(HashBangs)进行咨询.




比特帝国区块链交易所

最新评论

返回顶部