В процессе загрузки ресурсов... загрузка...

"Контрактная стратегия хеджирования OKEX в C++".

Автор:Изобретатели количественного измерения - мечты, Создано: 2019-08-26 14:30:47, Обновлено: 2024-12-17 20:43:39

img

"Контрактная стратегия хеджирования OKEX в C++".

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

  • Принципы стратегии

    Причина, по которой тактика выглядит довольно жесткой, заключается в том, что она написана на языке C++, и ее чтение немного сложно. Однако это не мешает читателю изучить суть стратегии. С точки зрения дизайна, стратегия имеет разумную структуру, низкую степень сплоченности кода, легко расширяется или оптимизируется; логическая мысль ясна, такая конструкция не только удобна для использования и расширения; в качестве учебной стратегии, стратегический дизайн также является хорошим примером. Принцип стратегии прост, т. е. долгосрочные контракты и краткосрочные контракты являются корректировкой, контрактным хеджированием, в принципе, соответствующим долгосрочному хеджированию товарных фьючерсов. В этом случае вы должны быть готовы к тому, что вы получите более высокую зарплату, чем в прошлом. В то же время, многие из них не хотят, чтобы их отношения с другими странами стали более напряженными. После того, как основные принципы прояснены, остается решать стратегию, как запустить хеджирование, как выравнить позиции, как увеличить позиции. Хеджирующая стратегия в основном сосредоточена на колебаниях цены на товар, и торгует обратной ценой. Однако возможны небольшие колебания, или значительные колебания, или односторонние колебания. Это приводит к неопределенности прибыли и убытка хеджирования, но риски все же намного меньше, чем односторонние тенденции. Для различных оптимизаций долгосрочной стратегии многие варианты выбираются с уровня контроля позиций, с триггера открытия позиций. Например, классический индикатор Бринн используется в качестве дифференцированного вариации цены.

  • Анализ стратегического кода

    Если посмотреть на код, то можно сделать вывод, что код, вероятно, состоит из четырех частей.

    • Определение значения списка, определение некоторых состояний, используемых для маркировки состояния. Некоторые функциональные функции, не связанные с стратегией, такие как функции кодирования url, функции преобразования времени и т. д., не имеют отношения к стратегии и используются только для обработки данных.
    • 2.K线数据生成器类:策略由该生成器类对象生成的K线数据驱动。
    • 3.对冲类:该类的对象可以执行具体的交易逻辑,对冲操作、策略细节的处理机制等。
    • 4.策略主函数,也就是 mainФункция.mainФункция является входной функцией стратегии, в которой выполняется основной цикл, а также выполняется важная операция, которая заключается в доступе к интерфейсу веб-сокета биржи, получении данных о движении тика, которые используются в качестве исходных данных для генератора данных K-линии.

    Изучив стратегический код в целом, мы сможем изучить его дизайн, идеи и приемы, проанализировав его шаг за шагом.

    • Определение цитируемых значений, другие функциональные функции

      1 StateЗаявление

      enum State {                    // 枚举类型  定义一些 状态
          STATE_NA,                   // 非正常状态
          STATE_IDLE,                 // 空闲
          STATE_HOLD_LONG,            // 持多仓
          STATE_HOLD_SHORT,           // 持空仓
      };
      

      Поскольку некоторые функции в коде возвращают определенное состояние, то все эти состояния определяются в списке типов.StateВ центре. Посмотрите, как это выглядит.STATE_NAЭто не нормальное состояние.STATE_IDLEДля состояния пустого состояния, то есть состояния, в котором можно использовать хеджирование.STATE_HOLD_LONGДля держателей позиций, имеющих правильные позиции хеджирования.STATE_HOLD_SHORTСтатус позиции, удерживаемой для хеджирования.

      2, замена строки, не вызванная в этой политике, является запасной инструментальной функцией, которая в основном обрабатывает строки.

      string replace(string s, const string from, const string& to)   
      

      Функция 3, которая преобразуется в шестнадцатизначнуюtoHex

      inline unsigned char toHex(unsigned char x)
      

      4, функция для обработки URL-кода

      std::string urlencode(const std::string& str)
      

      5, функция преобразования времени, которая преобразует время в формате строки в временные буквы.

      uint64_t _Time(string &s)
      
    • Класс K-линейных генераторов данных

      class BarFeeder {                                                                       // K线 数据生成器类
          public:
              BarFeeder(int period) : _period(period) {                                       // 构造函数,参数为 period 周期, 初始化列表中初始化
                  _rs.Valid = true;                                                           // 构造函数体中初始化 K线数据的 Valid属性。
              }    
      
              void feed(double price, Chart *c=nullptr, int chartIdx=0) {                     // 输入数据,nullptr 空指针类型,chartIdx 索引默认参数为 0
                  uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000;               // 秒级时间戳祛除不完整时间周期(不完整的_period 秒数),转为 毫秒级时间戳。
                  bool newBar = false;                                                        // 标记 新K线Bar 的标记变量
                  if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) {                    // 如果 K线数据 长度为 0 。 或者 最后一bar 的时间戳小于 epoch(K线最后一bar 比当前最近的周期时间戳还要靠前)
                      Record r;                                                               // 声明一个 K线bar 结构
                      r.Time = epoch;                                                         // 构造当前周期的K线bar 
                      r.Open = r.High = r.Low = r.Close = price;                              // 初始化 属性
                      _rs.push_back(r);                                                       // K线bar 压入 K线数据结构
                      if (_rs.size() > 2000) {                                                // 如果K线数据结构长度超过 2000 , 就剔除最早的数据。
                          _rs.erase(_rs.begin());
                      }
                      newBar = true;                                                          // 标记
                  } else {                                                                    // 其它情况,不是出现新bar 的情况下的处理。
                      Record &r = _rs[_rs.size() - 1];                                        // 引用 数据中最后一bar 的数据。
                      r.High = max(r.High, price);                                            // 对引用数据的最高价更新操作。
                      r.Low = min(r.Low, price);                                              // 对引用数据的最低价更新操作。
                      r.Close = price;                                                        // 对引用数据的收盘价更新操作。
                  }
          
                  auto bar = _rs[_rs.size()-1];                                               // 取最后一柱数据 ,赋值给 bar 变量
                  json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.Close};            // 构造一个 json 类型数据
                  if (c != nullptr) {                                                         // 图表对象指针不等于 空指针,执行以下。
                     if (newBar) {                                                            // 根据标记判断,如果出现新Bar 
                          c->add(chartIdx, point);                                            // 调用图表对象成员函数add,向图表对象中插入数据(新增K线bar)
                          c->reset(1000);                                                     // 只保留1000 bar的数据
                      } else {
                          c->add(chartIdx, point, -1);                                        // 否则就更新(不是新bar),这个点(更新这个bar)。
                      } 
                  }
              }
              Records & get() {                                                               // 成员函数,获取K线数据的方法。
                  return _rs;                                                                 // 返回对象的私有变量 _rs 。(即 生成的K线数据)
              }
          private:
              int _period;
              Records _rs;
      };
      

      Этот класс отвечает в основном за обработку полученных данных тика в K-линии разницы, которые используются для управления логикой стратегического хеджирования. Некоторые читатели могут задаться вопросом, зачем использовать данные tick? Зачем создавать такой генератор данных K-линии? Нехорошо ли использовать данные K-линии напрямую? В то время как данные на K-линии разницы между двумя контрактами представляют собой статистику изменения цены разницы в течение определенного периода, поэтому нельзя просто взять данные на K-линии двух контрактов для проведения вычитания, чтобы вычислить разницу между данными на каждой K-линии Bar. Таким образом, нам нужно использовать реальные данные о тиках, расчеты разницы в реальном времени, реальные статистические данные о ценовых изменениях в течение определенного цикла (т.е. высокие и низкие уровни в столбцах K); таким образом, нам нужен генератор данных K-линий, отдельно как класс, который хорошо обрабатывает логическое разделение.

    • Хеджирующий класс

      class Hedge {                                                                           // 对冲类,策略主要逻辑。
        public:
          Hedge() {                                                                           // 构造函数
              ...
          };
          
          State getState(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB) {        // 获取状态,参数: 合约A名称 、合约A深度数据, 合约B名称、 合约B深度数据
              
              ...
          }
          bool Loop(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB, string extra="") {       // 开平仓 策略主要逻辑
              
              ...
          }    
      
        private:
          vector<double> _addArr;                                     // 对冲加仓列表
          string _state_desc[4] = {"NA", "IDLE", "LONG", "SHORT"};    // 状态值 描述信息
          int _countOpen = 0;                                 // 开仓次数
          int _countCover = 0;                                // 平仓次数
          int _lastCache = 0;                                 // 
          int _hedgeCount = 0;                                // 对冲次数
          int _loopCount = 0;                                 // 循环计数(循环累计次数)
          double _holdPrice = 0;                              // 持仓价格
          BarFeeder _feederA = BarFeeder(DPeriod);            // A合约 行情 K线生成器
          BarFeeder _feederB = BarFeeder(DPeriod);            // B合约 行情 K线生成器
          State _st = STATE_NA;                               // 对冲类型 对象的 对冲持仓状态
          string _cfgStr;                                     // 图表配置 字符串
          double _holdAmount = 0;                             // 持仓量
          bool _isCover = false;                              // 是否平仓 标记
          bool _needCheckOrder = true;                        // 设置是否 检查订单
          Chart _c = Chart("");                               // 图表对象,并初始化
      };
      
      
      

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

      • GetState

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

      • Кружок

        В этой функции, в которой вызывается логика транзакций стратегии,getStateИспользуя объекты K-линейного генератора данных, можно генерировать K-линейные данные разницы, для того, чтобы выполнить определение логики хеджирования.

    • Главные функции стратегии

      void main() {  
      
          ...
          
          string realSymbolA = exchange.SetContractType(symbolA)["instrument"];    // 获取设置的A合约(this_week / next_week / quarter ) ,在 OKEX 合约 当周、次周、季度 对应的真实合约ID 。
          string realSymbolB = exchange.SetContractType(symbolB)["instrument"];    // ...
          
          string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump());    // 对 ws 接口的要传的参数进行 json 编码、 url 编码
          Log("try connect to websocket");                                                                                                            // 打印连接 WS接口的信息。
          auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs);     // 调用FMZ API Dial 函数 访问  OKEX 期货的 WS 接口
          Log("connect to websocket success");
          
          Depth depthA, depthB;                               // 声明两个 深度数据结构的变量 用于储存A合约和B合约 的深度数据
          auto fillDepth = [](json &data, Depth &d) {         // 用接口返回的json 数据,构造 Depth 数据的代码。
              d.Valid = true;
              d.Asks.clear();
              d.Asks.push_back({atof(string(data["asks"][0][0]).c_str()), atof(string(data["asks"][0][1]).c_str())});
              d.Bids.clear();
              d.Bids.push_back({atof(string(data["bids"][0][0]).c_str()), atof(string(data["bids"][0][1]).c_str())});
          };
          string timeA;   // 时间 字符串 A 
          string timeB;   // 时间 字符串 B 
          while (true) {
              auto buf = ws.read();                           // 读取 WS接口 推送来的数据
              
              ...
              
      }
      

      После запуска политика выполняется с функции main, которая в процессе инициализации функции main подписывается на тики интерфейса websocket. Основная задача функции main заключается в том, чтобы составить главный цикл, постоянно принимать тики, отправленные интерфейсом websocket, а затем вызывать члены объекта типа хеджирования: Loop-функции. Стоит отметить, что упомянутый в вышеприведенном тексте рынок клейков, на самом деле, представляет собой интерфейс глубокой информации о заказе подписки, который получает информацию о заказе в каждом файле. Однако в стратегии используются только данные из первого файла, фактически это почти то же самое, что и данные рынка клейков. Подробно о том, как настроить политику подписки на данные интерфейса websocket, читайте здесь.

      string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump());    
      Log("try connect to websocket");                                                                                                            
      auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs);     
      Log("connect to websocket success");
      

      Сначала нужно кодировать url в json-параметре для сообщения об подписке, передаваемого через интерфейс подписки, т.е.payloadЗначение параметров. Затем более важным шагом является вызов API-интерфейсной функции для квантовой платформы изобретателя.DialФункция.DialФункция может быть использована для доступа к интерфейсу websocket обмена. Мы здесь делаем некоторые настройки, чтобы создаваемый websocket соединялся с объектом управления ws с автоматическим пересоединением (подписка все еще использует строку qs значения параметров payload).DialДобавление параметровых параметров в строку функций.

      DialПервая часть параметра функции выглядит следующим образом:

      wss://real.okex.com:10442/ws/v3
      

      Это адрес интерфейса веб-сокета, к которому нужно получить доступ, а затем использовать|Разделение.compress=gzip_raw&mode=recv&reconnect=true&payload="+qsВсе параметры настроек.

      Имя параметра Описание
      компресс compress как компрессионный режим, интерфейс OKEX websocket использует gzip_raw.
      режим mode - режим, выбор dual, send, recv Три вида; dual - двусторонний, отправляет сжатые данные, получает сжатые данные;; send - отправляет сжатые данные;;recv - получает сжатые данные, локально разжимает;
      перезагрузить reconnect для установки повторного подключения или нет, reconnect=true для включения повторного подключения, без установки по умолчанию повторного подключения;
      полезная нагрузка payload - это подписка, которую нужно отправить при перезагрузке ws.

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

  • Контроль позиций

    Контроль позиций выполняется с использованием пропорций позиций хеджирования, аналогичных числу рядов "Пофинача".

    for (int i = 0; i < AddMax + 1; i++) {                                          // 构造 控制加仓数量的数据结构,类似 波菲纳契数列 对冲数量 比例。
        if (_addArr.size() < 2) {                                                   // 前两次加仓量变化为: 加一倍对冲数量 递增
            _addArr.push_back((i+1)*OpenAmount);
        }
        _addArr.push_back(_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]);   // 最后 两个加仓数量相加,算出当前的加仓数量储存到 _addArr数据结构中。
    }
    

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

  • Плотное положение: остановка убытков

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

  • Вход в рынок, выход из него, циклический дизайн

    Параметры NPeriod Контролируемый цикл осуществляет определенный динамический контроль над стратегией открытия позиций.

  • Стратегический график

    Стратегия автоматически генерирует диаграмму K-линий разницы, маркирующую соответствующую информацию о сделках.

    C++-политики настраивать графический рисунок также очень просты, как видно из конструкторской функции для класса хеджирования, мы использовали хорошо написанную графическую конфигурацию строки_cfgStr, которая конфигурируется для объекта_c, _c является частным членом класса хеджирования.ChartГрафические объекты, построенные функциями.

    _cfgStr = R"EOF(
    [{
    "extension": { "layout": "single", "col": 6, "height": "500px"},
    "rangeSelector": {"enabled": false},
    "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"},
    "plotOptions": {"candlestick": {"color": "#d75442", "upColor": "#6ba583"}},
    "chart":{"type":"line"},
    "title":{"text":"Spread Long"},
    "xAxis":{"title":{"text":"Date"}},
    "series":[
        {"type":"candlestick", "name":"Long Spread","data":[], "id":"dataseriesA"},
        {"type":"flags","data":[], "onSeries": "dataseriesA"}
        ]
    },
    {
    "extension": { "layout": "single", "col": 6, "height": "500px"},
    "rangeSelector": {"enabled": false},
    "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"},
    "plotOptions": {"candlestick": {"color": "#d75442", "upColor": "#6ba583"}},
    "chart":{"type":"line"},
    "title":{"text":"Spread Short"},
    "xAxis":{"title":{"text":"Date"}},
    "series":[
        {"type":"candlestick", "name":"Long Spread","data":[], "id":"dataseriesA"},
        {"type":"flags","data":[], "onSeries": "dataseriesA"}
        ]
    }
    ]
    )EOF";
    _c.update(_cfgStr);                 // 用图表配置 更新图表对象
    _c.reset();                         // 重置图表数据。
    
    • Звонок_c.update(_cfgStr);Используйте _cfgStr для настройки на объекты диаграммы.
    • Звонок_c.reset();Перезагрузка графических данных.

    В случае, когда в стратегическом коде требуется вставлять данные в график, это также может быть сделано путем прямого вызова членской функции объекта _c или передачи ссылки на _c в качестве параметра, а затем вызова членской функции объекта _c. Например:

    _c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action},  {"text", format("diff: %f", opPrice)}, {"color", color}});
    

    После подписания сделки на K-линейном графике печатается запись с этикетками.

    В данном случае, изображение K-строки является членом класса BarFeeder, используя его.feedВ этом случае, если вы используете _c, то это означает, что вы используете _c для обозначения.

    void feed(double price, Chart *c=nullptr, int chartIdx=0)
    

    То естьfeedДифференциальный параметр функции c.

    json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.Close};            // 构造一个 json 类型数据
    if (c != nullptr) {                                                         // 图表对象指针不等于 空指针,执行以下。
       if (newBar) {                                                            // 根据标记判断,如果出现新Bar 
            c->add(chartIdx, point);                                            // 调用图表对象成员函数add,向图表对象中插入数据(新增K线bar)
            c->reset(1000);                                                     // 只保留1000 bar个数据
        } else {
            c->add(chartIdx, point, -1);                                        // 否则就更新(不是新bar),这个点(更新这个bar)。
        } 
    }
    

    Название объекта _caddФункция-член, вставляющая новые данные K-линии Bar в график. Код:c->add(chartIdx, point);

  • Проверка

    img

    img

    img

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

Политический адрес:https://www.fmz.com/strategy/163447

Более интересные стратегии можно найти в статье "Изобретатели количественных торговых площадок":https://www.fmz.com


Связанные

Больше