"C++ OKEX Contract Hedging Strategy" yang membawa Anda belajar strategi hardcore.

Penulis:Mimpi kecil, Dibuat: 2019-08-26 14:30:47, Diperbarui: 2023-10-19 21:09:01

img

"C++ OKEX Contract Hedging Strategy" yang membawa Anda belajar strategi hardcore.

Berbicara tentang strategi hedging, ada banyak strategi yang berbeda, banyak kombinasi yang berbeda, banyak ide yang berbeda di setiap pasar. Kami mulai dari hedging jangka panjang yang paling klasik untuk mendesain ide dan konsep strategi hedging. Saat ini, aktivitas pasar mata uang digital jauh lebih tinggi dari awal pembentukan pasar, dan banyak bursa kontrak muncul, yang menawarkan banyak peluang untuk hedging yang menguntungkan.

  • Prinsip Strategi

    Strategi ini ditulis dalam bahasa C++ dan agak sulit untuk dibaca. Namun tidak menghalangi pembaca untuk mempelajari esensi desain dan ide strategi ini. Strategi ini lebih ringkas, kode panjangnya sederhana, hanya lebih dari 500 baris. Dalam hal pengambilan data pasar, berbeda dengan strategi sebelumnya yang menggunakan antarmuka rest, strategi ini menggunakan antarmuka websocket untuk menerima push pasar. Dalam hal desain, struktur strategi yang masuk akal, ketebalan kode yang rendah, mudah untuk diperluas atau dioptimalkan; pemikiran logis yang jelas, desain seperti itu tidak hanya mudah digunakan dan diperluas; sebagai strategi pengajaran, desain strategi belajar juga merupakan contoh yang baik. Prinsip strategi yang relatif sederhana, yaitu kontrak jangka panjang dan kontrak jangka pendek melakukan perbaikan, counter-set hedging, pada dasarnya sesuai dengan jangka panjang hedging komoditas berjangka. Dia mengatakan, "Saya tidak tahu apa yang akan terjadi, tapi saya pikir saya harus bekerja keras untuk mendapatkan pekerjaan yang lebih baik". Jika Anda tidak memiliki kontrak jangka panjang, maka Anda tidak akan memiliki kontrak jangka pendek. Setelah mengetahui prinsip-prinsip dasar, yang tersisa adalah strategi bagaimana memicu hedging, bagaimana mengimbangi, bagaimana meningkatkan posisi, bagaimana mengendalikan posisi, bagaimana menangani detail strategi. Strategi hedging terutama berfokus pada pergerakan perbedaan harga yang ditandai, untuk melakukan perdagangan regresi pada perbedaan. Namun, perbedaan mungkin terjadi sedikit, atau besar, atau satu arah. Hal ini menimbulkan ketidakpastian keuntungan dan kerugian dari hedging, namun risikonya jauh lebih kecil dari tren unilateral. Banyak optimasi strategi jangka panjang yang dilakukan dari tingkat kontrol posisi, mulai dari pemicu posisi terbuka. Misalnya, strategi ini dapat dengan mudah dimodifikasi menjadi strategi hedging jangka panjang indikator Brin karena desain yang masuk akal dan konvergensi yang rendah.

  • Analisis kode strategi

    Jika kita melihat kode, kita dapat menyimpulkan bahwa kode dibagi menjadi empat bagian.

    • 1. Definisi nilai daftar, mendefinisikan beberapa nilai status yang digunakan untuk menandai status. Beberapa fungsi fungsi yang tidak terkait dengan gagasan kebijakan, seperti fungsi kode url, fungsi konversi waktu, dll.
    • 2.K线数据生成器类:策略由该生成器类对象生成的K线数据驱动。
    • 3.对冲类:该类的对象可以执行具体的交易逻辑,对冲操作、策略细节的处理机制等。
    • 4.策略主函数,也就是 mainFungsi tersebut.mainFungsi ini adalah fungsi input dari strategi, dan utama loop yang dijalankan dalam fungsi ini, selain itu fungsi ini juga melakukan operasi penting, yaitu mengakses antarmuka websocket dari bursa, mendapatkan data tick yang didorong, sebagai data mentah dari generator data K-line.

    Dengan memahami kode strategi secara keseluruhan, kita dapat mempelajari secara menyeluruh desain, ide, dan teknik strategi ini dengan menganalisis setiap elemen secara bertahap.

    • Definisi nilai daftar, fungsi fungsi lainnya

      1 Jenis DaftarStatePernyataan

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

      Karena ada beberapa fungsi dalam kode yang kembali ke suatu status, maka status-status tersebut didefinisikan dalam jenis daftar.StateDi tengah-tengah. Lihatlah muncul dalam kode.STATE_NADia mengatakan, "Saya tidak tahu apa yang terjadi".STATE_IDLEUntuk kondisi kosong, yaitu kondisi yang dapat dioperasikan sebagai hedging.STATE_HOLD_LONGStatus untuk memegang posisi lindung nilai yang sah.STATE_HOLD_SHORTStatus untuk memegang posisi hedgeback.

      2, penggantian string, tidak disebut dalam kebijakan ini, dianggap sebagai fungsi alat cadangan, yang terutama menangani string.

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

      Fungsi 3 adalah fungsi yang dikonversi menjadi 16 digittoHex

      inline unsigned char toHex(unsigned char x)
      

      4, fungsi yang menangani kode url

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

      5, fungsi konversi waktu, yang mengubah waktu dari format string menjadi time stamp.

      uint64_t _Time(string &s)
      
    • K-line data generator kelas

      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;
      };
      

      Kelas ini terutama bertanggung jawab untuk memproses data tick yang diperoleh menjadi garis K perbedaan untuk mendorong logika hedging strategi. Beberapa pembaca mungkin bertanya-tanya, mengapa menggunakan data tick? Mengapa membangun generator data K-line seperti ini? Apakah tidak baik dengan data K-line langsung? Pertanyaan seperti itu berulang kali muncul, dan juga muncul ketika saya menulis beberapa strategi hedging. Sementara data garis K dari perbedaan harga dua kontrak adalah statistik perubahan harga perbedaan dalam periode tertentu, sehingga tidak dapat dengan mudah mengambil data garis K masing-masing kontrak untuk melakukan operasi pengurangan, menghitung perbedaan data masing-masing pada setiap garis K Bar, sebagai perbedaan harga. Kesalahan yang paling jelas adalah, misalnya, harga tertinggi, harga terendah, dua kontrak, tidak selalu pada saat yang sama. Jadi nilai pengurangan tidak terlalu berarti. Oleh karena itu kita perlu menggunakan data tik secara real-time, menghitung perbedaan secara real-time, statistik real-time terhadap perubahan harga dalam siklus tertentu (yaitu kenaikan dan penurunan pada kolom K). Jadi kita membutuhkan generator data K secara terpisah sebagai kelas yang baik untuk pemrosesan pemisahan logis.

    • Kelas Hedging

      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("");                               // 图表对象,并初始化
      };
      
      
      

      Karena kode yang relatif panjang, bagian yang dihilangkan, terutama untuk menunjukkan struktur kelas hedge ini, tidak menyebutkan fungsi konstruksi Hedge, terutama inisialisasi objek; sisanya, ada 2 fungsi utama.

      • getState

        Fungsi ini terutama menangani pemeriksaan pesanan, pembatalan pesanan, pemeriksaan posisi, keseimbangan posisi, dan lain-lain. Karena dalam proses perdagangan hedging, tidak dapat dihindari situasi dengan kaki tunggal (yaitu kontrak yang dikontrak, kontrak yang tidak dikontrak), jika pemeriksaan dilakukan dalam logika suborder, lalu memproses order atau penyeimbangan, logika strategi akan lebih berantakan. Jadi, ketika merancang bagian ini, pemikiran lain diambil. Jika pemicu operasi hedge, kali berikutnya, apakah terjadi situasi dengan kaki tunggal atau tidak, hedge secara default berhasil, kemudian mendeteksi keseimbangan posisi dalam fungsi getState, secara independen mendeteksi logika yang memproses keseimbangan.

      • Lompatan

        Logika transaksi strategi terbungkus dalam fungsi ini, di mana panggilangetState, Menggunakan objek K-line data generator untuk menghasilkan data K-line dari perbedaan harga, untuk melakukan hedging open, placement, dan penambahan logika trading. Ada juga beberapa operasi untuk memperbarui data grafik.

    • Fungsi utama strategi

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

      Setelah memulai, kebijakan ini dilaksanakan dari fungsi main, dan dalam proses inisialisasi dari fungsi main, kebijakan tersebut berlangganan tick market dari websocket interface. Tugas utama dari fungsi main adalah untuk membangun sebuah loop utama, menerima tick market yang dikirimkan oleh websocket interface dari bursa yang terus menerus, dan kemudian memanggil anggota dari obyek jenis hedge: Loop function. Satu hal yang perlu diperhatikan adalah bahwa pasar tik yang disebutkan di atas sebenarnya adalah antarmuka data mendalam pesanan langganan, yang diperoleh dengan data order tipis untuk setiap file. Tetapi strategi hanya menggunakan data file pertama, yang sebenarnya hampir sama dengan data pasar tik, strategi tidak menggunakan data file lain, atau tidak menggunakan jumlah pesanan file pertama. Untuk detailnya, lihat Kebijakan tentang cara berlangganan data untuk antarmuka websocket dan cara menyetelnya.

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

      Pertama, Anda harus mengkodekan url pesan langganan yang dikirim ke antarmuka langganan dengan parameter json, yaitu:payloadNilai parameter. Kemudian langkah yang lebih penting adalah memanggil fungsi antarmuka API inventor untuk platform perdagangan kuantitatif.DialFungsi tersebut.DialFungsi ini dapat digunakan untuk mengakses websocket interface. Di sini kita melakukan beberapa pengaturan sehingga websocket yang akan dibuat untuk menghubungkan objek kontrol ws memiliki sambungan putus otomatis (message berlangganan masih menggunakan string nilai qs parameter payload).DialTambahkan opsi konfigurasi ke dalam string parameter fungsi.

      DialBagian awal dari parameter fungsi adalah sebagai berikut:

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

      Ini adalah alamat antarmuka websocket yang harus diakses dan kemudian digunakan untuk|Perbedaan.compress=gzip_raw&mode=recv&reconnect=true&payload="+qsSemua adalah parameter konfigurasi.

      Nama parameter Deskripsi
      mengompres compress adalah mode kompresi, yang digunakan oleh antarmuka websocket OKEX untuk gzip_raw.
      modus mode sebagai mode, opsional dual, send, recv Tiga jenis; dual sebagai dua arah, mengirim data kompresi, menerima data kompresi;; send untuk mengirim data kompresi;;recv untuk menerima data kompresi, dekompresi lokal;;
      Hubungkan kembali reconnect untuk apakah koneksi ulang diatur, reconnect=true untuk mengaktifkan koneksi ulang, tidak mengatur default tidak terhubung.
      muatan Payload adalah pesan berlangganan yang harus dikirim saat terhubung kembali ke ws.

      Dengan pengaturan ini, bahkan jika koneksi websocket terputus, inventor akan mengkualifikasi platform perdagangan. Sistem bawah host akan melakukan koneksi ulang secara otomatis, mendapatkan data pasar terbaru secara tepat waktu. "Semoga kita bisa mendapatkan keuntungan yang lebih besar dari investasi yang kita lakukan", kata dia.

  • Kontrol Posisi

    Pengendalian posisi dilakukan dengan menggunakan rasio posisi lindung yang mirip dengan baris bilangan "Boffinach".

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

    Anda dapat melihat bahwa setiap kali jumlah posisi yang ditingkatkan adalah jumlah dari dua posisi terakhir. Pengendalian posisi seperti ini dapat dilakukan dengan memaksimalkan jumlah hedging suku bunga yang relatif meningkat, dengan posisi yang didistribusikan, sehingga dapat menangkap posisi kecil yang berfluktuasi dengan perbedaan kecil, dan posisi besar yang berfluktuasi dengan perbedaan yang meningkat dengan tepat.

  • Perimbangan: Stop Loss Stop Loss

    Harga stop loss, harga stop loss, dan harga stop loss tetap. Pengepungan yang berbeda dilakukan pada posisi stop loss atau stop loss.

  • Masuk, keluar, desain siklus

    Parameter NPeriod Periode yang dikendalikan untuk melakukan kontrol dinamis terhadap strategi trading.

  • Diagram Strategi

    Strategi ini secara otomatis menghasilkan grafik garis K dari perbedaan harga, menandai informasi transaksi yang relevan.

    Kebijakan C++ untuk membuat gambar grafik kustom juga sangat sederhana, seperti yang dapat dilihat dalam fungsi konstruksi kelas hedge, kita menggunakan kata sandi_cfgStr yang ditulis dengan baik untuk mengkonfigurasi objek grafik_c, _c adalah anggota pribadi dari kelas hedge yang disebut inventor untuk mengkuantifikasi fungsi antarmuka API grafik kustom pada saat initializationChartObjek grafik yang dibuat oleh fungsi.

    _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();                         // 重置图表数据。
    
    • Panggilan_c.update(_cfgStr);Gunakan _cfgStr untuk mengkonfigurasi objek grafik.
    • Panggilan_c.reset();Mengubah data grafik.

    Ketika kode kebijakan perlu memasukkan data ke dalam grafik, juga dilakukan dengan memanggil langsung fungsi anggota dari objek _c, atau mengirim referensi dari objek _c sebagai parameter, dan kemudian memanggil fungsi anggota objek _c ([[metode]]). Misalnya:

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

    Setelah melakukan transaksi, catat catatan label pada grafik K-line.

    Di bawah ini, gambar K baris adalah anggota fungsi dari kelas BarFeeder dengan memanggilfeedPada saat ini, kita akan menggunakan referensi objek grafik _c sebagai parameter.

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

    Ini adalahfeedParameter fungsi 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)。
        } 
    }
    

    Dengan memanggil objek grafik _caddFungsi anggota untuk memasukkan data baru K-lineBar ke dalam grafik. Kode:c->add(chartIdx, point);

  • Pengamatan

    img

    img

    img

Kebijakan ini hanya digunakan untuk belajar komunikasi, jika digunakan pada perangkat nyata, silahkan mengubah dan mengoptimalkan sendiri sesuai dengan situasi nyata.

Alamat kebijakan:https://www.fmz.com/strategy/163447

Untuk lebih banyak tips menarik, lihat "Inventors Quantify Trading Platform":https://www.fmz.com


Berkaitan

Lebih banyak