資源の読み込みに... 荷物...

"C++版 OKEXの契約ヘッジ戦略"

作者: リン・ハーン発明者 量化 - 微かな夢, 作成日:2019-08-26 14:30:47, 更新日:2023-10-19 21:09:01

img

"C++版 OKEXの契約ヘッジ戦略"

ハードジング戦略について言えば,様々な戦略,様々な組み合わせ,さまざまな考えが各市場で存在します.我々は最も古典的な跨期ヘッジから,ヘッジング戦略の設計思想や理念を模索しました.今日,デジタル通貨市場は,市場が形成されたときよりもはるかに活発であり,多くのコントラクト取引所が出現し,多くの利便性ヘッジング機会を提供しています.現貨跨市場利息,現期ヘッジング利息,フューチャー跨期利息,フューチャー跨市場利息など,戦略の層は無限です.我々は一緒にC++言語で記述された概念を理解しました.取引市場OKEX契約取引所の"ハードコア"跨期ヘッジング戦略,策略は開発者"インヴェンダー・トレード・プラットフォーム"に基づいています.

  • 戦略の原理

    戦略はC++言語で記述され,読み難度が少し高いため,なぜ戦略がハードコアなのか.しかし,この戦略のデザインや考え方の精髓を学ぶことを妨げない.戦略の概要は簡潔で,コードの長さは適度で,わずか500行以上である.市場データ取得については,以前の戦略の rest インターフェースを使用した戦略とは異なり,この戦略はwebsocket インターフェースを使用して,取引所からの取引を転送する. デザイン面では,戦略構造は合理的で,コード合度が低く,拡張または最適化が容易である.論理的思考が明確であるため,このようなデザインは使用や拡張が容易であるだけでなく,教育戦略として,学習戦略設計も良い例である.戦略原理は比較的単純である.すなわち,長期契約と近期契約の正し,反セットヘッジ,基本的には商品先物との長期間のヘッジに一致する. 仮面,空間の長期契約,近期契約. 長期契約や短期契約を空にする. 基本原理が明らかになった後,残っているのは,どのように戦略を動かすか,どのように平衡するか,どのように上場するか. ポジション制御方法,戦略の詳細処理. ヘッジ戦略は,主に指数の差の変動を中心に,差の回転取引を行う.しかし,差は,小幅な波動,または大幅な波動,または一方的な方向の可能性がある. これは,ヘッジ利益損失の不確実性をもたらしますが,リスクは一方的な傾向よりもはるかに小さい. 長期戦略の様々な最適化には,多くの場合,ポジション制御レベルから始め,開拓平止を誘発する選択があります. 例えば,クラシックなブリン指標を分散波動として使用し,正面,反面の開拓,平止点. この戦略は合理的に設計され,合度が低いため,簡単にブリン指標の跨期ヘッジ戦略に改定することができます.

  • 策略コード解析

    概して,コードを見てみると,コードは主に4つの部分に分かれていると結論付けることができます.

    • 1. リスト値定義,状態値を定義し,状態を表示する. いくつかの機能関数,例えばURLコード関数,時間変換関数など,この関数と戦略関数には関係なく,データ処理にのみ使用される.
    • 2.K线数据生成器类:策略由该生成器类对象生成的K线数据驱动。
    • 3.对冲类:该类的对象可以执行具体的交易逻辑,对冲操作、策略细节的处理机制等。
    • 4.策略主函数,也就是 mainこの関数は,main関数は,戦略の入力関数であり,主要な回路がこの関数内で実行される.また,この関数内で重要な操作が実行される.これは,取引所のwebsocketインターフェースにアクセスし,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, 16 桁の文字に変換する関数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線に処理し,戦略的ヘッジロジックを駆動する責任がある. 疑問を持つ読者はいるかもしれません.なぜ,ティックデータを使うのか?なぜ,このようなK線データジェネレーターを構築するのか?直接K線データを使うのは不良ですか?このような疑問は,何度も,私がいくつかのヘッジ戦略を書き始めたときも,浮上しています. 2つの契約の差値のK線データとは,一定の周期間の差値変化の統計である.したがって,2つの契約のそれぞれのK線データを単純に減法処理して,各K線Bar上の各データの差値を計算して,差値として計算することはできません. このような最も明らかな誤りは,例えば,2つの契約の最高価格,最低価格が必ずしも同じ瞬間である必要はありません.したがって,減算した値はそれほど重要ではありません. そのためには,リアルタイムでタックデータを利用し,リアルタイムで差値を計算し,リアルタイムで周期内の価格変動を統計する必要があります (つまり,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を省略し,主にオブジェクト初期化である.残っているのは,主に2つの機能関数である.

      • 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接口 推送来的数据
              
              ...
              
      }
      

      メイン関数では,ウェブソケットインターフェースのティック行列をサブスクリプトする. メイン関数の主な仕事は,主回路を構成し,継続的に取引所ウェブソケットインターフェースから送信されたティック行列を受信し,ヘッジタイプオブジェクトのメンバー関数:ループ関数,ループ関数の取引論理を呼び出すことである. 上記で述べたティック市場は,実際にはサブスクリプションのオーダー薄深度データインターフェースであり,各アーカイブのオーダー薄データを取得している.しかし,戦略は最初のアーカイブのデータのみを使用していますが,実際にはティック市場のデータとほぼ同じです.戦略は他のアーカイブのデータを使用していません. 詳細については,WEBソケットインターフェースのデータへのサブスクリプションのポリシー,および設定方法を参照してください.

      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");
      

      JSONのパラメータは,JSONのインターフェースから送信されたサブスクリプションメッセージの urlをコードします.payload重要なステップは,発明者の量化取引プラットフォームの API インターフェース関数を呼び出すことです.Dialこの関数は,Dialこの関数は,取引所の websocket インターフェースにアクセスするために使用されます. ここで,作成する websocket 接続制御オブジェクト ws に断線自動再接続 (サブスクリプションメッセージはまだ payload 参数の値 qs 文字列を使用します) を設定します.Dial関数の参数文字列に設定オプションを追加します.

      Dial函数参数の開き部分には,以下のようなものがあります.

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

      Webソケットのインターフェースのアドレスです.|隔離した.compress=gzip_raw&mode=recv&reconnect=true&payload="+qs設定のパラメータは全て、

      パラメータ名 記述
      圧縮する compress は圧縮モードで,OKEX websocket インターフェイスは gzip_raw を使用している.このモードは,gzip_raw に設定されている.
      モード mode はモード,選択可能な dual, send, recv 三種類である。dual は双方向で,圧縮データを送信し,圧縮データを受信する。send は圧縮データを送信する。recv は圧縮データを受信し,ローカル解圧する。
      再接続する reconnect は再接続を設定するかどうか,reconnect=true は再接続を有効にするか,デフォルトでは再接続を設定しないかを表示する.
      パイロード payload は ws に再接続されたときに送信されるサブスクリプションメッセージである.

      この設定では,Webソケット接続が切断された場合でも,発明者定量化取引プラットフォームのホスト層のシステムも自動的に再接続し,最新の市場データをタイムリーに入手します. 価格変動を把握し,適切なヘッジ市場を迅速に把握する.

  • ポジション制御

    ポジションコントロールは",ポフィナッチ"のような数列のヘッジポジション比率を用いて制御する.

    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数据结构中。
    }
    

    増やされたポジションの数が,前2つのポジションの合計であることがわかります. このようなポジション制御は,差が大きいほど,利息のヘッジの数が相対的に増加するほど,ポジションを分散させ,小差の波動を把握し,小差の波動を適切に増加させることができる.

  • 平仓:停止損失停止

    固定ストップ価格,ストップ損失価格 持株差が値に達し,停止損失位置に達すると,停止損失を停止します.

  • 市場投入,市場離脱 サイクルデザイン

    パラメータ 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 の参照を参数として入力します.

    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)。
        } 
    }
    

    グラフオブジェクト _c を呼び出し,addメンバー関数,新しいK線Barデータをグラフに挿入します. コード:c->add(chartIdx, point);

  • 復習する

    img

    img

    img

このポリシーは学習交流のみの目的で使用されます.実用盤を使用する場合は,実用盤の実態状況に応じて自作で修正して最適化してください.

戦略アドレスは:https://www.fmz.com/strategy/163447

発明者による量化取引のプラットフォームで,もっと面白い戦略を紹介します.https://www.fmz.com


関連性

もっと