4
Подписаться
1271
Подписчики

Разработка стратегии хеджирования спотовых цифровых валют (1)

Создано: 2021-07-19 17:38:24, Обновлено: 2023-09-20 10:35:16
comments   1
hits   3142

Разработка стратегии хеджирования спотовых цифровых валют (1)

Разработка стратегии хеджирования спотовых цифровых валют (1)

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

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

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

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

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

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

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

  • updateAccs
    
    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 
    }
    

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

  • cancelAll
    
    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
    }
    

Затем нам необходимо разработать и написать конкретную операцию хеджирования ордера, которая должна быть спроектирована как параллельный ордер:

  • hedge
    
    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()
    }
    

Наконец, давайте завершим разработку функции баланса, которая немного сложна.

  • keepBalance

    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
      // 计算币差
      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("无法平衡")            
          } else {
              // 平衡下单
              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("价格无效", price)
              }
          }        
          return false
      } else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
          Log("错误:", "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]

Это значительно облегчает последующее написание кода.

  • Проектирование, связанное с курсом обмена и точностью
    // 精度,汇率设置
    if (rateA != 1) {
        // 设置汇率A
        exA.SetRate(rateA)
        Log("交易所A设置汇率:", rateA, "#FF0000")
    }
    if (rateB != 1) {
        // 设置汇率B
        exB.SetRate(rateB)
        Log("交易所B设置汇率:", rateB, "#FF0000")
    }
    exA.SetPrecision(pricePrecisionA, amountPrecisionA)
    exB.SetPrecision(pricePrecisionB, amountPrecisionB)

Если параметр обменного курсаrateArateBНекоторые установлены на 1 (по умолчанию 1), т.е.rateA != 1илиrateB != 1Не сработает, поэтому конвертация обменного курса не будет установлена.

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

Разработка стратегии хеджирования спотовых цифровых валют (1)

Иногда при запуске политики необходимо удалить все журналы и очистить записанные данные. Вы можете разработать параметр интерфейса стратегииisReset, а затем спроектируйте код сброса в разделе инициализации стратегии, например:

    if (isReset) {   // 当isReset为真时重置数据
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("重置所有数据", "#FF0000")
    }
  • Восстановить исходные данные учетной записи и обновить текущие данные учетной записи Для определения баланса стратегия должна постоянно регистрировать начальные активы счета для сравнения с текущей ситуацией.nowAccsЭта переменная используется для записи данных текущего счета с помощью функции, которую мы только что разработали.updateAccsПолучите данные аккаунта текущей биржи.initAccsИспользуется для записи начального состояния счета (таких данных, как количество монет и количество номинальных монет биржи A и биржи B). дляinitAccsПервое использование_G()Восстановление функции (_Функция G будет записывать данные постоянно и может вернуть записанные данные снова. Подробности см. в документации API:Связь), если запрос не выполнен, используйте информацию о текущем счете, чтобы назначить значение и использовать_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 
        }

Здесь вы можете увидеть параллельные функции платформы FMZ.exchange.Go, создал вызовGetDepth()Объект параллелизма интерфейсаdepthARoutinedepthBRoutine. Когда эти два параллельных объекта созданы, вызовитеGetDepth()Интерфейс также появился немедленно, и на биржу было отправлено два запроса на получение данных о глубине. Тогда позвонитеdepthARoutinedepthBRoutineОбъектwait()Метод получения данных о глубине.
После получения данных о глубине необходимо проверить их на достоверность. Выполнение триггера при аномалиях данныхcontinueОператор повторно выполняет основной цикл.

  • использовать价差值Параметры или差价比例параметр?
        var targetDiffPrice = hedgeDiffPrice
        if (diffAsPercentage) {
            targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
        }

Мы сделали такую ​​конструкцию по параметрам. Параметры ФМЗ могут быть основаны на определенном параметрепоказыватьилискрывать, поэтому мы можем создать параметр, который решит, использовать ли价格差,все еще差价比例

Разработка стратегии хеджирования спотовых цифровых валют (1)

В параметры интерфейса стратегии добавлен параметрdiffAsPercentage. Два других параметра, которые отображаются или скрываются на основе этого параметра, устанавливаются следующим образом: hedgeDiffPrice@!diffAsPercentage,когдаdiffAsPercentageЗначение False отображает этот параметр. hedgeDiffPercentage@diffAsPercentage,когдаdiffAsPercentageTrue для отображения этого параметра. После этого дизайна мы проверили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 盘口条件满足            
            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("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks)  // 提示信息
                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 盘口条件满足
            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("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks)  // 提示信息
                hedge(exA, exB, price, amount)
                cancelAll()
                lastKeepBalanceTS = 0
                isTrade = true 
            }            
        }

Существует несколько условий срабатывания хеджирования: 1. Во-первых, удовлетворите разницу в цене хеджирования. Хеджирование может быть выполнено только тогда, когда разница в цене на рынке соответствует установленным параметрам разницы в цене. 2. Объем хеджирования на рынке должен соответствовать минимальному объему хеджирования, установленному в параметрах. Поскольку на разных биржах могут быть разные минимальные объемы ордеров, следует брать наименьший из двух. 3. На бирже достаточно активов для операций по продаже, чтобы продать, и на бирже достаточно активов для операций по покупке, чтобы купить. При выполнении этих условий выполняется функция хеджирования для размещения ордера на хеджирование. Перед основной функцией мы заранее объявили переменнуюisTradeИспользуется для обозначения того, происходит ли хеджирование. Если хеджирование срабатывает, эта переменная устанавливается вtrue. И сбросить глобальные переменныеlastKeepBalanceTSУстановите 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", 
            "当前A,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n", 
            "当前B,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n", 
            "初始A,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n", 
            "初始B,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)

Строка состояния не особенно сложна по дизайну. Она отображает текущее время, разницу в цене между биржей A и биржей B, а также разницу в цене между биржей B и биржей A. Отображает текущий целевой спред хеджирования. Отображает данные об активах биржевого счета A и биржевого счета B.

Обработка торговых пар с разными номинированными валютами

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

  • Конвертация цен во все рыночные данные, данные о заказах и данные о позициях.
  • Конвертация деноминированной валюты в активы счета. Например, текущая торговая пара —BTC_USDT, единицы ценыUSDTДоступная валюта в активах счета составляетUSDT. Если я хочу конвертировать его в CNY, укажите это в кодеexchange.SetRate(6.8)ТолькоexchangeДанные, полученные всеми функциями в рамках этого объекта обмена, конвертируются в CNY. Почему для конвертации используется именно эта валюта?SetRateПередача функцииОбменный курс из текущей валюты в целевую валюту

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