Говоря о стратегии хеджирования, на разных рынках существует множество различных стратегий, множество разнообразных комбинаций, множество разнообразных идей. Мы начали с классического хеджирования с идей и идей. Сегодня активность рынка цифровых валют намного выше, чем в начале его существования, и появилось множество контрактных бирж, предлагающих множество возможностей для льготного хеджирования.
Причина, по которой тактика выглядит довольно жесткой, заключается в том, что она написана на языке C++, и ее чтение немного сложно. Однако это не мешает читателю изучить суть стратегии. С точки зрения дизайна, стратегия имеет разумную структуру, низкую степень сплоченности кода, легко расширяется или оптимизируется; логическая мысль ясна, такая конструкция не только удобна для использования и расширения; в качестве учебной стратегии, стратегический дизайн также является хорошим примером. Принцип стратегии прост, т. е. долгосрочные контракты и краткосрочные контракты являются корректировкой, контрактным хеджированием, в принципе, соответствующим долгосрочному хеджированию товарных фьючерсов. В этом случае вы должны быть готовы к тому, что вы получите более высокую зарплату, чем в прошлом. В то же время, многие из них не хотят, чтобы их отношения с другими странами стали более напряженными. После того, как основные принципы прояснены, остается решать стратегию, как запустить хеджирование, как выравнить позиции, как увеличить позиции. Хеджирующая стратегия в основном сосредоточена на колебаниях цены на товар, и торгует обратной ценой. Однако возможны небольшие колебания, или значительные колебания, или односторонние колебания. Это приводит к неопределенности прибыли и убытка хеджирования, но риски все же намного меньше, чем односторонние тенденции. Для различных оптимизаций долгосрочной стратегии многие варианты выбираются с уровня контроля позиций, с триггера открытия позиций. Например, классический индикатор Бринн используется в качестве дифференцированного вариации цены.
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)
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);
Эта политика используется только для изучения обмена, и при использовании реального диска, пожалуйста, сделайте собственные изменения и оптимизацию в соответствии с реальными условиями диска.
Политический адрес:https://www.fmz.com/strategy/163447
Более интересные стратегии можно найти в статье "Изобретатели количественных торговых площадок":https://www.fmz.com