Конструкция стратегии спотового хеджирования криптовалют ((1)

Автор:Лидия., Создано: 2022-08-16 10:30:56, Обновлено: 2023-09-19 21:46:16

img

Конструкция стратегии спотового хеджирования криптовалют ((1)

Стратегии хеджирования являются очень хорошими практическими стратегиями для начинающих в разработке стратегии.

Разработка некоторых функций и параметров интерфейса стратегии в соответствии с требованиями стратегии

Прежде всего, ясно, что стратегия, которая должна быть разработана, - это стратегия спотового хеджирования криптовалют. Мы разрабатываем самую простую стратегию хеджирования. Мы продаем на бирже с более высокой ценой только между двумя спотовыми биржами и покупаем на бирже с более низкой ценой, чтобы взять разницу. Когда биржи с более высокими ценами все номинальные монеты (потому что монеты с более высокими ценами продаются), а биржи с более низкими ценами все монеты (монеты с более низкими ценами покупаются), это не может быть хеджировано. В это время мы можем только ждать, пока перелом цены будет хеджироваться.

При хеджировании цена и количество ордера ограничиваются биржей, а также есть ограничение на минимальное количество ордера. В дополнение к минимальному пределу стратегия хеджирования также должна учитывать максимальный объем ордера за один раз. Если объем ордера слишком велик, объем ордера будет недостаточным. Также необходимо рассмотреть вопрос о том, как конвертировать обменный курс, если две валюты, деноминированные на бирже, отличаются. При хеджировании, комиссионная за обработку и скольжение заказчика - это все затраты на транзакцию, а не до тех пор, пока существует разница в цене, которую можно хеджировать. Поэтому разница в цене хеджирования также имеет триггерное значение. Если она ниже определенной разницы в цене, хеджирование потеряет.

Исходя из этих соображений, стратегия должна быть разработана с учетом нескольких параметров:

  • Различие хеджирования:hedgeDiffPrice, когда разница превышает это значение, операция хеджирования запускается.
  • Минимальная сумма хеджирования:minHedgeAmount, минимальная сумма ордера (монеты), которая может быть хеджирована.
  • Максимальная сумма хеджирования:maxHedgeAmount, максимальная сумма ордера (монеты) для одного хеджирования.
  • Точность цены А:pricePrecisionA, точность цены заказа (количество запятой), размещенной биржей А.
  • Точность величины A:amountPrecisionA, точная сумма заказа, размещенного биржей A (количество запятой).
  • Точность цены B:pricePrecisionB, точность цены заказа (количество запятой), размещенной биржей B.
  • Точность количества B:amountPrecisionB, точная сумма заказа, размещенного биржей B (количество запятой).
  • Обменный курс А:rateA, конвертация валютного курса первого добавленного обменного объекта, по умолчанию 1, не конвертирована.
  • Курс B:rateB, конвертация валютного курса второго добавленного обменного объекта, по умолчанию 1, не конвертирована.

Стратегия хеджирования должна сохранять количество монет на двух счетах неизменным (то есть не держать позиции в любом направлении и поддерживать нейтралитет), поэтому в стратегии должна быть логика баланса, чтобы всегда обнаруживать баланс. При проверке баланса неизбежно получать данные о активах с двух бирж. Нам нужно написать функцию для использования.

  • обновлениеAccs
    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)
                }
            }
        })
    }
    

При сбалансировании количества монет, нам нужно найти цену, накопленную на определенное количество монет в определенной глубине данных, так что нам нужна такая функция, чтобы справиться с этим.

  • GetDepthPrice
    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не будет запускаться, поэтому конверсия валютного курса не будет установлена.

  • Сбросить все данные

    img

    Иногда необходимо удалить все журналы и очистить записанные данные при запуске стратегии.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.

    img

    Параметр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 
              }            
          }
    

    Условия запуска хеджирования следующие:

    1. В первую очередь, следует соблюдать спред хеджирования, и только тогда, когда спред ордера соответствует установленным параметрам спреда, он может быть хеджирован.
    2. Сумма, которая может быть хеджирована на рынке, должна соответствовать минимальной сумме хеджирования, установленной в параметрах.
    3. Актив, участвующий в обмене операции продажи, достаточно для продажи, а актив, участвующий в обмене операции покупки, достаточно для покупки. Когда эти условия выполнены, выполняется функция хеджирования для размещения ордера хеджирования.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.

Полная стратегия:Стратегия спотового хеджирования различных деноминированных валют (обучение)


Связанные

Больше