[TOC] 我在2020年写过一篇文章介绍高频策略,https://www.fmz.com/digest-topic/6228 。虽然得到不少关注,但写的并不深入。时间又过去了2年多,市场也发生了变化。那篇文章发出后,我的高频策略很长时间能很稳定的赚钱,但慢慢的利润逐渐下降,甚至一度也中止过。最近几个月又花了精力进行了改造,目前还能能赚些小钱。这篇文章将更详细的介绍我高频策略的思路和一部分简化的代码,起到抛砖引玉的作用,欢迎大家交流反馈。
返佣的账户,以币安为例,目前maker返佣十万分之5,如果每天的成交额是1亿U,返佣就有5000U。当然taker还是依据vip费率,所以如果策略不需要吃单,vip等级对高频策略影响不大。一般交易所不同的等级还有不同的返佣费率,需要维护较高的成交额。在很早之前一些币种行情波动大的时候,没返佣也是有利润的,随着内卷的加剧,返佣占了利润较大的比例,甚至全靠返佣,高频交易者都追求顶级的费率。
速度。高频策略之所以称之为高频,就是因为速度很快。加入交易所colo服务器,获得最低的延时和最稳定的连接也成了内卷的条件之一。策略的内部耗时也要尽可能低,本文将介绍下我使用的websocket的框架,用了并发执行。
合适的市场。高频交易被称为量化交易的明珠,相信很多程序化交易者都进行过尝试,但大部分人都应该会因为不赚钱也找不到改进的方向而停下来,主要的原因应该是找错了交易市场。策略的起步阶段应该找相对容易的市场赚钱进行交易,这样有利润也有改进的反馈,有利于策略的进步。如果一开始就在竞争最激烈的市场中,和很多潜在的对手竞争,怎么尝试都是亏钱,很快就坚持不下去。我推荐新上永续合约交易对,这时候竞争者没有那么多,特别是交易量相对大的,这时候赚钱最容易。BTC和ETH的交易量最大,成交最为活跃,但也是最难生存的。
直面竞争。任何交易的市场都是动态变化的,没有交易策略能一劳永逸,高频交易更加明显,进入这个市场就直接和一批最聪明、最勤奋的交易者做对手。在零和博弈的市场,你赚的多等于别人赚的就少。进入的越晚,难度就越高,已经在市场中的也要不断改进,随时可能被淘汰。三四年前应该是最好的机会,最近数字货币市场整体活跃性下降,现在从新手开始做高频交易的,已经非常困难。
高频策略分为多种
我的策略是趋势和市商的结合,先判断趋势,然后挂单,成交后立刻挂单卖出,不持有库存仓位, 下面结合策略代码介绍。
下面的代码是基于币安永续合约的基础的架构,主要订阅了websocket深度depth订单流trades行情,以及仓位position信息。由于行情和账户信息的是分别的订阅的,需要不断用read(-1)来判断是否获取到最新信息,这里用到了EventLoop(1000),避免了直接的死循环,降低了系统负担。EventLoop(1000)会阻塞到有wss或者并发任务返回,超时时间1000ms。
var datastream = null
var tickerstream = null
var update_listenKey_time = 0
function ConncetWss(){
if (Date.now() - update_listenKey_time < 50*60*1000) {
return
}
if(datastream || tickerstream){
datastream.close()
tickerstream.close()
}
//需要APIKEY
let req = HttpQuery(Base+'/fapi/v1/listenKey', {method: 'POST',data: ''}, null, 'X-MBX-APIKEY:' + APIKEY)
let listenKey = JSON.parse(req).listenKey
datastream = Dial("wss://fstream.binance.com/ws/" + listenKey + '|reconnect=true', 60)
//Symbols是设定的交易对
let trade_symbols_string = Symbols.toLowerCase().split(',')
let wss_url = "wss://fstream.binance.com/stream?streams="+trade_symbols_string.join(Quote.toLowerCase()+"@aggTrade/")+Quote.toLowerCase()+"@aggTrade/"+trade_symbols_string.join(Quote.toLowerCase()+"@depth20@100ms/")+Quote.toLowerCase()+"@depth20@100ms"
tickerstream = Dial(wss_url+"|reconnect=true", 60)
update_listenKey_time = Date.now()
}
function ReadWss(){
let data = datastream.read(-1)
let ticker = tickerstream.read(-1)
while(data){
data = JSON.parse(data)
if (data.e == 'ACCOUNT_UPDATE') {
updateWsPosition(data)
}
if (data.e == 'ORDER_TRADE_UPDATE'){
updateWsOrder(data)
}
data = datastream.read(-1)
}
while(ticker){
ticker = JSON.parse(ticker).data
if(ticker.e == 'aggTrade'){
updateWsTrades(ticker)
}
if(ticker.e == 'depthUpdate'){
updateWsDepth(ticker)
}
ticker = tickerstream.read(-1)
}
makerOrder()
}
function main() {
while(true){
ConncetWss()
ReadWss()
worker()
updateStatus()
EventLoop(1000)
}
}
前面说过我的高频策略需要先判断趋势再执行买卖。判断短期趋势主要依据逐笔成交数据,即订阅里的aggTrade,包含成交方向,价格,数量,成交时间等。买卖主要参考深度和成交量。下面详细介绍下需要关注的指标,大部分指标都分为买卖两组,并且都是在一定时间窗口动态统计,我的策略的时间窗口在10s以内。
//bull代表短期看涨,bear短期看跌
let bull = last_sell_price > avg_sell_price && last_buy_price > avg_buy_price &&
avg_buy_amount / avg_buy_time > avg_sell_amount / avg_sell_time;
let bear = last_sell_price < avg_sell_price && last_buy_price < avg_buy_price &&
avg_buy_amount / avg_buy_time < avg_sell_amount / avg_sell_time;
如果最新卖价大于卖单平均价,最新买价大于买单平均价,固定间隔买单价值大于卖单价值,此时判断短期看涨。反过来看跌。
function updatePrice(depth, bid_amount, ask_amount) {
let buy_price = 0
let sell_price = 0
let acc_bid_amount = 0
let acc_ask_amount = 0
for (let i = 0; i < Math.min(depth.asks.length, depth.bids.length); i++) {
acc_bid_amount += parseFloat(depth.bids[i][1])
acc_ask_amount += parseFloat(depth.asks[i][1])
if (acc_bid_amount > bid_amount && buy_price == 0) {
buy_price = parseFloat(depth.bids[i][0]) + tick_size
}
if (acc_ask_amount > ask_amount && sell_price == 0) {
sell_price = parseFloat(depth.asks[i][0]) - tick_size
}
if (buy_price > 0 && sell_price > 0) {
break
}
}
return [buy_price, sell_price]
}
这里还是采取老的思路,迭代深度到所需要的量,这里假设1s内能成交10个币的买单,不考虑新挂单的情况下,卖单价格设置为10个币的买单冲击到的位置。具体多大的时间窗口需要自己设定。
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
Ratio代表固定比例, 代表买单量为最近卖单数量的固定比例。这样策略能自适应的根据当前买卖活跃度调整下单大小。
if(bull && (sell_price-buy_price) > N * avg_diff) {
trade('buy', buy_price, buy_amount)
}else if(position.amount < 0){
trade('buy', buy_price, -position.amount)
}
if(bear && (sell_price-buy_price) > N * avg_diff) {
trade('sell', sell_price, sell_amount)
}else if(position.amount > 0){
trade('sell', sell_price, position.amount)
}
其中avg_diff是平均盘口的差价,只有当下单买卖差价大于一定倍数这个值并且看涨的的时候才会下买单,如果持有空单,此时也会平仓,避免长期持单。下单可以下only-maker的订单,确保挂单成交。并且可以用币安的自定义订单id,这样可以不用等待订单返回。
var tasks = []
var jobs = []
function worker(){
let new_jobs = []
for(let i=0; i<tasks.length; i++){
let task = tasks[i]
jobs.push(exchange.Go.apply(this, task.param))
}
_.each(jobs, function(t){
let ret = t.wait(-1)
if(ret === undefined){
new_jobs.push(t)//未返回的任务下次继续等待
}
})
jobs = new_jobs
tasks = []
}
/*
需要的任务参数写在param里
tasks.push({'type':'order','param': ["IO", "api", "POST","/fapi/v1/order",
"symbol="+symbol+Quote+"&side="+side+"&type=LIMIT&timeInForce=GTX&quantity="+
amount+"&price="+price+"&newClientOrderId=" + UUID() +"×tamp="+Date.now()]})
*/
mztcoin 请问草神,卖出的逻辑是什么呢?“成交后立刻挂单卖出,不持有库存仓位” ,意思是在同一个for循环里挂单吧,挂什么价格,没成交怎么办呢?求解答
mztcoin 草神牛逼
DANGOU 草神牛波一
77924998 草神用的AWS哪一款服务器?
xukitty 牛逼牛逼
bwxiaok 草神出个付费的高频课程吧
我心依旧 能做成复制就能用的吗这个策略
作手君TradeMan 打call草神,希望多多出教学,学习入门高频交易中ing~
~风口上的浪里个浪~ 牛逼
fmzero 草神牛逼!!!
奥克量化 牛逼
小草 目太难了,出了意义也不大