
[TOC] 저는 2020년에 고빈도 전략을 소개하는 기사를 썼습니다. https://www.fmz.com/digest-topic/6228. 많은 주목을 받았지만 깊이 있게 쓰여지지는 않았습니다. 2년이 넘도록 시장은 변화해 왔습니다. 그 기사가 나온 후로, 저의 고빈도 전략은 오랫동안 꾸준히 수익을 낼 수 있었지만, 수익은 점점 줄어들었고 어느 순간 중단되기도 했습니다. 최근 몇 달 동안, 저는 리노베이션에 많은 노력을 기울였고, 지금은 약간의 돈을 벌 수 있게 되었습니다. 이 글에서는 고빈도 전략에 대한 제 아이디어와 몇 가지 단순화된 코드를 더 자세히 소개할 것입니다. 이는 토론의 시작점이 될 수 있습니다. 누구나 소통하고 피드백을 제공할 수 있습니다.
리베이트를 받는 계정의 경우, 바이낸스를 예로 들면, 현재 메이커 리베이트는 100,000의 0.5%입니다. 일일 거래량이 1억 U인 경우, 리베이트는 5,000 U가 됩니다. 물론, 테이커 수수료는 여전히 VIP 요금에 따라 결정되므로, 해당 전략에 주문 접수가 필요하지 않다면 VIP 레벨은 고빈도 전략에 거의 영향을 미치지 않습니다. 일반적으로 거래소의 레벨에 따라 할인율이 다르며, 더 높은 거래량을 유지해야 합니다. 오래전 일부 통화의 시장이 크게 변동했을 때, 리베이트가 없어도 여전히 이익이 있었습니다. 내부 유통이 강화되면서 리베이트가 이익의 상당 부분을 차지했고, 심지어 전적으로 리베이트에 의존하기도 했습니다. 고빈도 트레이더는 다음을 추구합니다. 최상의 가격.
속도. 고빈도 전략이 고빈도라고 불리는 이유는 그것이 매우 빠르기 때문입니다. 가장 낮은 지연 시간과 가장 안정적인 연결을 얻기 위해 거래소의 콜로 서버에 가입하는 것도 내부 순환을 위한 조건 중 하나가 되었습니다. 전략의 내부 시간 소모도 가능한 한 낮아야 합니다. 이 글에서는 동시 실행을 사용하는 제가 사용하는 웹소켓 프레임워크를 소개합니다.
적절한 시장. 고빈도 트레이딩은 양적 트레이딩의 보석으로 알려져 있습니다. 저는 많은 프로그래매틱 트레이더가 시도해 봤다고 믿지만, 대부분의 사람들은 돈을 벌지 못하고 개선할 방법을 찾을 수 없어서 중단할 것입니다. 가장 큰 이유는 아마도 그들이 잘못된 길을 찾고 있다는 것입니다. 거래 시장. 전략의 초기 단계에서는 돈을 벌기 쉬운 시장을 찾아 거래해야 합니다. 그렇게 하면 수익을 낼 수 있고 개선에 대한 피드백도 얻을 수 있는데, 이는 전략의 진전에 도움이 됩니다. 가장 경쟁이 심한 시장에서 사업을 시작하여 많은 잠재적 경쟁자들과 경쟁하게 되면 아무리 노력해도 돈을 잃게 되고 더 이상 버틸 수 없게 됩니다. 새로 상장된 영구 계약 거래 쌍을 추천합니다. 지금은 경쟁자가 그렇게 많지 않습니다. 특히 거래량이 비교적 많을 때는 더욱 그렇습니다. 돈을 벌기 가장 쉬운 시기입니다. BTC와 ETH는 거래량이 가장 많고 거래 활동도 가장 활발하지만, 생존하기가 가장 어렵기도 합니다.
경쟁에 정면으로 맞서세요. 모든 거래 시장은 역동적으로 변화합니다. 어떤 거래 전략도 단번에 효과가 있을 수는 없습니다. 이는 고빈도 거래에서 더욱 분명합니다. 이 시장에 진입한다는 것은 가장 똑똑하고 가장 근면한 거래자 그룹과 직접 경쟁한다는 것을 의미합니다. 제로섬 시장에서는 당신이 많이 벌수록 다른 사람들의 수입은 줄어듭니다. 늦게 진입할수록 더 어려워질 것입니다. 이미 시장에 있는 사람들도 언제든지 제거될 수 있으므로 계속 개선해야 합니다. 3~4년 전이 가장 좋은 기회였어야 합니다. 최근 디지털 통화 시장의 전반적인 활동이 감소했고, 이제 초보자가 고빈도 거래에 참여하기가 매우 어렵습니다.
고빈도 전략에는 여러 유형이 있습니다
제 전략은 트렌드와 마켓 메이커의 조합입니다. 저는 먼저 트렌드를 파악한 다음 주문을 하고, 거래가 완료된 후 즉시 매도 주문을 합니다. 저는 재고 포지션을 보유하지 않습니다. 전략 코드는 아래에 소개되어 있습니다.
다음 코드는 바이낸스 선물 계약의 기본 아키텍처를 기반으로 하며, 주로 웹소켓 심도 주문 흐름 거래, 시장 정보 및 포지션 정보를 구독합니다. 시장 정보와 계좌 정보가 별도로 구독되므로 최신 정보를 얻었는지 여부를 확인하기 위해 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를 기준으로 판단되며, 여기에는 거래 방향, 가격, 수량, 거래 시간 등이 포함됩니다. 매수 및 매도의 주요 기준은 거래량과 깊이입니다. 다음은 주의가 필요한 지표에 대한 자세한 소개입니다. 대부분의 지표는 매수와 매도의 두 그룹으로 나뉘며 특정 시간 창에서 동적으로 계산됩니다. 내 전략의 시간 창은 10초 이내입니다.
//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]
}
여기서 우리는 여전히 오래된 아이디어를 채택하고 필요한 양만큼 깊이를 반복합니다. 여기서 우리는 10코인의 매수 주문이 1초 이내에 실행될 수 있다고 가정합니다. 새로운 보류 주문을 고려하지 않고 매도 주문 가격은 다음 위치로 설정됩니다. 10개의 코인을 매수 주문하면 성사됩니다. 구체적인 시간 창 크기를 직접 설정해야 합니다.
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
비율은 고정 비율을 의미하며, 이는 매수 주문 수량이 가장 최근의 매도 주문 수량에 대한 고정 비율임을 의미합니다. 이 전략을 사용하면 현재의 매수 및 매도 활동에 따라 주문 크기를 적응적으로 조정할 수 있습니다.
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는 평균 시장 가격의 차이입니다. 매수 주문은 매수-매도 차이가 이 값의 특정 배수보다 크고 추세가 강세일 때만 이루어집니다. 숏 주문을 보유하면 포지션 또한, 장기간 주문을 보류하는 것을 피하기 위해 이 시점에는 주문이 닫힙니다. 보류 주문이 실행되도록 하려면 유일 메이커 주문을 할 수 있습니다. 또한 바이낸스의 사용자 지정 주문 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()]})
*/