
Dalam hal strategi lindung nilai, ada berbagai strategi, berbagai kombinasi, dan berbagai ide di berbagai pasar. Kita akan mulai dengan lindung nilai lintas periode yang paling klasik untuk mengeksplorasi ide desain dan konsep strategi lindung nilai. Saat ini, pasar mata uang digital jauh lebih aktif dibandingkan saat pasar pertama kali terbentuk, dan banyak bursa kontrak telah muncul, menyediakan sejumlah besar peluang arbitrase dan lindung nilai. Ada banyak sekali strategi, seperti arbitrase lintas pasar spot, arbitrase lindung nilai spot-futures, arbitrase lintas periode futures, arbitrase lintas pasar futures, dan sebagainya. Selanjutnya, mari kita lihat strategi lindung nilai lintas periode “hardcore” yang ditulis dalam bahasa C++, dengan pasar perdagangannya adalah bursa kontrak OKEX. Strategi ini ditulis berdasarkan “Inventor Quantitative Trading Platform”.
Alasan mengapa strategi ini agak sulit adalah karena strategi tersebut ditulis dalam bahasa C++, yang membuatnya sedikit lebih sulit untuk dibaca. Meskipun demikian, hal itu tidak menghalangi pembaca untuk mempelajari hakikat desain dan gagasan strategi ini. Strateginya relatif ringkas, dan panjang kodenya sedang, hanya lebih dari 500 baris. Dalam hal perolehan data pasar, tidak seperti strategi sebelumnya yang menggunakan antarmuka rest, strategi ini menggunakan antarmuka websocket untuk menerima dorongan data pasar dari bursa. Dari segi desain, struktur strateginya masuk akal, penggabungan kodenya sangat rendah, dan sangat mudah untuk diperluas atau dioptimalkan. Pemikiran logisnya jelas, dan desain ini tidak hanya mudah digunakan dan diperluas. Sebagai strategi pengajaran, desain strategi pembelajaran juga merupakan contoh yang baik. Prinsip strategi ini relatif sederhana, yaitu menggunakan kontrak berjangka dan kontrak jangka pendek untuk lindung nilai positif dan negatif, yang pada dasarnya sama dengan lindung nilai lintas periode pada komoditas berjangka. Arbitrase positif, kontrak berjangka pendek dan kontrak jangka pendek yang panjang. Lindung nilai, mengambil posisi panjang pada kontrak berjangka dan posisi pendek pada kontrak jangka pendek. Sekarang prinsip dasarnya sudah jelas, yang tersisa adalah bagaimana strategi memicu posisi lindung nilai, cara menutup posisi, dan cara meningkatkan posisi. Metode kontrol posisi dan pemrosesan detail strategi. Strategi lindung nilai terutama berfokus pada fluktuasi perbedaan harga aset dasar dan melakukan perdagangan regresi atas perbedaan harga tersebut. Namun, penyebarannya mungkin berfluktuasi sedikit, atau tajam, atau dalam satu arah. Hal ini menimbulkan ketidakpastian dalam lindung nilai keuntungan dan kerugian, tetapi risikonya masih jauh lebih kecil daripada tren unilateral. Banyak dari berbagai pengoptimalan strategi lintas periode memilih untuk memulai dari tingkat kontrol posisi, dimulai dari pemicu pembukaan dan penutupan. Misalnya, indikator Bollinger klasik digunakan sebagai titik pembukaan dan penutupan arbitrase positif dan negatif ketika perbedaan harga berfluktuasi. Karena desainnya yang masuk akal dan tingkat penggabungan yang rendah, strategi ini juga dapat dengan mudah dimodifikasi menjadi strategi lindung nilai lintas periode indikator Bollinger.
#### Melihat kode secara umum, kita dapat menyimpulkan bahwa kode tersebut terutama dibagi menjadi empat bagian.
main fungsi.main Fungsi ini adalah fungsi entri strategi. Loop utama dijalankan dalam fungsi ini. Selain itu, fungsi ini juga melakukan operasi penting, yaitu mengakses antarmuka websocket bursa untuk mendapatkan data pasar tick yang didorong sebagai data mentah. bahan generator data garis K. data.#### Dengan memahami kode strategi secara keseluruhan, kita sekarang dapat menganalisis setiap tautan secara bertahap untuk mempelajari sepenuhnya desain, ide, dan teknik strategi.
State penyataanenum State { // 枚举类型 定义一些 状态
STATE_NA, // 非正常状态
STATE_IDLE, // 空闲
STATE_HOLD_LONG, // 持多仓
STATE_HOLD_SHORT, // 持空仓
};
Karena beberapa fungsi dalam kode mengembalikan status tertentu, status ini didefinisikan dalam tipe enumerasiStatetengah.
Lihat kodenya munculSTATE_NA Maksudnya, itu adalah keadaan yang tidak normal.STATE_IDLE Berada dalam kondisi idle, yakni kondisi di mana operasi lindung nilai dapat dilakukan.STATE_HOLD_LONG Keadaan memegang posisi lindung nilai positif.STATE_HOLD_SHORT Keadaan memegang posisi lindung nilai.
string replace(string s, const string from, const string& to)
toHexinline unsigned char toHex(unsigned char x)
std::string urlencode(const std::string& str)
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;
};
Kelas ini terutama bertanggung jawab untuk memproses data tick yang diperoleh menjadi perbedaan harga K-line untuk menggerakkan logika strategi lindung nilai. Beberapa pembaca mungkin bertanya-tanya mengapa kita perlu menggunakan data centang? Mengapa kita perlu membangun generator data K-line seperti itu? Bukankah lebih baik menggunakan data K-line secara langsung? Ketiga pertanyaan ini muncul dalam pikiran saya ketika saya menulis beberapa strategi lindung nilai. Saya menemukan jawabannya ketika saya menulis strategi lindung nilai Bollinger spread. Karena data K-line dari suatu kontrak tunggal merupakan statistik perubahan harga kontrak tersebut dalam kurun waktu tertentu. Data garis K dari selisih harga antara dua kontrak merupakan statistik selisih harga dalam kurun waktu tertentu. Oleh karena itu, kita tidak bisa begitu saja mengurangkan data garis K dari kedua kontrak tersebut untuk menghitung selisih setiap data pada setiap garis K. garis Bar. Nilai, sebagai perbedaan harga. Kesalahan yang paling nyata adalah bahwa harga tertinggi dan harga terendah dari dua kontrak belum tentu terjadi pada waktu yang sama. Oleh karena itu, nilai yang dikurangi tidak masuk akal. Oleh karena itu, kita perlu menggunakan data tick waktu nyata, menghitung perbedaan harga secara real time, dan membuat statistik tentang perubahan harga dalam periode tertentu secara real time (yaitu pembukaan tertinggi dan penutupan terendah pada kolom garis K). Dengan cara ini, kita memerlukan generator data K-line sebagai kelas terpisah untuk memisahkan logika pemrosesan.
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 kodenya cukup panjang, sebagian dihilangkan. Kode ini terutama menunjukkan struktur kelas hedge. Konstruktor Hedge tidak disebutkan, karena utamanya untuk inisialisasi objek. Ada dua fungsi utama yang tersisa.
Fungsi ini terutama menangani deteksi order, pembatalan order, deteksi posisi, penyeimbangan posisi, dll. Karena dalam proses transaksi hedging, situasi single leg (yaitu satu kontrak diperdagangkan dan yang lainnya tidak) tidak dapat dihindari. Jika terdeteksi dalam order logic, maka order susulan atau penutupan posisi diproses. , logika strategi akan kacau. Jadi kami mengambil pendekatan lain saat mendesain bagian ini. Jika operasi lindung nilai dipicu, perintah ditempatkan sekali. Terlepas dari apakah situasi lindung nilai satu kaki terjadi, lindung nilai dianggap berhasil secara default. Kemudian keseimbangan posisi diperiksa dalam fungsi getState, dan logika untuk memeriksa dan memproses saldo dipisahkan.
Logika perdagangan dari strategi ini dirangkum dalam fungsi ini, di mana panggilangetState , menggunakan objek generator data garis K untuk menghasilkan data garis K dari perbedaan harga, dan membuat penilaian tentang logika lindung nilai pembukaan, penutupan, dan penambahan posisi. Ada juga beberapa operasi pembaruan data untuk bagan.
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 strategi dimulai, strategi akan mulai dijalankan dari fungsi utama. Selama inisialisasi fungsi utama, strategi akan berlangganan ke pasar tick dari antarmuka websocket. Tugas utama fungsi utama adalah membangun loop utama, terus menerima informasi tick yang didorong oleh antarmuka websocket bursa, dan kemudian memanggil fungsi anggota objek kelas lindung nilai: fungsi Loop. Logika perdagangan dalam fungsi Loop didorong oleh data pasar. Satu hal yang perlu dijelaskan adalah bahwa pasar centang yang disebutkan di atas sebenarnya adalah antarmuka data kedalaman buku pesanan yang berlangganan, yang memperoleh data buku pesanan setiap level. Namun, strategi ini hanya menggunakan data dari level pertama, yang sebenarnya mirip dengan data pasar tick. Strategi ini tidak menggunakan data dari level lain, juga tidak menggunakan nilai volume order dari level pertama. Mari kita lihat lebih dekat bagaimana strategi berlangganan data antarmuka websocket dan cara pengaturannya.
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 perlu mengkode URL parameter JSON pesan berlangganan yang dikirimkan oleh antarmuka berlangganan, yaitu,payload Nilai parameter. Kemudian langkah yang lebih penting adalah memanggil fungsi antarmuka API dari Platform Perdagangan Kuantitatif InventorDial fungsi.Dial Fungsi ini dapat digunakan untuk mengakses antarmuka websocket pertukaran. Kita membuat beberapa pengaturan di sini untuk mengaktifkan objek kontrol koneksi websocket ws agar dapat dibuat untuk menyambung kembali secara otomatis setelah pemutusan (pesan langganan masih menggunakan string nilai qs dari parameter payload). Untuk mencapai fungsi ini, Anda perluDial Tambahkan opsi konfigurasi ke string parameter fungsi.
DialParameter fungsi dimulai sebagai berikut:
wss://real.okex.com:10442/ws/v3
Adalah alamat antarmuka websocket yang perlu diakses, lalu digunakan| Pemisahan.
compress=gzip_raw&mode=recv&reconnect=true&payload="+qs Ini semua adalah parameter konfigurasi.
| Nama parameter | Deskripsi |
|---|---|
| compress | compress adalah metode kompresi. Antarmuka websocket OKEX menggunakan gzip_raw, jadi diatur ke gzip_raw |
| mode | mode adalah modenya, dan opsinya adalah dual, kirim, dan terima. dual berarti dua arah, mengirim data terkompresi dan menerima data terkompresi. kirim adalah untuk mengirim data yang terkompresi. recv menerima data terkompresi dan mendekompresinya secara lokal. Bahasa Indonesia: |
| reconnect | reconnect adalah apakah akan menyetel penyambungan ulang. reconnect=true mengaktifkan penyambungan ulang. Jika tidak disetel, penyambungan ulang dinonaktifkan secara default. Bahasa Indonesia: |
| payload | Payload adalah pesan berlangganan yang perlu dikirim saat ws terhubung kembali. Bahasa Indonesia: |
Setelah pengaturan ini, bahkan jika koneksi websocket terputus, sistem dasar kustodian Inventor Quantitative Trading Platform akan secara otomatis terhubung kembali dan memperoleh data pasar terkini secara tepat waktu. Manfaatkan setiap fluktuasi perbedaan harga dan segera tangkap pasar lindung nilai yang tepat.
Kontrol posisi menggunakan rasio posisi lindung nilai yang mirip dengan seri “Porfinacci” untuk mengontrol.
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数据结构中。
}
Dapat dilihat bahwa jumlah posisi yang ditambahkan setiap kali adalah jumlah dari dua posisi terkini. Kontrol posisi tersebut dapat mencapai bahwa semakin besar perbedaan harga, semakin besar jumlah lindung nilai arbitrase, dan posisi dapat disebarkan, sehingga dapat memahami fluktuasi perbedaan harga kecil dengan posisi kecil, dan meningkatkan posisi dengan tepat dengan perbedaan harga yang besar. fluktuasi.
Spread take profit tetap, spread stop loss tetap. Ketika perbedaan harga posisi mencapai posisi take-profit atau posisi stop-loss, take profit atau stop loss akan dieksekusi.
Periode yang dikontrol oleh parameter NPeriod memberikan tingkat kontrol dinamis tertentu atas pembukaan dan penutupan posisi strategi.
Strategi ini secara otomatis menghasilkan grafik kandil perbedaan harga dan menandai informasi transaksi yang relevan.
Operasi menggambar grafik kustom pada strategi C++ juga sangat sederhana. Anda dapat melihat bahwa dalam konstruktor kelas hedge, kami menggunakan string konfigurasi grafik tertulis _cfgStr untuk mengonfigurasi objek grafik _c._c adalah kelas lindung nilai. Saat anggota privat diinisialisasi, fungsi antarmuka API bagan kustom kuantitatif milik penemu dipanggil.Chart Objek bagan yang dibangun 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(); // 重置图表数据。
_c.update(_cfgStr); menggunakan _cfgStr Konfigurasi ke objek bagan._c.reset(); Setel ulang data bagan.Ketika kode strategi perlu memasukkan data ke dalam grafik, itu juga disebut langsung_fungsi anggota objek c, atau_Referensi c dilewatkan sebagai parameter, dan kemudian fungsi anggota objek (metode) _c dipanggil untuk melakukan operasi pembaruan dan penyisipan data bagan. Misalnya:
_c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}});
Setelah melakukan pemesanan, tandai pada grafik kandil.
Seperti yang ditunjukkan di bawah ini, menggambar garis K dilakukan dengan memanggil fungsi anggota kelas BarFeederfeed Ketika objek grafik_Referensi ke c dilewatkan sebagai parameter .
void feed(double price, Chart *c=nullptr, int chartIdx=0)
SekarangfeedParameter 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, menyisipkan data Batang K-line baru ke dalam bagan.
Kode:c->add(chartIdx, point);



Strategi ini hanya untuk pembelajaran dan komunikasi. Saat menggunakannya dalam perdagangan nyata, harap modifikasi dan optimalkan sesuai dengan situasi perdagangan yang sebenarnya.
Alamat strategi: https://www.fmz.com/strategy/163447
Untuk strategi yang lebih menarik, silakan kunjungi “Inventor Quantitative Trading Platform”: https://www.fmz.com