Berbicara tentang strategi lindung nilai, ada berbagai jenis, kombinasi yang beragam, dan ide yang beragam di berbagai pasar. Kami mengeksplorasi ide desain dan konsep strategi lindung nilai dari lindung nilai intertemporal yang paling klasik. Saat ini, pasar mata uang kripto jauh lebih aktif daripada pada awalnya, dan juga ada banyak bursa kontrak berjangka yang menawarkan banyak peluang untuk lindung nilai arbitrage. Arbitrage lintas pasar spot, arbitrage lindung nilai tunai, arbitrage intertemporal berjangka, arbitrage lintas pasar berjangka, dll, strategi perdagangan kuantitatif kripto muncul satu demi satu. Mari kita lihat strategi lindung nilai intertemporal
strategi ini agak hardcore karena strategi ini ditulis dalam bahasa C++ dan pembacaan strategi sedikit lebih sulit. Tapi itu tidak mencegah pembaca untuk mempelajari esensi desain dan ide strategi ini. Logika strategi relatif sederhana, panjang kode moderat, hanya 500 baris. Dalam hal akuisisi data pasar, tidak seperti strategi lain yang menggunakan antarmuka
Dalam hal desain, struktur strategi masuk akal, tingkat kopling kode sangat rendah, dan mudah diperluas atau dioptimalkan. Logika jelas, dan desain semacam itu tidak hanya mudah dipahami. Sebagai bahan ajar, mempelajari desain strategi ini juga merupakan contoh yang baik. Prinsip strategi ini relatif sederhana, yaitu, apakah spread kontrak maju dan kontrak baru-baru ini positif atau negatif? prinsip dasarnya konsisten dengan lindung nilai intertemporal komoditas berjangka.
- Spread Positive, menjual kontrak jangka pendek, membeli kontrak lama. - Spread negatif, membeli kontrak jangka panjang, menjual kontrak pendek baru-baru ini.
setelah memahami prinsip-prinsip dasar, sisanya adalah bagaimana strategi memicu posisi pembukaan lindung nilai, bagaimana menutup posisi, bagaimana menambahkan posisi, metode kontrol posisi total dan pemrosesan detail strategi lainnya.
Strategi lindung nilai terutama berkaitan dengan fluktuasi perbedaan harga subjek (The Spread) dan regresi.
Hal ini membawa ketidakpastian tentang lindung nilai keuntungan dan kerugian, tetapi risikonya masih jauh lebih kecil daripada tren sepihak. Untuk berbagai optimasi strategi intertemporal, kita dapat memilih untuk memulai dari tingkat pengendalian posisi dan kondisi pemicu pembukaan dan penutupan. Misalnya, kita dapat menggunakan Indikator Bollinger Band klasik untuk menentukan fluktuasi harga. Karena desain yang wajar dan tingkat kopling yang rendah, strategi ini dapat dengan mudah dimodifikasi menjadi strategi lindung nilai intertemporal indeks Bollinger
Melihat kode di seluruhnya, Anda dapat menyimpulkan bahwa kode secara kasar dibagi menjadi empat bagian.
Menghitung definisi nilai, mendefinisikan beberapa nilai keadaan, dan digunakan untuk menandai keadaan. Beberapa fungsi fungsional yang tidak terkait dengan strategi, seperti fungsi pengkodean url, fungsi konversi waktu, dll, tidak memiliki hubungan dengan logika strategi, hanya untuk pemrosesan data.
Kelas generator data K-line: Strategi ini didorong oleh data K-line yang dihasilkan oleh objek kelas generator.
Kelas lindung nilai: Objek kelas ini dapat melakukan logika perdagangan tertentu, operasi lindung nilai, dan detail pemrosesan strategi.
Fungsi utama dari strategi, yaitu fungsi
Melalui pemahaman keseluruhan tentang kode strategi, kita dapat secara bertahap mempelajari berbagai aspek strategi, dan kemudian mempelajari desain, ide dan keterampilan strategi.
State
pernyataanenum 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
};
Karena beberapa fungsi dalam kode mengembalikan keadaan, keadaan ini didefinisikan dalam jenis enumerasiState
.
Melihat ituSTATE_NA
muncul dalam kode tidak normal, danSTATE_IDLE
adalah tidak aktif, yaitu, keadaan operasi dapat dilindungi.STATE_HOLD_LONG
adalah kondisi di mana posisi lindung nilai positif dipegang.STATE_HOLD_SHORT
adalah kondisi di mana posisi lindung nilai negatif dipegang.
string replace(string s, const string from, const string& to)
toHex
inline unsigned char toHex(unsigned char x)
std::string urlencode(const std::string& str)
uint64_t _Time(string &s)
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;
};
Kelas ini terutama bertanggung jawab untuk memproses data tick yang diperoleh ke dalam garis K perbedaan untuk mendorong logika lindung nilai strategi.
Beberapa pembaca mungkin memiliki pertanyaan, mengapa menggunakan data tik? Mengapa membangun generator data K-line seperti ini? Bukankah baik untuk menggunakan data K-line secara langsung? Pertanyaan semacam ini telah dikeluarkan dalam tiga ledakan. Ketika saya menulis beberapa strategi lindung nilai, saya juga membuat keributan. Saya menemukan jawabannya ketika saya menulis
Data K-line dari perbedaan antara dua kontrak adalah statistik perubahan harga perbedaan dalam periode tertentu. Oleh karena itu, tidak mungkin untuk hanya mengambil data K-line dari masing-masing dari dua kontrak untuk pengurangan dan menghitung perbedaan setiap data pada setiap Bar K-line. Kesalahan yang paling jelas adalah, misalnya, harga tertinggi dan harga terendah dari dua kontrak, tidak selalu pada saat yang sama. Jadi nilai yang dikurangi tidak banyak masuk akal.
Oleh karena itu, kita perlu menggunakan data tick real-time untuk menghitung perbedaan dalam waktu nyata dan menghitung perubahan harga dalam periode tertentu dalam waktu nyata (yaitu, harga tertinggi, terendah, terbuka dan dekat pada kolom K-line).
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
};
Karena kode relatif panjang, beberapa bagian dihilangkan, ini terutama menunjukkan struktur kelas lindung nilai ini, constructor fungsi lindung nilai dihilangkan, terutama untuk tujuan inisialisasi objek.
getState
Fungsi ini terutama berkaitan dengan pemeriksaan pesanan, pembatalan pesanan, deteksi posisi, penyeimbangan posisi dan sebagainya. Karena dalam proses transaksi lindung nilai, tidak mungkin untuk menghindari satu kaki (yaitu, kontrak dieksekusi, yang lain tidak), jika pemeriksaan dilakukan dalam logika pesanan penempatan, dan kemudian pemrosesan operasi pesanan kembali atau operasi posisi penutupan, logika strategi akan kacau.
Jadi ketika merancang bagian ini, saya mengambil ide lain. jika operasi lindung nilai dipicu, selama pesanan ditempatkan sekali, terlepas dari apakah ada lindung nilai satu kaki, default adalah bahwa lindung nilai berhasil, dan kemudian saldo posisi terdeteksi digetState
fungsi, dan logika untuk memproses saldo akan ditangani secara independen.
Lompatan
Logika perdagangan dari strategi ini disimpulkan dalam fungsi ini, di managetState
disebut, dan objek generator data garis K digunakan untuk menghasilkan data garis K dari perbedaan (the spread), dan penilaian pembukaan, penutupan, dan penjumlahan logika posisi dilakukan.
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 access the WS interface of OKEX Futures
Log("connect to websocket success");
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
...
}
Setelah strategi dimulai, ia dieksekusi dari fungsi utama. Dalam inisialisasi fungsi utama, strategi berlangganan ke pasar tik antarmuka websocket. Tugas utama fungsi utama adalah untuk membangun loop utama yang terus menerima kutipan tik yang didorong oleh antarmuka websocket bursa, dan kemudian memanggil anggota fungsi dari objek kelas lindung nilai: fungsi loop. Logika perdagangan dalam fungsi loop didorong oleh data pasar.
Satu hal yang perlu dicatat adalah bahwa pasar tik yang disebutkan di atas sebenarnya adalah antarmuka data kedalaman tipis pesanan langganan, yang merupakan data buku pesanan untuk setiap file. Namun, strategi hanya menggunakan file data pertama, pada kenyataannya, hampir sama dengan data pasar tik. Strategi tidak menggunakan data file lain, juga tidak menggunakan nilai pesanan dari file pertama.
Lihat lebih dekat bagaimana strategi berlangganan data antarmuka websocket dan bagaimana itu diatur.
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, URL pengkodean dari pesan langganan json parameter dilewatkan oleh antarmuka langganan, yaitu nilai daripayload
Kemudian langkah penting adalah untuk memanggil fungsi antarmuka API platform FMZ QuantDial
Fungsi.Dial
fungsi dapat digunakan untuk mengakses antarmuka websocket exchangeqs
string daripayload
parameter), untuk mencapai fungsi ini, Anda perlu menambahkan konfigurasi dalam string parameter dariDial
function.
Awal dariDial
parameter fungsi adalah sebagai berikut:
wss://real.okex.com:10442/ws/v3
ini adalah alamat antarmuka websocket yang perlu diakses, dan dipisahkan oleh
Compress=gzip_raw&mode=recv&reconnect=true&payload="+qs
adalah semua parameter konfigurasi.
Nama parameter | deskripsi |
---|---|
mengompres | Compress adalah mode kompresi, antarmuka websocket OKEX menggunakan gzip_raw dengan cara ini, sehingga ditetapkan untuk gzip_raw |
modus | Mode adalah mode, opsional dual, send dan recv tiga jenis. dual adalah bidirectional, mengirim data terkompresi dan menerima data terkompresi. send adalah untuk mengirim data terkompresi. recv menerima data terkompresi dan mendekompresnya secara lokal. |
Hubungkan kembali | Reconnect diatur untuk terhubung kembali, reconnect=true untuk memungkinkan koneksi kembali, tidak ada default tidak terhubung kembali. |
muatan | Muat adalah pesan langganan yang perlu dikirim saat ws dihubungkan kembali. |
Setelah pengaturan ini, bahkan jika koneksi websocket terputus, sistem dasar platform perdagangan FMZ Quant dari sistem docker akan secara otomatis terhubung kembali dan mendapatkan data pasar terbaru tepat waktu.
Tangkap setiap fluktuasi harga dan cepat menangkap lindung nilai yang tepat.
Kontrol posisi dikendalikan dengan menggunakan rasio posisi lindung nilai yang mirip dengan seri
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.
}
Hal ini dapat dilihat bahwa jumlah posisi tambahan yang ditambahkan setiap kali adalah jumlah dari dua posisi terakhir.
Kontrol posisi tersebut dapat mewujudkan perbedaan yang lebih besar, peningkatan relatif dari lindung nilai arbitrase, dan dispersi posisi, sehingga menangkap posisi kecil dari fluktuasi harga kecil, dan posisi fluktuasi harga besar ditingkatkan dengan tepat.
Spread stop loss tetap dan take profit spread.
Ketika perbedaan posisi mencapai posisi take profit dan posisi stop loss, take profit dan stop loss dilakukan.
Periode parameterNPeriod
kontrol memberikan beberapa kontrol dinamis atas posisi pembukaan dan penutupan strategi.
Strategi secara otomatis menghasilkan grafik garis K spread untuk menandai informasi transaksi yang relevan.
C++ strategi operasi menggambar grafik kustom juga sangat sederhana. Anda dapat melihat bahwa dalam konstruktor kelas lindung nilai, kita menggunakan ditulis grafik konfigurasi string_cfgStr
untuk mengkonfigurasi objek grafik_c
, _c
adalah komponen pribadi dari kelas lindung nilai.chart
objek yang dibangun oleh FMZ Quant platform fungsi antarmuka API grafik khusus dipanggil.
_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.
Ketika kode strategi perlu memasukkan data ke dalam grafik, ia juga memanggil anggota fungsi dari_c
objek langsung, atau melewati referensi dari_c
sebagai parameter, dan kemudian memanggil fungsi anggota objek (metode) dari_c
untuk memperbarui data grafik dan memasukkan operasi.
Misalnya:
_c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}});
Setelah memesan, tanda grafik garis K.
Berikut ini, ketika menggambar garis K, referensi untuk objek grafik_c
diteruskan sebagai parameter ketika memanggil fungsi anggotafeed
dariBarFeeder
class.
void feed(double price, Chart *c=nullptr, int chartIdx=0)
Artinya, parameter formalc
darifeed
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).
}
}
Masukkan data baru K-line Bar ke dalam grafik dengan memanggiladd
fungsi anggota dari objek grafik_c
.
c->add(chartIdx, point);
Strategi ini hanya untuk tujuan pembelajaran dan komunikasi. Ketika menerapkannya di pasar nyata, silakan modifikasi dan optimalisasi sesuai dengan situasi pasar yang sebenarnya.
Alamat strategi:https://www.fmz.com/strategy/163447
Strategi yang lebih menarik ada di platform FMZ Quant":https://www.fmz.com