Я написал статью в 2020 году, в которой представил высокочастотные стратегии,https://www.fmz.com/bbs-topic/9750. Хотя он получил довольно много внимания, он не был очень глубоким. С тех пор прошло более двух лет, и рынок изменился. После того, как эта статья была опубликована, моя высокочастотная стратегия могла приносить стабильную прибыль в течение длительного времени, но постепенно прибыль снижалась и даже остановилась в определенный момент. В последние месяцы я приложил некоторые усилия, чтобы обновить ее, и теперь она все еще может приносить некоторые прибыли. В этой статье я предоставлю более подробное введение в мои идеи высокочастотной стратегии и некоторый упрощенный код в качестве отправной точки для обсуждения коммуникаций; и обратная связь приветствуется.
Если сумма ежедневной транзакции составляет 100 миллионов юаней, то скидка составит 5000 юаней. Конечно, сборы за покупку по-прежнему основаны на ставках VIP, поэтому, если стратегия не требует покупателей, уровень VIP мало влияет на стратегии высокой частоты. Различные уровни бирж обычно имеют разные ставки скидки и требуют поддержания высокой суммы транзакции. В первые времена, когда некоторые валютные рынки сильно колебались, были прибыли даже без скидок. По мере усиления конкуренции на скидки приходилась большая доля прибыли или даже полагались исключительно на них; высокочастотные трейдеры преследовали сборы верхнего уровня.
Скорость. Причина, по которой высокочастотные стратегии называются высокочастотными, заключается в том, что они очень быстры. Присоединение к цветовому серверу обмена, получение наименьшей задержки и наиболее стабильного соединения также стало одним из условий внутренней конкуренции. Время внутреннего потребления стратегии должно быть как можно меньше, и в этой статье будет представлена используемая мной веб-фреймворк, которая использует одновременное исполнение.
Подходящий рынок. Высокочастотный трейдинг известен как жемчужина количественной торговли, и многие программистские трейдеры пробовали его, но большинство людей остановились, потому что они не могут получать прибыль и не могут найти направление для улучшения. Основная причина должна быть в том, что они выбрали неправильный торговый рынок. На начальном этапе разработки стратегии, относительно легкие рынки должны быть выбраны для получения прибыли в торговле, чтобы были прибыли и обратная связь для улучшения, что способствует прогрессу стратегии. Если вы начнете конкурировать на самом конкурентном рынке со многими потенциальными оппонентами, независимо от того, как сильно вы постараетесь, вы скоро потеряете деньги и сдадитесь.
Перед лицом конкуренции. Рынок для любой транзакции постоянно меняется, и ни одна торговая стратегия не может длиться вечно, особенно в высокочастотной торговле. Вход на этот рынок означает непосредственную конкуренцию с самыми умными и самыми прилежными трейдерами. На рынке игры с нулевой суммой, чем больше вы зарабатываете, тем меньше зарабатывают другие. Чем позже вы входите, тем выше сложность; те, кто уже на рынке, также должны постоянно улучшаться. 3-4 года назад, вероятно, была лучшая возможность; в последнее время общая активность на рынках цифровой валюты снизилась, что делает очень трудным для новичков начать высокочастотную торговлю сейчас.
Существуют различные высокочастотные стратегии:
Следующий код основан на базовой структуре вечных контрактов Binance, в основном подписываясь на глубину веб-сокета, глубину данных рынка потока заказов и информацию о позиции. Поскольку данные рынка и информация о счете подписываются отдельно, необходимо использовать read ((-1) непрерывно, чтобы определить, была ли получена последняя информация. Здесь EventLoop ((1000) используется для избежания прямых бесконечных петлей и снижения нагрузки системы. EventLoop ((1000) блокирует, пока не будут wss или одновременные возвраты задач с тайм-аутом 1000 мс.
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]
}
Здесь мы по-прежнему используем старый подход, повторяя до требуемой глубины. Предполагая, что 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 - это средняя разница в рыночной цене, и ордер на покупку будет размещен только тогда, когда спред bid-ask больше определенного кратного этого значения и он быстрый. Если удерживать короткую позицию, он также закрывает позицию, чтобы избежать удержания в течение длительного периода. Ордера могут быть размещены как ордера только для того, чтобы гарантировать их исполнение. Кроме того, можно использовать пользовательский идентификатор ордера Binance
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()]})
*/