"C++ phiên bản của OKEX hợp đồng tương lai chiến lược phòng ngừa rủi ro" mà đưa bạn thông qua chiến lược định lượng cứng

Tác giả:Tốt, Tạo: 2019-08-29 16:05:07, Cập nhật: 2025-01-15 22:07:49

“C++ version of OKEX futures contract hedging strategy” that takes you through hardcore quantitative strategy

Nói về các chiến lược phòng ngừa rủi ro, có nhiều loại, sự kết hợp đa dạng và các ý tưởng đa dạng trên các thị trường khác nhau. Chúng tôi khám phá các ý tưởng thiết kế và khái niệm của chiến lược phòng ngừa rủi ro từ việc phòng ngừa rủi ro giữa thời gian cổ điển nhất. Ngày nay, thị trường tiền điện tử hoạt động tích cực hơn nhiều so với lúc đầu, và cũng có nhiều sàn giao dịch hợp đồng tương lai cung cấp nhiều cơ hội để phòng ngừa rủi ro.

Nguyên tắc chiến lược

Tại sao chiến lược lại hơi cứng bởi vì chiến lược được viết bằng C++ và việc đọc chiến lược khó hơn một chút. Nhưng nó không ngăn cản người đọc học được bản chất của thiết kế và ý tưởng chiến lược này. Logic chiến lược tương đối đơn giản, độ dài mã là vừa phải, chỉ 500 dòng. Về việc thu thập dữ liệu thị trường, không giống như các chiến lược khác sử dụng giao diện rest. Chiến lược này sử dụng giao diện websocket để chấp nhận báo giá thị trường sàn giao dịch.

Về mặt thiết kế, cấu trúc chiến lược là hợp lý, mức độ kết nối mã rất thấp, và nó thuận tiện để mở rộng hoặc tối ưu hóa. Logic là rõ ràng, và một thiết kế như vậy không chỉ dễ hiểu. Là một tài liệu giảng dạy, học thiết kế của chiến lược này cũng là một ví dụ tốt. Nguyên tắc của chiến lược này tương đối đơn giản, đó là, sự lan rộng của hợp đồng tương lai và hợp đồng gần đây là tích cực hay tiêu cực? nguyên tắc cơ bản phù hợp với việc bảo hiểm giữa thời gian của hợp đồng tương lai hàng hóa.

  • Spread Positive, bán hợp đồng ngắn hạn, mua hợp đồng dài hạn.
  • Spread âm, mua hợp đồng dài, bán hợp đồng ngắn.

sau khi hiểu các nguyên tắc cơ bản, phần còn lại là cách chiến lược kích hoạt vị trí mở của phòng ngừa rủi ro, cách đóng vị trí, cách thêm các vị trí, phương pháp kiểm soát vị trí tổng và xử lý chi tiết chiến lược khác.

Chiến lược phòng ngừa chủ yếu liên quan đến sự biến động của chênh lệch giá đối tượng (The Spread) và sự hồi quy của nó. Tuy nhiên, sự khác biệt có thể biến động nhẹ, hoặc dao động mạnh hoặc theo một hướng.

Điều này mang lại sự không chắc chắn về bảo hiểm lợi nhuận và lỗ, nhưng rủi ro vẫn nhỏ hơn nhiều so với xu hướng đơn phương. cho các tối ưu hóa khác nhau của chiến lược liên thời gian, chúng ta có thể chọn bắt đầu từ mức kiểm soát vị trí và điều kiện kích hoạt mở và đóng. ví dụ, chúng ta có thể sử dụng chỉ số băng Bollinger cổ điển để xác định biến động giá. Do thiết kế hợp lý và mức độ kết nối thấp, chiến lược này có thể dễ dàng được sửa đổi thành chiến lược bảo hiểm liên thời gian chỉ số Bollinger

Phân tích mã chiến lược

Nhìn vào toàn bộ mã, bạn có thể kết luận rằng mã được chia thành bốn phần.

  1. Đánh giá các định nghĩa giá trị, xác định một số giá trị trạng thái và sử dụng để đánh dấu trạng thái. Một số hàm chức năng không liên quan đến chiến lược, chẳng hạn như các hàm mã hóa url, các hàm chuyển đổi thời gian, v.v., không có mối quan hệ với logic chiến lược, chỉ để xử lý dữ liệu.

  2. Lớp máy phát dữ liệu đường K: Chiến lược được điều khiển bởi dữ liệu đường K được tạo ra bởi đối tượng lớp máy phát.

  3. Lớp phòng ngừa rủi ro: Các đối tượng thuộc lớp này có thể thực hiện logic giao dịch cụ thể, các hoạt động phòng ngừa rủi ro và xử lý chi tiết về chiến lược.

  4. Chức năng chính của chiến lược, đó là chức năng main. Chức năng chính là chức năng đầu vào của chiến lược. Vòng lặp chính được thực hiện bên trong chức năng này. Ngoài ra, chức năng này cũng thực hiện một hoạt động quan trọng, tức là truy cập giao diện websocket của sàn giao dịch, và lấy dữ liệu thị trường tick thô được đẩy như bộ tạo dữ liệu đường K.

Thông qua sự hiểu biết tổng thể về mã chiến lược, chúng ta có thể dần dần tìm hiểu các khía cạnh khác nhau của chiến lược, và sau đó nghiên cứu thiết kế, ý tưởng và kỹ năng của chiến lược.

  • Định nghĩa giá trị liệt kê, các chức năng chức năng khác
  1. loại được liệt kêStatetuyên bố
enum State {                    // Enum type defines some states
    STATE_NA,                   // Abnormal state
    STATE_IDLE,                 // idle
    STATE_HOLD_LONG,            // holding long positions
    STATE_HOLD_SHORT,           // holding short positions
};

Bởi vì một số hàm trong mã trả về một trạng thái, các trạng thái này được định nghĩa trong loại enumerationState.

Nhìn thấy thếSTATE_NAxuất hiện trong mã là bất thường, vàSTATE_IDLElà không hoạt động, nghĩa là trạng thái của hoạt động có thể được bảo hiểm.STATE_HOLD_LONGlà trạng thái trong đó vị trí phòng ngừa rủi ro tích cực được giữ.STATE_HOLD_SHORTlà trạng thái trong đó vị trí phòng ngừa rủi ro âm được giữ.

  1. thay thế chuỗi, không được gọi trong chiến lược này, là một chức năng tiện ích thay thế, chủ yếu xử lý chuỗi.
string replace(string s, const string from, const string& to)
  1. Một chức năng để chuyển đổi thành ký tự hexadecimaltoHex
inline unsigned char toHex(unsigned char x)
  1. Quản lý các chức năng mã hóa url
std::string urlencode(const std::string& str)
  1. Một chức năng chuyển đổi thời gian chuyển đổi thời gian trong định dạng chuỗi thành dấu thời gian.
uint64_t _Time(string &s)
  • Lớp máy phát dữ liệu đường K
class BarFeeder { // K line data generator class
    public:
        BarFeeder(int period) : _period(period) { // constructor with argument "period" period, initialized in initialization list
            _rs.Valid = true; // Initialize the "Valid" property of the K-line data in the constructor body.
        }

        void feed(double price, chart *c=nullptr, int chartIdx=0) { // input data, "nullptr" null pointer type, "chartIdx" index default parameter is 0
            uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000; // The second-level timestamp removes the incomplete time period (incomplete _period seconds) and is converted to a millisecond timestamp.
            bool newBar = false; // mark the tag variable of the new K line Bar
            if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) { // if the K line data is 0 in length. Or the last bar's timestamp is less than epoch (the last bar of the K line is more than the current most recent cycle timestamp)
                record r; // declare a K line bar structure
                r.Time = epoch; // construct the K line bar of the current cycle
                r.Open = r.High = r.Low = r.close = price; // Initialize the property
                _rs.push_back(r); // K line bar is pressed into the K line data structure
                if (_rs.size() > 2000) { // if the K-line data structure length exceeds 2000, the oldest data is removed.
                    _rs.erase(_rs.begin());
                }
                newBar = true; // tag
            } else { // In other cases, it is not the case of a new bar.
                record &r = _rs[_rs.size() - 1]; // Reference the data of the last bar in the data.
                r.High = max(r.High, price); // The highest price update operation for the referenced data.
                r.Low = min(r.Low, price); // The lowest price update operation for the referenced data.
                r.close = price; // Update the closing price of the referenced data.
            }
    
            auto bar = _rs[_rs.size()-1]; // Take the last column data and assign it to the bar variable
            json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // construct a json type data
            if (c != nullptr) { // The chart object pointer is not equal to the null pointer, do the following.
               if (newBar) { // judge if the new Bar appears
                    c->add(chartIdx, point); // call the chart object member function add to insert data into the chart object (new k line bar)
                    c->reset(1000); // retain only 1000 bar of data
                } else {
                    c->add(chartIdx, point, -1); // Otherwise update (not new bar), this point (update this bar).
                }
            }
        }
        records & get() { // member function, method for getting K line data.
            Return _rs; // Returns the object's private variable _rs . (ie generated K-line data)
        }
    private:
        int _period;
        records _rs;
};

Lớp này chủ yếu chịu trách nhiệm xử lý dữ liệu tick được thu được thành một đường K khác biệt để điều khiển logic phòng ngừa rủi ro chiến lược.

Một số độc giả có thể có câu hỏi, tại sao sử dụng dữ liệu tick? Tại sao xây dựng một máy tạo dữ liệu K-line như thế này? Không phải là tốt để sử dụng dữ liệu K-line trực tiếp? Những câu hỏi như vậy đã được đưa ra trong ba đợt. Khi tôi viết một số chiến lược phòng hộ, tôi cũng đã gây ra một tiếng ồn. Tôi đã tìm thấy câu trả lời khi tôi viết chiến lược phòng hộ Bollinger.

Dữ liệu đường K của sự khác biệt giữa hai hợp đồng là số liệu thống kê thay đổi giá chênh lệch trong một khoảng thời gian nhất định. Do đó, không thể đơn giản lấy dữ liệu đường K của mỗi hai hợp đồng để trừ và tính toán sự khác biệt của mỗi dữ liệu trên mỗi thanh đường K. Sai lầm rõ ràng nhất là, ví dụ, giá cao nhất và giá thấp nhất của hai hợp đồng, không nhất thiết cùng một lúc. Vì vậy, giá trị trừ không có ý nghĩa nhiều.

Do đó, chúng ta cần sử dụng dữ liệu tick thời gian thực để tính toán sự khác biệt trong thời gian thực và tính toán sự thay đổi giá trong một khoảng thời gian nhất định trong thời gian thực (tức là giá cao nhất, thấp nhất, mở và đóng trên cột đường K).

  • Lớp phòng ngừa rủi ro
class Hedge { // Hedging class, the main logic of the strategy.
  public:
    Hedge() { // constructor
        ...
    };
    
    State getState(string &symbolA, depth &depthA, string &symbolB, depth &depthB) { // Get state, parameters: contract A name, contract A depth data, contract B name, contract B depth data
        
        ...
    }
    bool Loop(string &symbolA, depth &depthA, string &symbolB, depth &depthB, string extra="") { // Opening and closing position main logic
        
        ...
    }

  private:
    vector<double> _addArr; // Hedging adding position list
    string _state_desc[4] = {"NA", "IDLE", "LONG", "SHORT"}; // Status value Description
    int _countOpen = 0; // number of opening positions
    int _countcover = 0; // number of closing positions
    int _lastcache = 0; //
    int _hedgecount = 0; // number of hedging
    int _loopcount = 0; // loop count (cycle count)
    double _holdPrice = 0; // holding position price
    BarFeeder _feederA = BarFeeder(DPeriod); // A contract Quote K line generator
    BarFeeder _feederB = BarFeeder(DPeriod); // B contract Quote K line generator
    State _st = STATE_NA; // Hedging type Object Hedging position status
    string _cfgStr; // chart configuration string
    double _holdAmount = 0; // holding position amount
    bool _iscover = false; // the tag of whether to close the position
    bool _needcheckOrder = true; // Set whether to check the order
    chart _c = chart(""); // chart object and initialize
};

Bởi vì mã tương đối dài, một số phần bị bỏ qua, đây chủ yếu là cho thấy cấu trúc của lớp hedge này, hàm Hedge được bỏ qua, chủ yếu cho mục đích khởi tạo đối tượng.

getState

Chức năng này chủ yếu liên quan đến việc kiểm tra lệnh, hủy lệnh, phát hiện vị trí, cân bằng vị trí v.v. Bởi vì trong quá trình giao dịch phòng ngừa rủi ro, không thể tránh một chân duy nhất (tức là một hợp đồng được thực hiện, một hợp đồng khác không), nếu kiểm tra được thực hiện trong logic đặt lệnh, và sau đó xử lý hoạt động gửi lại lệnh hoặc hoạt động đóng vị trí, logic chiến lược sẽ hỗn loạn.

Vì vậy, khi thiết kế phần này, tôi đã có một ý tưởng khác. nếu các hoạt động phòng hộ được kích hoạt, miễn là lệnh được đặt một lần, bất kể là có một phòng hộ một chân, mặc định là phòng hộ là thành công, và sau đó số dư vị trí được phát hiện tronggetStatechức năng, và logic để xử lý số dư sẽ được xử lý độc lập.

Chuỗi

Logic giao dịch của chiến lược được tóm tắt trong chức năng này, trong đógetStateđược gọi, và đối tượng máy tạo dữ liệu đường K được sử dụng để tạo ra dữ liệu đường K của sự khác biệt (the spread), và phán quyết mở, đóng và thêm vị trí logic được thực hiện.

  • Chức năng chính của chiến lược
void main() {

    ...
    
    string realSymbolA = exchange.SetcontractType(symbolA)["instrument"]; // Get the A contract (this_week / next_week / quarter ), the real contract ID corresponding to the week, next week, and quarter of the OKEX futures contract.
    string realSymbolB = exchange.SetcontractType(symbolB)["instrument"]; // ...
    
    string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump()) ; // jSON encoding, url encoding for the parameters to be passed on the ws interface
    Log("try connect to websocket"); // Print the information of the connection WS interface.
    auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs); // call the FMZ API "Dial" function to acess the WS interface of OKEX Futures
    Log("connect to websocket sucess");
    
    depth depthA, depthB; // Declare two variables of the depth data structure to store the depth data of the A contract and the B contract
    auto filldepth = [](json &data, depth &d) { // construct the code for the depth data with the json data returned by the interface.
        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; // time string A
    string timeB; // time string B
    while (true) {
        auto buf = ws.read(); // Read the data pushed by the WS interface
        
        ...
        
}

Sau khi chiến lược được bắt đầu, nó được thực hiện từ chức năng chính. Trong quá trình khởi tạo chức năng chính, chiến lược đăng ký thị trường tick của giao diện websocket. Công việc chính của chức năng chính là xây dựng một vòng lặp chính liên tục nhận được các báo giá tick được đẩy bởi giao diện websocket của sàn giao dịch, và sau đó gọi hàm thành viên của đối tượng lớp phòng ngừa: Loop function.

Một điểm cần lưu ý là thị trường tick được đề cập ở trên thực sự là giao diện dữ liệu chiều sâu mỏng của đơn đặt hàng, đó là dữ liệu sổ đặt hàng cho mỗi tệp. Tuy nhiên, chiến lược chỉ sử dụng tệp dữ liệu đầu tiên, trên thực tế, nó gần giống như dữ liệu thị trường tick. Chiến lược không sử dụng dữ liệu của các tệp khác, cũng không sử dụng giá trị đơn đặt hàng của tệp đầu tiên.

Hãy xem xét kỹ hơn về cách chiến lược đăng ký dữ liệu của giao diện websocket và cách nó được thiết lập.

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

Đầu tiên, mã hóa url của thông báo đăng ký tham số json được truyền qua giao diện đăng ký, đó là giá trị củapayloadSau đó, một bước quan trọng là để gọi FMZ Quant nền tảng s API chức năng giao diệnDialchức năng.Dialchức năng có thể được sử dụng để truy cập vào giao diện websocket của trao đổi. Ở đây chúng tôi thực hiện một số cài đặt, để các đối tượng điều khiển kết nối websocket ws được tạo có tự động kết nối lại của ngắt kết nối (thông điệp đăng ký vẫn sử dụng giá trịqschuỗi củapayloadtham số), để đạt được chức năng này, bạn cần phải thêm cấu hình trong chuỗi tham số củaDial function.

Sự khởi đầu củaDialtham số chức năng là như sau:

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

đây là địa chỉ của giao diện websocket cần được truy cập, và được tách bằng


|Parameter name|description|
|-|-|
|compress|compress is compression mode, OKEX websocket interface uses gzip_raw this way, so it is set to gzip_raw|
|mode|Mode is mode, optional dual, send and recv three kind. Dual is bidirectional, sending compressed data and receiving compressed data. Send is to send compressed data. Recv receives the compressed data and decompresses it locally.|
|reconnect|Reconnect is set to reconnect, reconnect=true to enable reconnection, no default is not reconnected.|
|payload|The payload is a subscription message that needs to be sent when ws is reconnected.|

After this setting, even if the websocket connection is disconnected, FMZ Quant trading platform's underlying system of the docker system will automatically reconnect and get the latest market data in time.

Grab every price fluctuation and quickly capture the right hedge.

- Position control

Position control is controlled using a ratio of hedge positions similar to the “Fibonaci” series.

cho (int i = 0; i < AddMax + 1; i++) { // xây dựng một cấu trúc dữ liệu kiểm soát số lượng đầu óc, tương tự như tỷ lệ của chuỗi Bofinac với số lượng phòng hộ. if (_addArr.size() < 2) { // Hai vị trí đầu tiên được thêm vào được thay đổi như sau: gấp đôi số hedges _addArr.push_back (((i+1)*OpenAmount); } _addArr.push_back ((_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); // Hai vị trí cộng cuối cùng được cộng lại với nhau, và số lượng vị trí hiện tại được tính toán và lưu trữ trong cấu trúc dữ liệu _addArr. }


It can be seen that the number of additional positions added each time is the sum of the last two positions.

Such position control can realize the larger the difference, the relative increase of the arbitrage hedge, and the dispersion of the position, so as to grasp the small position of the small price fluctuation, and the large price fluctuation position is appropriately increased.

- closing position: stop loss and take profit

Fixed stop loss spread and take profit spread.

When the position difference reaches the take profit position and the stop loss position, the take profit and stop loss are carried out.

- The designing of entering the market and leaving the market

The period of the parameter ```NPeriod``` control provides some dynamic control over the opening and closing position of the strategy.

- Strategy chart

The strategy automatically generates a spread K-line chart to mark relevant transaction information.

c++ strategy custom chart drawing operation is also very simple. You can see that in the constructor of the hedge class, we use the written chart configuration string ```_cfgStr``` to configure the chart object ```_c```, ```_c``` is the private component of the hedge class. When the private member is initialized, the ```chart``` object constructed by the FMZ Quant platform custom chart API interface function is called.

_cfgStr = REOF( [{ mở rộng: { bố cục: đơn , col : 6, chiều cao : 500px }, rangeSelector: {enabled: false}, tooltip: {xDateformat: %Y-%m-%d %H:%M:%S, %A}, plotOptions: {candlestick: {color: #d75442, upcolor: #6ba583}}, biểu đồ:{type:line}, title:{text:Spread Long}, xAxis:{title:{text:date}}, series:[ {type:candlestick, name:Long Spread, data:[], id:dataseriesA}, {type:flags,data:[], onSeries: dataseriesA} ] ♪ mở rộng: { bố cục: đơn , col : 6, chiều cao : 500px }, rangeSelector: {enabled: false}, tooltip: {xDateformat: %Y-%m-%d %H:%M:%S, %A}, plotOptions: {candlestick: {color: #d75442, upcolor: #6ba583}}, biểu đồ:{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ập nhật các đối tượng biểu đồ với cấu hình biểu đồ _c.reset(); // Reset dữ liệu biểu đồ.


gọi _c.update(_cfgStr); Sử dụng _cfgStr để cấu hình đối tượng biểu đồ.

gọi _c.reset(); để đặt lại dữ liệu biểu đồ.


When the strategy code needs to insert data into the chart, it also calls the member function of the ```_c``` object directly, or passes the reference of ```_c``` as a parameter, and then calls the object member function (method) of ```_c``` to update the chart data and insert operation.

E.g:

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


After placing the order, mark the K line chart.

As follows, when drawing a K line, a reference to the chart object ```_c``` is passed as a parameter when calling the member function ```feed``` of the ```BarFeeder``` class.

void feed ((giá gấp đôi, biểu đồ *c=nullptr, int biểu đồIdx=0)


That is, the formal parameter ```c``` of the ```feed``` function.

điểm json = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // xây dựng một kiểu dữ liệu json if (c!= nullptr) { // Chỉ mục đối tượng biểu đồ không bằng chỉ mục không, làm như sau. if (newBar) { // đánh giá nếu thanh mới xuất hiện c->add(chartIdx, điểm); // gọi hàm thành viên đối tượng biểu đồ add để chèn dữ liệu vào đối tượng biểu đồ (bảng dòng k mới) c->reset(1000); // chỉ giữ dữ liệu 1000 bar { other { c->add(chartIdx, điểm, -1); // Nếu không cập nhật (không phải thanh mới), điểm này (đổi mới thanh này). } }


Insert a new K-line Bar data into the chart by calling the ```add``` member function of the chart object ```_c```.

c->add ((chartIdx, điểm); `

Kiểm tra hậu quả

“C++ version of OKEX futures contract hedging strategy” that takes you through hardcore quantitative strategy “C++ version of OKEX futures contract hedging strategy” that takes you through hardcore quantitative strategy “C++ version of OKEX futures contract hedging strategy” that takes you through hardcore quantitative strategy

Chiến lược này chỉ dành cho mục đích học tập và truyền thông.

Địa chỉ chiến lược:https://www.fmz.com/strategy/163447

Các chiến lược thú vị hơn có trong nền tảng FMZ Quant:https://www.fmz.com


Nội dung liên quan

Nhiều hơn nữa