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

"С++ версия стратегии хеджирования фьючерсных контрактов OKEX", которая проводит вас через жесткую количественную стратегию

Автор:Доброта, Создано: 2019-08-29 16:05:07, Обновлено: 2023-11-07 20:53:27

img

Говоря о стратегиях хеджирования, на разных рынках существуют различные типы, разнообразные комбинации и разнообразные идеи. Мы исследуем дизайнерские идеи и концепции стратегии хеджирования из самого классического межвременного хеджирования. Сегодня рынок криптовалют гораздо более активен, чем в начале, и также существует много бирж фьючерсных контрактов, которые предлагают множество возможностей для арбитражного хеджирования. Спотный арбитраж, арбитраж денежных средств, фьючерсный межвременный арбитраж, арбитраж фьючерсов на межвременном рынке и т. Д., Крипто количественные торговые стратегии возникают одна за другой. Давайте посмотрим на hardcore межвременную стратегию хеджирования, написанную на C++ и торгующую на бирже OKEX FM. Стратегия основана на количественной торговой платформе QuantZ.

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

Почему стратегия несколько хардкорная, потому что стратегия написана на C++ и чтение стратегии немного сложнее. Но это не мешает читателям узнать суть дизайна и идей этой стратегии. Логика стратегии относительно проста, длина кода умеренная, всего 500 строк. С точки зрения сбора рыночных данных, в отличие от других стратегий, которые используют интерфейс rest. Эта стратегия использует интерфейс websocket для принятия котировок на биржевом рынке.

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

  • Положительный спред, продажа коротких форвардных контрактов, покупка длинных недавних контрактов.
  • Негативный спред, покупка долгосрочных контрактов, продажа коротких недавних контрактов.

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

Стратегия хеджирования в основном касается колебаний разницы в цене объекта (спред) и ее регрессии.

Это приводит к неопределенности в отношении хеджирования прибыли и убытков, но риск все еще намного меньше, чем односторонняя тенденция. для различных оптимизаций межвременной стратегии мы можем выбрать начать с уровня контроля позиции и условия открытия и закрытия триггера. например, мы можем использовать классический Индикатор полосы Боллинджера для определения колебаний цены. Из-за разумной конструкции и низкой степени соединения эта стратегия может быть легко изменена в Индекс Боллинджера межвременная стратегия хеджирования

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

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

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

  2. Класс генератора данных K-линии: стратегия определяется данными K-линии, генерируемыми объектом класса генератора.

  3. Класс хеджирования: объекты этого класса могут выполнять специфическую логику торговли, операции хеджирования и обработку деталей стратегии.

  4. Основная функция стратегии, которая является функцией main. Основной функцией является функция входа в стратегию. Главная петля выполняется внутри этой функции. Кроме того, эта функция также выполняет важную операцию, то есть доступ к интерфейсу веб-сокета биржи и получение выдвинутых сырых рыночных данных как генератор данных K-линии.

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

  • Определение значения перечисления, другие функции
  1. перечисленный типStateЗаявление
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
};

Поскольку некоторые функции в коде возвращают состояние, эти состояния определяются в типе перечисленияState.

Увидев этоSTATE_NAпоявляется в коде ненормально, иSTATE_IDLEявляется неактивным, то есть состояние операции может быть хеджировано.STATE_HOLD_LONGявляется состоянием, в котором положительная позиция хеджирования удерживается.STATE_HOLD_SHORTявляется состоянием, в котором удерживается отрицательная позиция хеджирования.

  1. Замена строки, не вызванная в этой стратегии, является альтернативной полезной функцией, в основном занимающейся строками.
string replace(string s, const string from, const string& to)
  1. Функция для преобразования в гексадецимальные символыtoHex
inline unsigned char toHex(unsigned char x)
  1. Управление URL-кодированными функциями
std::string urlencode(const std::string& str)
  1. Функция преобразования времени, которая преобразует время в формате строки в метку времени.
uint64_t _Time(string &s)
  • Класс генератора данных линии 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;
};

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

У некоторых читателей могут возникнуть вопросы, зачем использовать данные тика? Зачем строить генератор данных K-линии таким образом? Разве не хорошо использовать данные K-линии напрямую? Этот тип вопроса был выпущен в три всплеска. Когда я написал некоторые стратегии хеджирования, я также устроил ажиотаж. Я нашел ответ, когда написал стратегию хеджирования Боллинджера. Поскольку K-линиевые данные для одного контракта - это статистика изменения цены для этого контракта за определенный период времени.

К-линейные данные разницы между двумя контрактами - это статистика изменения цены на разницу в определенном периоде. Поэтому невозможно просто взять K-линейные данные каждого из двух контрактов для вычитания и вычислить разницу каждого данного на каждом K-линейном столбце. Наиболее очевидной ошибкой является, например, самая высокая цена и самая низкая цена двух контрактов, не обязательно одновременно. Таким образом, вычитаемое значение не имеет особого смысла.

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

  • Класс хеджирования
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
};

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

GetState

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

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

Кружок

Торговая логика стратегии заключается в этой функции, в которойgetStateК-линейный объект генератора данных используется для генерации K-линейных данных разницы ((spread), и выполняется суждение об открытии, закрытии и добавлении логики позиции.

  • Основная функция стратегии
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
        
        ...
        
}

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

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

Взгляните внимательнее на то, как стратегия подписывается на данные интерфейса веб-сокета и как он настроен.

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

Во-первых, URL кодирование подписки сообщения json параметр, прошедший подписанный интерфейс, то есть значениеpayloadЗатем важным шагом является вызов функции интерфейса API платформы FMZ QuantDialФункция.DialЗдесь мы делаем некоторые настройки, пусть объект управления соединением веб-сокета, который будет создан, будет иметь автоматическое воссоединение или отключение (сообщение подписки все еще использует значениеqsстрокаpayloadпараметр), чтобы достичь этой функции, вы должны добавить конфигурацию в строке параметровDial function.

НачалоDialпараметр функции следующий:

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

это адрес интерфейса веб-сокета, к которому необходимо получить доступ, и разделен you.

compress=gzip_raw&mode=recv&reconnect=true&payload="+qsВсе параметры конфигурации.

Наименование параметра описание
компресс компресс - это режим сжатия, интерфейс OKEX websocket использует gzip_raw таким образом, поэтому он установлен на gzip_raw
режим Режим - режим, опциональный двойной, отправляет и восстанавливает три вида. Двойной является двунаправленным, отправляя сжатые данные и получая сжатые данные. Отправлять - отправлять сжатые данные. Recv получает сжатые данные и декомпрессирует их локально.
перезагрузить Reconnect настроена на reconnect, reconnect=true для включения повторного подключения, по умолчанию не восстанавливается.
полезная нагрузка Полезная нагрузка - это подписка на сообщение, которое нужно отправить, когда ws будет восстановлено.

После этой настройки, даже если соединение websocket отключено, базовая система докерной системы торговой платформы FMZ Quant автоматически воссоединится и вовремя получит последние данные о рынке.

Захватывайте любую колебание цен и быстро захватывайте правильный хедж.

  • Управление положением

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

for (int i = 0; i < AddMax + 1; i++) { // construct a data structure that controls the number of scalping, similar to the ratio of the Bofinac sequence to the number of hedges.
     if (_addArr.size() < 2) { // The first two added positions are changed as: double the number of hedges
         _addArr.push_back((i+1)*OpenAmount);
     }
     _addArr.push_back(_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); // The last two adding positions are added together, and the current position quantity is calculated and stored in the "_addArr" data structure.
}

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

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

  • Закрытие позиции: стоп-лосс и прибыль

Фиксированный спред стоп-лосса и спред прибыли.

Когда разница позиции достигает позиции take profit и стоп-лосс, то выполняются позиции take profit и stop loss.

  • Проектирование входа на рынок и выхода с рынка

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

  • График стратегии

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

C ++ стратегия пользовательская схема рисования операция также очень проста. Вы можете видеть, что в конструктор хедж класса, мы используем записанный диаграмм конфигурации строки_cfgStrчтобы настроить объект диаграмма_c, _cявляется частным компонентом класса хеджирования.chartобъект, созданный платформой FMZ Quant, называется пользовательской графической API-интерфейсной функцией.

_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);                 // Update chart objects with chart configuration
_c.reset();                         // Reset chart data。
call _c.update(_cfgStr); Use _cfgStr to configure to the chart object.

call _c.reset(); to reset the chart data.

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

Например:

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

После размещения заказа, отметьте линейную диаграмму K.

Следующим образом, при рисовании K линии, ссылка на объект диаграмма_cпередается в качестве параметра при вызове функции членовfeedВ соответствии сBarFeeder class.

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

То есть формальный параметрcВ соответствии сfeed function.

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); // only keep 1000 bar data
     } else {
         c->add(chartIdx, point, -1); // Otherwise update (not new bar), this point (update this bar).
     }
}

Вставьте новые данные K-линии в график, вызвавaddфункция члена объекта диаграммы_c.

c->add(chartIdx, point);

Обратный тест

img img img

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

Адрес стратегии:https://www.fmz.com/strategy/163447

Более интересные стратегии представлены на платформе FMZ Quant":https://www.fmz.com


Связанные

Больше