Bercakap mengenai strategi lindung nilai, terdapat pelbagai strategi, pelbagai kombinasi, pelbagai pemikiran yang berbeza di setiap pasaran. Kami mulakan dengan idea dan konsep reka bentuk strategi lindung nilai dari jangka panjang yang paling klasik. Hari ini, pasaran mata wang digital jauh lebih aktif daripada ketika pasaran mula terbentuk, dan banyak bursa kontrak muncul, yang menawarkan banyak peluang lindung nilai.
Strategi ini agak keras, kerana ia ditulis dalam bahasa C++, dan agak sukar dibaca. Tetapi tidak menghalang pembaca daripada belajar tentang reka bentuk dan pemikiran strategi ini. Strategi ini lebih ringkas, panjang kodnya sederhana, hanya lebih daripada 500 baris. Dari segi reka bentuk, struktur strategi adalah munasabah, perpaduan kod yang rendah, mudah diperluaskan atau dioptimumkan; pemikiran logik yang jelas, reka bentuk sedemikian tidak hanya mudah digunakan dan diperluaskan; sebagai strategi pengajaran, reka bentuk strategi pembelajaran juga merupakan contoh yang baik. Prinsip strategi adalah mudah, iaitu kontrak jangka panjang dan kontrak jangka pendek, penyesuaian, lindung nilai kontra, pada asasnya sesuai dengan lindung nilai jangka panjang masa hadapan komoditi. Di samping itu, beliau juga mencadangkan untuk membuat kontrak jangka panjang dan kontrak jangka pendek. Di samping itu, beliau juga mencadangkan untuk membuat kontrak jangka panjang dan kontrak jangka pendek. Dengan asas asas yang jelas, yang tersisa adalah strategi bagaimana untuk mencetuskan hedging, bagaimana untuk menyeimbangkan, bagaimana untuk meningkatkan kedudukan. Strategi hedging adalah mengenai pergerakan harga yang berbeza dari satu mata wang kepada yang lain. Walau bagaimanapun, harga yang berbeza boleh menjadi sedikit, besar, atau satu arah. Ini membawa kepada ketidakpastian keuntungan dan kerugian lindung nilai, tetapi risiko masih jauh lebih kecil daripada trend satu hala. Untuk pelbagai optimasi strategi jangka panjang, banyak pilihan untuk bermula dari tahap kawalan kedudukan, dari pencetus posisi terbuka. Sebagai contoh, strategi ini boleh diubah dengan mudah menjadi strategi lindung nilai jangka panjang dengan indikator Brin klasik apabila pergerakan harga berbeza, membuka, membuka, dan meletakkan titik.
main
Fungsi tersebut.main
Fungsi ini adalah fungsi input untuk dasar, dan utama putaran dijalankan di dalam fungsi ini, selain itu fungsi ini juga menjalankan operasi penting, iaitu mengakses antara muka websocket pertukaran, mendapatkan data pergerakan tik yang disuntik, sebagai data mentah penjana data K-line.1, jenis senaraiState
Pernyataan
enum State { // 枚举类型 定义一些 状态
STATE_NA, // 非正常状态
STATE_IDLE, // 空闲
STATE_HOLD_LONG, // 持多仓
STATE_HOLD_SHORT, // 持空仓
};
Kerana terdapat beberapa fungsi dalam kod yang mengembalikan keadaan tertentu, maka keadaan ini ditakrifkan dalam jenis senarai.State
Antara lain:
Lihat di dalam kod.STATE_NA
Di sini, kita akan melihat bagaimana ia berfungsi.STATE_IDLE
Untuk keadaan kosong, iaitu keadaan yang boleh digunakan sebagai perlindungan.STATE_HOLD_LONG
Status untuk memegang kedudukan lindung nilai yang sah.STATE_HOLD_SHORT
Keadaan untuk memegang kedudukan lindung nilai.
2, Penggantian String, tidak dipanggil dalam dasar ini, adalah fungsi alat sandaran, yang menangani strings terutamanya.
string replace(string s, const string from, const string& to)
Fungsi 3, sebagai fungsi yang ditukar kepada 16 digittoHex
inline unsigned char toHex(unsigned char x)
4. Fungsi yang memproses kod url
std::string urlencode(const std::string& str)
5, fungsi penukaran masa, menukar masa dalam format senar kepada senar masa.
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 bertanggungjawab untuk memproses data tik yang diperoleh ke dalam garisan K perbezaan untuk memandu logik lindung nilai strategi. Mungkin ada pembaca yang mempunyai soalan, mengapa menggunakan data tik? Mengapa membina penjana data K seperti ini? Adakah ia tidak baik untuk menggunakan data K secara langsung? Soalan seperti ini berulang kali muncul, dan juga muncul ketika saya mula menulis beberapa strategi lindung nilai. Data garis K perbezaan harga dua kontrak adalah statistik perubahan harga perbezaan dalam tempoh tertentu, jadi tidak boleh mengambil data garis K kedua kontrak masing-masing untuk melakukan pengurangan operasi, untuk mengira perbezaan data pada setiap garis K Bar, sebagai perbezaan. Kesalahan yang paling jelas adalah, sebagai contoh, harga tertinggi, harga terendah, tidak semestinya pada masa yang sama. Oleh itu, jumlah nilai yang dikurangkan tidak begitu penting. Oleh itu, kita perlu menggunakan data tik masa nyata, perhitungan perbezaan masa nyata, statistik masa nyata untuk perubahan harga dalam satu kitaran tertentu (iaitu kenaikan tinggi dan rendah pada tiang 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(""); // 图表对象,并初始化
};
Oleh kerana kodnya agak panjang, satu bahagian telah dihapuskan, terutama untuk menunjukkan struktur kelas lindung nilai ini, fungsi pembinaan Hedge tidak disebutkan, terutama untuk inisialisasi objek; selebihnya, terdapat 2 fungsi utama.
getState
Fungsi ini terutamanya menangani pemeriksaan pesanan, pembatalan pesanan, pemeriksaan kedudukan, keseimbangan kedudukan dan lain-lain. Kerana dalam proses dagangan lindung nilai, tidak dapat dielakkan keadaan kaki tunggal (iaitu, kontrak yang telah diselesaikan, kontrak yang tidak diselesaikan), jika pemeriksaan dilakukan dalam logik pesanan bawah, kemudian memproses pesanan atau perpecahan, logik strategi akan menjadi agak berantakan. Oleh itu, pemikiran lain diambil ketika merancang bahagian ini.
Lemparan
Logik transaksi strategi terbungkus dalam fungsi ini, di mana panggilangetState
, Menggunakan objek penjana data K-line untuk menghasilkan data K-line perbezaan harga, untuk membuat pertimbangan logik pembukaan, penempatan, dan penambahan hedging. Terdapat juga beberapa operasi untuk mengemas kini data carta.
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接口 推送来的数据
...
}
Apabila dasar dimulakan, ia dilaksanakan dari fungsi main, dan dalam kerja inisialisasi fungsi main, ia melanggan pasaran tik pada antara muka websocket. Tugas utama fungsi main adalah untuk membina satu putaran utama, menerima pasaran tik yang dihantar oleh antara muka websocket pertukaran yang berterusan, dan kemudian memanggil fungsi ahli objek jenis lindung nilai: Loop fungsi. Logik dagangan dalam Loop fungsi yang didorong oleh data pasaran. Satu perkara yang perlu diperhatikan, pasaran tik yang disebut di atas sebenarnya adalah antarmuka data mendalam pesanan langganan, yang memperoleh data pesanan tipis untuk setiap kumpulan. Tetapi strategi hanya menggunakan data kumpulan pertama, sebenarnya hampir sama dengan data pasaran tik, strategi tidak menggunakan data kumpulan lain, atau tidak menggunakan jumlah pesanan kumpulan pertama. Untuk maklumat lanjut, sila lihat di sini: Bagaimana untuk mendaftarkan data pada websocket interface dan bagaimana untuk menetapkannya.
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 mengkodkan url untuk mesej langganan yang dihantar ke antara muka langganan dengan parameter json, iaitupayload
Nilai parameter. Kemudian langkah yang lebih penting adalah memanggil fungsi antara muka API platform perdagangan kuantiti pencipta.Dial
Fungsi tersebut.Dial
Fungsi ini boleh digunakan untuk mengakses antara muka websocket bursa. Kami melakukan beberapa tetapan di sini supaya websocket yang akan dicipta untuk mengontrol objek sambungan ws mempunyai sambungan putus secara automatik ((pesanan langganan masih menggunakan string qs nilai parameter payload), untuk mencapai fungsi ini memerlukanDial
Menambah pilihan konfigurasi pada senar parameter fungsi.
Dial
Bahagian awal parameter fungsi adalah sebagai berikut:
wss://real.okex.com:10442/ws/v3
Ini adalah alamat antara muka websocket yang perlu diakses dan kemudian digunakan untuk|
Perpisahan.compress=gzip_raw&mode=recv&reconnect=true&payload="+qs
Semua parameter adalah parameter konfigurasi.
Nama parameter | Penerangan |
---|---|
memampatkan | compress sebagai mod mampatan, yang digunakan oleh antara muka websocket OKEX gzip_raw |
mod | mod sebagai mod, pilihan dual, send, recv Tiga jenis.dual sebagai dua arah, menghantar data mampatan, menerima data mampatan.send untuk menghantar data mampatan.recv untuk menerima data mampatan, membasmi mampatan tempatan. |
sambung semula | reconnect menunjukkan sama ada sambungan semula ditetapkan, reconnect=true untuk membolehkan sambungan semula, tanpa menetapkan lalai tidak disambung semula. |
muatan | Payload adalah mesej langganan yang perlu dihantar apabilaws disambung semula. |
Dengan cara ini, walaupun sambungan websocket terputus, pencipta akan mengutip platform dagangan kuantitatif. Sistem bawah pentadbir juga akan disambung semula secara automatik untuk mendapatkan data pasaran terkini pada masa yang tepat. Di samping itu, ia juga boleh digunakan untuk mengesan setiap pergerakan harga dan dengan cepat menangkap pasaran lindung nilai yang sesuai.
Pengendalian kedudukan menggunakan peratusan kedudukan lindung nilai yang serupa dengan baris nombor "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数据结构中。
}
Seperti yang dapat dilihat, setiap kali jumlah kedudukan yang ditingkatkan adalah jumlah kedua-dua kedudukan terdahulu yang paling baru. Pengendalian kedudukan sedemikian dapat dicapai dengan peningkatan margin yang lebih besar, peningkatan jumlah lindungan faedah yang relatif, dan penyebaran kedudukan, sehingga dapat menangkap kedudukan kecil yang berfluktuasi dengan perbezaan kecil, kedudukan besar yang berfluktuasi dengan peningkatan yang sewajarnya.
Harga hentian tetap, harga hentian rugi. Pengepungan saham akan dilakukan apabila harga yang berlainan mencapai kedudukan kenaikan, kedudukan stop loss dan kedudukan stop loss.
Parameter NPeriod mengawal tempoh untuk mengawal pergerakan strategi.
Strategi ini secara automatik menjana carta garis K perbezaan, menandai maklumat dagangan yang berkaitan.
Kaedah C++ untuk membuat rajah rajah khas juga sangat mudah, seperti yang dapat dilihat dalam fungsi pembina kelas hedge, kita menggunakan senar_cfgStr untuk menetapkan objek rajah _c, _c adalah ahli persendirian kelas hedge.Chart
Objek grafik yang dibina 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);
Gunakan _cfgStr untuk mengkonfigurasi objek grafik._c.reset();
Mengubah semula data grafik.Apabila kod strategi memerlukan pemasangan data ke dalam carta, ia juga boleh dilakukan dengan memanggil fungsi ahli objek _c secara langsung, atau menghantar rujukan objek _c sebagai parameter, dan kemudian memanggil fungsi ahli objek _c ([[metode]]). Contohnya:
_c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}});
Apabila pesanan selesai, catatan label akan diletakkan pada carta K.
Di bawah ini, garis K digambarkan sebagai fungsi ahli kelas BarFeeder dengan memanggilfeed
Apabila anda memasukkan rujukan kepada objek grafik _c sebagai parameter, anda akan melihat bahawa _c adalah rujukan kepada objek grafik.
void feed(double price, Chart *c=nullptr, int chartIdx=0)
Maksudnya,feed
Parameter 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 _cadd
Fungsi ahli untuk memasukkan data Bar K bar baru ke dalam carta.
Kode:c->add(chartIdx, point);
Kaedah ini hanya digunakan untuk pembelajaran pertukaran, jika menggunakan cakera sebenar, sila ubah dan optimumkan sendiri mengikut keadaan sebenar cakera sebenar.
Alamat strategi:https://www.fmz.com/strategy/163447
Lebih banyak strategi menarik boleh didapati di "Inventors Quantify Trading Platform":https://www.fmz.com