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

Автор:Нинабадасс., Создано: 2022-04-14 11:57:46, Обновлено: 2022-04-14 16:32:56

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

Для новичков в разработке стратегии, стратегия хеджирования является очень хорошей для практики.

Функции проектирования и параметры по стратегическим требованиям

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

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

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

  • Пространство хеджирования:hedgeDiffPrice; когда спред превышает значение, запускается хеджирование.
  • Минимальная сумма хеджирования:minHedgeAmount, минимальная сумма ордера (сумма символа), доступная для хеджирования.
  • Максимальная сумма хеджирования:maxHedgeAmount, максимальная сумма ордера (сумма символа), доступная для хеджирования.
  • Точность цены А:pricePrecisionA, точность цены заказа (десятизначная цифра) платформы А.
  • Точность суммы заказа A:amountPrecisionA, точность величины заказа (десятизначная цифра) платформы А.
  • Точность цены 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 currency spread 
        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("cannot balance")            
            } else {
                // balanced ordering 
                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("error:", "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]
    

    Тогда будет удобнее написать код позже.

  • Обменный курс и точность

      // settings of precision and exchange rate
      if (rateA != 1) {
          // set exchange rate A 
          exA.SetRate(rateA)
          Log("Platform A sets exchange rate:", rateA, "#FF0000")
      }
      if (rateB != 1) {
          // set exchange rate B
          exB.SetRate(rateB)
          Log("Platform B sets 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используется для записи исходного состояния счета (данные, такие как сумма валютного символа как 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 
          }
    

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

  • Использованиеprice spreadили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
          }
    

    С точки зрения параметров, мы сделали такую конструкцию.показыватьилипрятатьсяна основе параметра, так что мы можем сделать параметр, чтобы решить, следует ли использовать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 condition satisfied             
              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("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks)  // prompt message 
                  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 condition satisfied 
              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("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks)  // prompt message
                  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", 
              "currentA,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n", 
              "currentB,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n", 
              "initialA,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n", 
              "initialB,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
    

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

Обработка торговых пар для различных валют котировок

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

Для функции будет влиять два аспекта:

  • Преобразование цен во всех данных о котировках на рынке, данных о заказах и данных о позициях.
  • Преобразование валют котировок в активы счета.

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

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


Больше