Стратегии хеджирования являются очень хорошими практическими стратегиями для начинающих в разработке стратегии.
Прежде всего, ясно, что стратегия, которая должна быть разработана, - это стратегия спотового хеджирования криптовалют. Мы разрабатываем самую простую стратегию хеджирования. Мы продаем на бирже с более высокой ценой только между двумя спотовыми биржами и покупаем на бирже с более низкой ценой, чтобы взять разницу. Когда биржи с более высокими ценами все номинальные монеты (потому что монеты с более высокими ценами продаются), а биржи с более низкими ценами все монеты (монеты с более низкими ценами покупаются), это не может быть хеджировано. В это время мы можем только ждать, пока перелом цены будет хеджироваться.
При хеджировании цена и количество ордера ограничиваются биржей, а также есть ограничение на минимальное количество ордера. В дополнение к минимальному пределу стратегия хеджирования также должна учитывать максимальный объем ордера за один раз. Если объем ордера слишком велик, объем ордера будет недостаточным. Также необходимо рассмотреть вопрос о том, как конвертировать обменный курс, если две валюты, деноминированные на бирже, отличаются. При хеджировании, комиссионная за обработку и скольжение заказчика - это все затраты на транзакцию, а не до тех пор, пока существует разница в цене, которую можно хеджировать. Поэтому разница в цене хеджирования также имеет триггерное значение. Если она ниже определенной разницы в цене, хеджирование потеряет.
Исходя из этих соображений, стратегия должна быть разработана с учетом нескольких параметров:
hedgeDiffPrice
, когда разница превышает это значение, операция хеджирования запускается.minHedgeAmount
, минимальная сумма ордера (монеты), которая может быть хеджирована.maxHedgeAmount
, максимальная сумма ордера (монеты) для одного хеджирования.pricePrecisionA
, точность цены заказа (количество запятой), размещенной биржей А.amountPrecisionA
, точная сумма заказа, размещенного биржей A (количество запятой).pricePrecisionB
, точность цены заказа (количество запятой), размещенной биржей B.amountPrecisionB
, точная сумма заказа, размещенного биржей B (количество запятой).rateA
, конвертация валютного курса первого добавленного обменного объекта, по умолчанию 1, не конвертирована.rateB
, конвертация валютного курса второго добавленного обменного объекта, по умолчанию 1, не конвертирована.Стратегия хеджирования должна сохранять количество монет на двух счетах неизменным (то есть не держать позиции в любом направлении и поддерживать нейтралитет), поэтому в стратегии должна быть логика баланса, чтобы всегда обнаруживать баланс. При проверке баланса неизбежно получать данные о активах с двух бирж. Нам нужно написать функцию для использования.
function updateAccs(arrEx) {
var ret = []
for (var i = 0 ; i < arrEx.length ; i++) {
var acc = arrEx[i].GetAccount()
if (!acc) {
return null
}
ret.push(acc)
}
return ret
}
После размещения заказа, если нет завершенного заказа, мы должны отменить его вовремя, и заказ не может оставаться в ожидании. Эта операция должна быть обработана как в модуле баланса, так и в логике хеджирования, поэтому также необходимо разработать функцию полного снятия заказа.
function cancelAll() {
_.each(exchanges, function(ex) {
while (true) {
var orders = _C(ex.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
ex.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
}
})
}
При сбалансировании количества монет, нам нужно найти цену, накопленную на определенное количество монет в определенной глубине данных, так что нам нужна такая функция, чтобы справиться с этим.
function getDepthPrice(depth, side, amount) {
var arr = depth[side]
var sum = 0
var price = null
for (var i = 0 ; i < arr.length ; i++) {
var ele = arr[i]
sum += ele.Amount
if (sum >= amount) {
price = ele.Price
break
}
}
return price
}
Затем нам нужно разработать и написать конкретную операцию хеджирования ордера, которая должна быть разработана для размещения одновременных ордеров:
function hedge(buyEx, sellEx, price, amount) {
var buyRoutine = buyEx.Go("Buy", price, amount)
var sellRoutine = sellEx.Go("Sell", price, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Наконец, давайте завершим конструкцию функции баланса, которая немного сложна.
function keepBalance(initAccs, nowAccs, depths) {
var initSumStocks = 0
var nowSumStocks = 0
_.each(initAccs, function(acc) {
initSumStocks += acc.Stocks + acc.FrozenStocks
})
_.each(nowAccs, function(acc) {
nowSumStocks += acc.Stocks + acc.FrozenStocks
})
var diff = nowSumStocks - initSumStocks
// Calculate the currency difference
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
var index = -1
var available = []
var side = diff > 0 ? "Bids" : "Asks"
for (var i = 0 ; i < nowAccs.length ; i++) {
var price = getDepthPrice(depths[i], side, Math.abs(diff))
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
available.push(i)
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
available.push(i)
}
}
for (var i = 0 ; i < available.length ; i++) {
if (index == -1) {
index = available[i]
} else {
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
index = available[i]
} else if (priceIndex && priceI && priceI < priceIndex) {
index = available[i]
}
}
}
if (index == -1) {
Log("unable to balance")
} else {
// balance order
var price = getDepthPrice(depths[index], side, Math.abs(diff))
if (price) {
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
tradeFunc(price, Math.abs(diff))
} else {
Log("invalid price", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("errors:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
После разработки этих функций в соответствии с требованиями стратегии, затем начните разработку основной функции стратегии.
На платформе FMZ стратегия выполняется сmain
В началеmain
функция, мы должны сделать некоторые инициализации работы стратегии.
Имя объекта обмена Поскольку многие операции в стратегии должны использовать обменные объекты, такие как получение рыночных котировок, размещение заказов и так далее.
var exA = exchanges[0]
var exB = exchanges[1]
Это облегчает написание кода позже.
Обменный курс, конструкция, связанная с точностью
// precision, exchange rate settings
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Exchange A sets the exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Exchange B sets the exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Если параметры обменного курсаrateA
, rateB
устанавливаются на 1 (по умолчанию это 1), то естьrateA != 1
илиrateB != 1
не будет запускаться, поэтому конверсия валютного курса не будет установлена.
Сбросить все данные
Иногда необходимо удалить все журналы и очистить записанные данные при запуске стратегии.isReset
, и разработать код сброса в части инициализации стратегии, например:
if (isReset) { // When isReset is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#FF0000")
}
Восстановить исходные данные счета, обновить данные текущего счета
Для оценки баланса стратегия должна постоянно регистрировать активы на первоначальном счете для сравнения с текущим.nowAccs
используется для записи данных текущего счета, используя функцию, которую мы только что разработалиupdateAccs
чтобы получить данные счета текущей биржи.initAccs
используется для записи первоначального состояния счета (количество монет, количество деноминированных монет и т.д. на биржах А и В).initAccs
, используйте_G()
функция восстановить сначала (функция _G будет постоянно записывать данные, и может вернуть записанные данные снова, см. документацию API для деталей: [ссылка](https://www.fmz.com/api#_gk-v)), если запрос не работает, используйте информацию о текущем счете для присвоения значения и используйте_G
функция для записи.
Например, следующий код:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Код в основной петле - это процесс каждого раунда выполнения логики стратегии, который выполняется снова и снова, чтобы сформировать основную петлю стратегии.
Получение рыночных данных и оценка их достоверности
var ts = new Date().getTime()
var depthARoutine = exA.Go("GetDepth")
var depthBRoutine = exB.Go("GetDepth")
var depthA = depthARoutine.wait()
var depthB = depthBRoutine.wait()
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
Sleep(500)
continue
}
Здесь мы видим, что одновременная функцияexchange.Go
платформы FMZ используется для создания одновременных объектовdepthARoutine
, depthBRoutine
что называютGetDepth()
Когда эти два одновременных объектов создаются,GetDepth()
Интерфейс вызван немедленно, и оба запроса на глубину данных отправляются на обмен.
Тогда позвониwait()
МетодdepthARoutine
, depthBRoutine
объекты для получения данных о глубине.
После получения данных глубины необходимо проверить данные глубины для определения их достоверности.continue
Указание запускается для повторного выполнения основной петли.
Используйтеspread value
параметр илиspread ratio
Параметр?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
С точки зрения параметров, мы сделали такую конструкцию: параметры FMZ могут бытьпоказыватьилипрятатьсяна основе параметра, так что мы можем сделать параметр, чтобы решить, следует ли использоватьprice spread
, илиspread ratio
.
ПараметрdiffAsPercentage
Другие два параметра, которые должны быть отображены или скрыты на основе этого параметра:hedgeDiffPrice@!diffAsPercentage
, который отображается, когдаdiffAsPercentage
ложь.hedgeDiffPercentage@diffAsPercentage
, который отображается, когдаdiffAsPercentage
Это правда.
После этого дизайна мы проверилиdiffAsPercentage
параметр, который является условием запуска хеджирования на основе коэффициента разницы цен.diffAsPercentage
параметр проверен, хеджирование запускается разницей в цене.
Определить условия, запускающие хеджирование
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market conditions are met
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
Log("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // Tips
hedge(exB, exA, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A market conditions are met
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
Log("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // Tips
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Условия запуска хеджирования следующие:
isTrade
Здесь, если хеджирование запускается, переменная устанавливается наtrue
. И перезагрузить глобальную переменнуюlastKeepBalanceTS
на 0 (lastKeepBalanceTS используется для обозначения временной отметки последней операции по балансированию, установка на 0 немедленно запускает операцию по балансированию), а затем отменяет все ожидающие ордера.Операция балансировки
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
}
Можно заметить, что балансирующая функция выполняется периодически, но еслиlastKeepBalanceTS
Если после запуска операции хеджирования она будет сброшена до 0, то операция балансирования будет инициирована немедленно.
Информация из строки состояния
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
"current A, Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"current B, Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initial A, Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initial B, Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Построение строки состояния не является особенно сложным. Она отображает текущее время, разницу в ценах от биржи А к бирже В и разницу в ценах от биржи В к бирже А. Она также отображает текущий целевой спред хеджирования, данные активов счета биржи А и счета биржи В.
С точки зрения параметров, мы разработали параметр стоимости конверсионного курса, и мы также разработали конверсию обменного курса в начальной операцииmain
При этом необходимо отметить, чтоSetRate
Функция конвертации обменного курса должна быть выполнена сначала.
Потому что эта функция влияет на два аспекта:
BTC_USDT
, ценная единицаUSDT
, а наличная номинальная валюта в активах счета такжеUSDT
. Если я хочу конвертировать значение в CNY, установитьexchange.SetRate(6.8)
в коде для преобразования данных, полученных всеми функциями в соответствии сexchange
обменный объект на CNY.
Для конвертации в какую валютуобменный курс от текущей деноминированной валюты к целевой деноминированной валютекSetRate
function.Полная стратегия:Стратегия спотового хеджирования различных деноминированных валют (обучение)