저는 2020년에 고주파 전략들을 소개하는 기사를 썼습니다.https://www.fmz.com/bbs-topic/9750. 비록 꽤 많은 관심을 받았지만, 그것은 매우 깊이 있는 것이 아니었습니다. 그 이후로 2 년 이상이 흘렀고, 시장은 변했습니다. 그 기사가 출판 된 후, 내 고주파 전략은 오랫동안 안정적으로 이익을 얻을 수 있었지만, 점차적으로 이익이 감소하고 심지어 한 지점에서 멈췄습니다. 최근 몇 달 동안 나는 그것을 개편하기 위해 약간의 노력을 기울였으며, 지금은 여전히 약간의 이익을 얻을 수 있습니다. 이 기사에서, 나는 토론의 시작점으로 내 고주파 전략 아이디어와 간단한 코드를 좀 더 상세하게 소개 할 것입니다. 그리고 피드백은 환영합니다.
리베이트 계정은 바이낸스를 예로 들자면 현재 0.0005%의 메이커 리베이트를 가지고 있다. 일일 거래 금액이 1억 U인 경우 리베이트는 5000 U이 될 것이다. 물론, 타커 수수료는 여전히 VIP 요금을 기준으로 하고 있기 때문에 전략에 타커가 필요하지 않다면, VIP 수준은 고주파 전략에 거의 영향을 미치지 않는다. 서로 다른 수준의 거래소는 일반적으로 다른 리베이트율을 가지고 있으며 높은 거래 금액을 유지해야 한다. 일부 통화 시장이 크게 변동하는 초기에는 리베이트 없이도 이익이 있었다. 경쟁이 심화됨에 따라 리베이트는 이익의 더 큰 비율을 차지하거나 심지어 그것에만 의존했다; 고주파 트레이더들은 최고 수준의 수수료를 추구했다.
속도. 고주파 전략이 고주파라고 불리는 이유는 매우 빠르기 때문입니다. 교환의 컬로 서버에 가입하여 가장 낮은 지연시간과 가장 안정적인 연결을 얻는 것도 내부 경쟁의 조건 중 하나가되었습니다. 전략의 내부 소비 시간은 가능한 한 작아야하며이 기사는 동시 실행을 채택하는 내가 사용하는 웹소켓 프레임워크를 소개합니다.
적절한 시장. 높은 주파수 거래는 양적 거래의 진주로 알려져 있으며 많은 프로그래밍 트레이더들이 시도했지만 대부분의 사람들은 수익을 창출하고 개선 방향을 찾지 못하기 때문에 중단했습니다. 주된 이유는 그들이 잘못된 거래 시장을 선택했기 때문입니다. 전략 개발의 초기 단계에서 상대적으로 쉬운 시장은 수익을 창출하기 위해 거래에서 수익을 창출하기 위해 선택되어야하여 수익과 개선에 대한 피드백이 있기 때문에 전략의 발전에 유도합니다. 많은 잠재적 인 상대와 가장 경쟁력있는 시장에서 경쟁하기 시작하면 아무리 노력해도 곧 돈을 잃고 포기 할 것입니다. 경쟁자가 많지 않을 때, 특히 상대적으로 큰 거래 금액이있는 경우 새 영구 계약 거래 쌍을 추천합니다. 이 때 수익이 가장 쉽습니다. BTC와 ETH는 거래액이 가장 많고 생존하기 가장 어렵습니다.
경쟁에 직면. 어떤 거래의 시장은 끊임없이 변화하고 있으며, 특히 고주파 거래에서 어떤 거래 전략도 영원히 지속될 수 없습니다. 이 시장에 진입하는 것은 가장 똑똑하고 가장 열심히 거래하는 거래자들과 직접 경쟁하는 것을 의미합니다. 제로섬 게임 시장에서, 당신이 더 많이 벌면 다른 사람들이 더 적게 벌 것입니다. 당신이 더 늦게 들어가면 어려움이 더 높습니다. 이미 시장에있는 사람들도 지속적으로 개선해야합니다. 3-4 년 전은 아마도 가장 좋은 기회였습니다. 최근에는 디지털 통화 시장의 전반적인 활동이 감소하여 신입업자가 지금 고주파 거래를 시작하는 것이 매우 어렵습니다.
다양한 고주파 전략이 있습니다.
다음 코드는 바이낸스 영구 계약의 기본 프레임워크를 기반으로 주로 웹소켓 깊이, 깊이 주문 흐름 거래 시장 데이터 및 위치 정보를 구독합니다. 시장 데이터와 계정 정보가 별도로 구독되기 때문에 최신 정보가 얻었는지 여부를 결정하기 위해 지속적으로 읽기 (read) 을 사용해야합니다. 여기서 EventLoop (1000) 은 직접 끝없는 루프를 피하고 시스템 부하를 줄이기 위해 사용됩니다. EventLoop (1000) 은 1000ms의 타임 아웃으로 wss 또는 동시 작업 반환이있을 때까지 차단합니다.
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()
}
//Need 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 are the set trading pairs
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 represents short-term bullish, bear represents short-term bearish
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]
}
여기, 우리는 여전히 오래된 접근 방식을 채택하고, 필요한 깊이로 반복합니다. 새로운 대기 주문을 고려하지 않고 1 초 안에 10 동전이 거래 될 수 있다고 가정하면, 판매 가격은 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는 평균 시장 가격 차이이며, 바이너리 주문은 이 값의 특정 배수보다 더 큰 경우만 구매할 수 있으며, 상승률이 높습니다. 단위 포지션을 보유하면 장기간 보유를 피하기 위해 포지션을 닫을 수도 있습니다. 주문은 실행을 보장하기 위해 유일한 메이커 주문으로 배치 할 수 있습니다. 또한 바이낸스
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)//Unreturned tasks will continue to wait next time
}
})
jobs = new_jobs
tasks = []
}
/*
Write the required task parameters in 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()]})
*/