Bercakap mengenai strategi lindung nilai, terdapat pelbagai jenis, kombinasi yang pelbagai, dan pelbagai idea di pelbagai pasaran. Kami meneroka idea dan konsep reka bentuk strategi lindung nilai dari lindung nilai intertemporal yang paling klasik. Hari ini, pasaran mata wang kripto jauh lebih aktif daripada pada mulanya, dan juga terdapat banyak bursa kontrak niaga hadapan yang menawarkan banyak peluang untuk lindung nilai arbitrase. Arbitraj spot lintas pasaran, arbitraj lindung nilai tunai, arbitraj intertemporal niaga hadapan, arbitraj lintas pasaran niaga hadapan, dan lain-lain, strategi perdagangan kuantitatif kripto muncul satu demi satu. Mari kita lihat strategi lindung nilai intertemporal
strategi ini agak hardcore kerana strategi ini ditulis dalam bahasa c++ dan bacaan strategi sedikit lebih sukar. Tetapi ia tidak menghalang pembaca daripada mempelajari intipati reka bentuk dan idea strategi ini. Logik strategi agak mudah, panjang kod sederhana, hanya 500 baris. Dari segi pemerolehan data pasaran, tidak seperti strategi lain yang menggunakan antara muka
Dari segi reka bentuk, struktur strategi adalah munasabah, tahap penyambungan kod sangat rendah, dan mudah untuk berkembang atau mengoptimumkan. Logiknya jelas, dan reka bentuk sedemikian tidak hanya mudah difahami. Sebagai bahan pengajaran, mempelajari reka bentuk strategi ini juga merupakan contoh yang baik. Prinsip strategi ini agak mudah, iaitu, adakah penyebaran kontrak hadapan dan kontrak baru-baru ini positif atau negatif? prinsip asasnya konsisten dengan lindung nilai intertemporal niaga hadapan komoditi.
selepas memahami prinsip asas, selebihnya adalah bagaimana strategi mencetuskan kedudukan pembukaan lindung nilai, bagaimana untuk menutup kedudukan, bagaimana untuk menambah kedudukan, kaedah kawalan kedudukan keseluruhan dan pemprosesan butiran strategi lain.
Strategi lindung nilai terutamanya berkaitan dengan turun naik perbezaan harga subjek (The Spread) dan regresi.
Ini membawa ketidakpastian mengenai lindung nilai keuntungan dan kerugian, tetapi risiko masih jauh lebih kecil daripada trend sepihak. untuk pelbagai pengoptimuman strategi intertemporal, kita boleh memilih untuk bermula dari tahap kawalan kedudukan dan keadaan pencetus pembukaan dan penutupan. contohnya, kita boleh menggunakan
Melihat kod di seluruh, anda boleh membuat kesimpulan bahawa kod itu kira-kira dibahagikan kepada empat bahagian.
Senaraikan definisi nilai, tentukan beberapa nilai keadaan, dan gunakan untuk menandakan keadaan. Beberapa fungsi fungsional yang tidak berkaitan dengan strategi, seperti fungsi pengekodan url, fungsi penukaran masa, dan lain-lain, tidak mempunyai hubungan dengan logik strategi, hanya untuk pemprosesan data.
Kelas penjana data K-line: Strategi ini didorong oleh data K-line yang dihasilkan oleh objek kelas penjana.
Kelas lindung nilai: Objek kelas ini boleh melakukan logik perdagangan tertentu, operasi lindung nilai, dan memproses butiran strategi.
Fungsi utama strategi, yang merupakan fungsi
Melalui pemahaman keseluruhan kod strategi, kita boleh secara beransur-ansur mempelajari pelbagai aspek strategi, dan kemudian mengkaji reka bentuk, idea dan kemahiran 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
};
Kerana beberapa fungsi dalam kod mengembalikan keadaan, keadaan ini ditakrifkan dalam jenis pengiraanState
.
Melihat bahawaSTATE_NA
muncul dalam kod adalah tidak normal, danSTATE_IDLE
adalah tidak aktif, iaitu keadaan operasi boleh dilindungi.STATE_HOLD_LONG
adalah keadaan di mana kedudukan lindung nilai positif dipegang.STATE_HOLD_SHORT
adalah keadaan di mana kedudukan 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 bertanggungjawab terutamanya untuk memproses data tik yang diperoleh ke dalam garis perbezaan K untuk memandu logik lindung nilai strategi.
Beberapa pembaca mungkin mempunyai soalan, mengapa menggunakan data tik? Mengapa membina penjana data K-line seperti ini? Tidakkah baik untuk menggunakan data K-line secara langsung? Soalan semacam ini telah dikeluarkan dalam tiga ledakan. Apabila saya menulis beberapa strategi lindung nilai, saya juga membuat keributan. Saya mendapat jawapannya ketika saya menulis
Data K-line perbezaan antara dua kontrak adalah statistik perubahan harga perbezaan dalam tempoh tertentu. Oleh itu, tidak mungkin untuk mengambil data K-line setiap kontrak untuk pengurangan dan mengira perbezaan setiap data pada setiap Bar K-line. Kesalahan yang paling jelas adalah, sebagai contoh, harga tertinggi dan harga terendah dua kontrak, tidak semestinya pada masa yang sama. Jadi nilai yang dikurangkan tidak banyak masuk akal.
Oleh itu, kita perlu menggunakan data tik masa nyata untuk mengira perbezaan dalam masa nyata dan mengira perubahan harga dalam tempoh tertentu dalam masa nyata (iaitu, harga tertinggi, terendah, terbuka dan ditutup pada lajur K-garis).
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
};
Kerana kodnya agak panjang, beberapa bahagian dihilangkan, ini terutamanya menunjukkan struktur kelas lindung nilai ini, fungsi pembina lindung nilai dihilangkan, terutamanya untuk tujuan permulaan objek.
getState
Fungsi ini terutamanya berkaitan dengan pemeriksaan pesanan, pembatalan pesanan, pengesanan kedudukan, penyeimbangan kedudukan dan sebagainya. Kerana dalam proses urus niaga lindung nilai, mustahil untuk mengelakkan satu kaki (iaitu, kontrak dilaksanakan, yang lain tidak), jika pemeriksaan dilakukan dalam logik pesanan penempatan, dan kemudian pemprosesan operasi pesanan kembali atau operasi kedudukan penutupan, logik strategi akan menjadi huru-hara.
Jadi ketika merancang bahagian ini, saya mengambil idea lain. jika operasi lindung nilai dicetuskan, selagi pesanan diletakkan sekali, tidak kira sama ada terdapat lindung nilai satu kaki, lalai adalah bahawa lindung nilai adalah berjaya, dan kemudian baki kedudukan dikesan dalamgetState
fungsi, dan logik untuk memproses baki akan ditangani secara bebas.
Lemparan
Logik dagangan strategi ini disimpulkan dalam fungsi ini, di managetState
objek penjana data K-garis digunakan untuk menjana data K-garis perbezaan, dan penghakiman pembukaan, penutupan, dan penambahan logik kedudukan 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 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
...
}
Selepas strategi dimulakan, ia dilaksanakan dari fungsi utama. Dalam inisialisasi fungsi utama, strategi melanggan pasaran tik antara muka websocket. Tugas utama fungsi utama adalah untuk membina gelung utama yang terus menerima sebut harga tik yang didorong oleh antara muka websocket bursa
Satu perkara yang perlu diperhatikan adalah bahawa pasaran tik yang disebutkan di atas sebenarnya adalah antara muka data kedalaman nipis pesanan langganan, yang merupakan data buku pesanan untuk setiap fail. Walau bagaimanapun, strategi hanya menggunakan fail data pertama, sebenarnya, ia hampir sama dengan data pasaran tik. Strategi tidak menggunakan data fail lain, juga tidak menggunakan nilai pesanan fail pertama.
Lihat lebih dekat bagaimana strategi melanggan data antara muka websocket dan bagaimana ia disiapkan.
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");
Pertama, penyulitan url mesej langganan json parameter yang diluluskan oleh antara muka langganan, iaitu nilaipayload
Kemudian langkah penting adalah untuk memanggil FMZ Quant platformDial
fungsi.Dial
fungsi boleh digunakan untuk mengakses antara muka websocket pertukaranqs
rentetanpayload
parameter), untuk mencapai fungsi ini, anda perlu menambah konfigurasi dalam rentetan parameterDial
function.
PermulaanDial
Parameter fungsi adalah seperti berikut:
wss://real.okex.com:10442/ws/v3
ini adalah alamat antara muka websocket yang perlu diakses, dan dipisahkan oleh
compress=gzip_raw&mode=recv&reconnect=true&payload="+qs
adalah semua parameter konfigurasi.
Nama parameter | Penerangan |
---|---|
memampatkan | memampatkan adalah mod mampatan, antara muka websocket OKEX menggunakan gzip_raw cara ini, jadi ia ditetapkan untuk gzip_raw |
mod | mod adalah mod, pilihan dua, menghantar dan menerima tiga jenis. dual adalah bidirectional, menghantar data terdesak dan menerima data terdesak. menghantar adalah untuk menghantar data terdesak. recv menerima data terdesak dan mendekompresnya secara tempatan. |
sambung semula | Sambungan semula ditetapkan untuk menyambung semula, reconnect=true untuk membolehkan sambungan semula, tiada lalai tidak disambung semula. |
muatan | Beban berguna adalah mesej langganan yang perlu dihantar apabila ws disambungkan semula. |
Selepas tetapan ini, walaupun sambungan websocket terputus, platform dagangan FMZ Quant
Mengambil setiap turun naik harga dan dengan cepat menangkap lindung nilai yang betul.
Kawalan kedudukan dikawal menggunakan nisbah kedudukan lindung nilai yang serupa dengan siri
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.
}
Ia boleh dilihat bahawa bilangan kedudukan tambahan yang ditambah setiap kali adalah jumlah dua kedudukan terakhir.
Kawalan kedudukan sedemikian dapat merealisasikan perbezaan yang lebih besar, peningkatan nisbah lindung nilai arbitrase, dan penyebaran kedudukan, supaya dapat memahami kedudukan kecil turun naik harga kecil, dan kedudukan turun naik harga besar meningkat dengan sewajarnya.
Spread stop loss tetap dan Spread keuntungan.
Apabila perbezaan kedudukan mencapai kedudukan mengambil keuntungan dan kedudukan hentian kerugian, mengambil keuntungan dan hentian kerugian dijalankan.
Tempoh parameterNPeriod
kawalan menyediakan beberapa kawalan dinamik ke atas kedudukan pembukaan dan penutupan strategi.
Strategi secara automatik menjana carta garis K yang tersebar untuk menandakan maklumat transaksi yang relevan.
C ++ strategi operasi lukisan carta tersuai juga sangat mudah. anda boleh melihat bahawa dalam pembina kelas lindung nilai, kita menggunakan senar konfigurasi carta yang ditulis_cfgStr
untuk mengkonfigurasi objek carta_c
, _c
adalah komponen persendirian kelas lindung nilai. Apabila anggota persendirian dimulakan,chart
objek yang dibina oleh FMZ Quant platform fungsi antara muka API carta tersuai 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.
Apabila kod strategi perlu memasukkan data ke dalam carta, ia juga memanggil fungsi ahli_c
objek secara langsung, atau lulus rujukan_c
sebagai parameter, dan kemudian memanggil fungsi ahli objek (metod)_c
untuk mengemas kini data carta dan memasukkan operasi.
Contohnya:
_c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}});
Selepas meletakkan pesanan, tanda carta garis K.
Seperti berikut, apabila melukis garis K, rujukan kepada objek carta_c
adalah lulus sebagai parameter apabila memanggil fungsi ahlifeed
daripadaBarFeeder
class.
void feed(double price, chart *c=nullptr, int chartIdx=0)
Iaitu, parameter formalc
daripadafeed
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 bar K-garis baru ke dalam carta dengan memanggiladd
fungsi ahli objek carta_c
.
c->add(chartIdx, point);
Strategi ini hanya untuk tujuan pembelajaran dan komunikasi. Apabila digunakan di pasaran sebenar, sila ubah dan optimumkan mengikut keadaan pasaran yang sebenar.
Alamat strategi:https://www.fmz.com/strategy/163447
Strategi yang lebih menarik adalah dalam platform FMZ Quant":https://www.fmz.com