
Khi nói đến các chiến lược phòng ngừa rủi ro, có nhiều chiến lược, nhiều sự kết hợp và nhiều ý tưởng khác nhau trên nhiều thị trường khác nhau. Chúng ta sẽ bắt đầu với chiến lược phòng ngừa rủi ro xuyên thời kỳ cổ điển nhất để khám phá các ý tưởng thiết kế và khái niệm về chiến lược phòng ngừa rủi ro. Ngày nay, thị trường tiền kỹ thuật số sôi động hơn nhiều so với khi thị trường mới hình thành và nhiều sàn giao dịch hợp đồng đã xuất hiện, mang đến nhiều cơ hội đầu tư chênh lệch giá và phòng ngừa rủi ro. Có vô số chiến lược, chẳng hạn như kinh doanh chênh lệch giá giao ngay chéo thị trường, kinh doanh chênh lệch giá phòng ngừa rủi ro giao ngay-tương lai, kinh doanh chênh lệch giá tương lai chéo kỳ hạn, kinh doanh chênh lệch giá tương lai chéo thị trường, v.v. Tiếp theo, chúng ta hãy xem xét một chiến lược phòng ngừa rủi ro xuyên kỳ “hardcore” được viết bằng ngôn ngữ C++, với thị trường giao dịch là sàn giao dịch hợp đồng OKEX. Chiến lược này được viết dựa trên “Nền tảng giao dịch định lượng Inventor”.
Lý do khiến chiến lược này có phần khó khăn là vì chiến lược này được viết bằng ngôn ngữ C++, khiến nó hơi khó đọc. Tuy nhiên, điều này không ngăn cản người đọc tìm hiểu bản chất của thiết kế và ý tưởng chiến lược này. Chiến lược này tương đối súc tích và độ dài mã ở mức vừa phải, chỉ hơn 500 dòng. Về mặt thu thập dữ liệu thị trường, không giống như các chiến lược trước đây sử dụng giao diện rest, chiến lược này sử dụng giao diện websocket để nhận dữ liệu thị trường từ các sàn giao dịch. Về mặt thiết kế, cấu trúc chiến lược hợp lý, mức độ liên kết mã rất thấp, rất thuận tiện cho việc mở rộng hoặc tối ưu hóa. Tư duy logic rõ ràng và thiết kế này không chỉ dễ sử dụng và mở rộng. Là một chiến lược giảng dạy, thiết kế chiến lược học tập cũng là một ví dụ điển hình. Nguyên lý của chiến lược này tương đối đơn giản, đó là sử dụng hợp đồng kỳ hạn và hợp đồng ngắn hạn để phòng ngừa rủi ro tích cực và tiêu cực, về cơ bản giống với phòng ngừa rủi ro chéo kỳ hạn của hợp đồng tương lai hàng hóa. Trọng tài tích cực, hợp đồng kỳ hạn ngắn hạn và hợp đồng dài hạn ngắn hạn. Phòng ngừa rủi ro, mua vào các hợp đồng kỳ hạn và bán ra các hợp đồng ngắn hạn. Bây giờ các nguyên tắc cơ bản đã rõ ràng, điều còn lại là cách chiến lược kích hoạt các vị thế phòng ngừa rủi ro, cách đóng các vị thế và cách tăng các vị thế. Phương pháp kiểm soát vị trí và xử lý chi tiết chiến lược. Chiến lược phòng ngừa rủi ro chủ yếu tập trung vào sự biến động chênh lệch giá của tài sản cơ sở và thực hiện giao dịch hồi quy trên chênh lệch giá. Tuy nhiên, mức chênh lệch có thể dao động nhẹ, hoặc mạnh hoặc theo một hướng. Điều này gây ra sự không chắc chắn trong việc phòng ngừa lợi nhuận và thua lỗ, nhưng rủi ro vẫn thấp hơn nhiều so với xu hướng đơn phương. Nhiều phương pháp tối ưu hóa khác nhau của các chiến lược xuyên giai đoạn chọn bắt đầu từ cấp độ kiểm soát vị thế, bắt đầu từ các yếu tố kích hoạt mở và đóng. Ví dụ, chỉ báo Bollinger cổ điển được sử dụng làm điểm mở và đóng của giao dịch chênh lệch giá dương và âm khi chênh lệch giá dao động. Nhờ thiết kế hợp lý và mức độ liên kết thấp, chiến lược này cũng có thể dễ dàng được điều chỉnh thành chiến lược phòng ngừa rủi ro theo chu kỳ bằng chỉ báo Bollinger.
#### Khi xem xét toàn bộ mã, chúng ta có thể kết luận rằng mã này chủ yếu được chia thành bốn phần.
main chức năng.main Hàm này là hàm nhập của chiến lược. Vòng lặp chính được thực hiện trong hàm này. Ngoài ra, hàm này cũng thực hiện một hoạt động quan trọng, đó là truy cập vào giao diện websocket của sàn giao dịch để lấy dữ liệu thị trường tick được đẩy làm dữ liệu thô vật liệu của máy phát dữ liệu dòng K. dữ liệu.#### Bằng cách hiểu toàn bộ mã chiến lược, giờ đây chúng ta có thể phân tích dần từng liên kết để hiểu đầy đủ về thiết kế, ý tưởng và kỹ thuật của chiến lược.
State tuyên bốenum State { // 枚举类型 定义一些 状态
STATE_NA, // 非正常状态
STATE_IDLE, // 空闲
STATE_HOLD_LONG, // 持多仓
STATE_HOLD_SHORT, // 持空仓
};
Bởi vì một số hàm trong mã trả về một trạng thái nhất định, các trạng thái này được xác định trong kiểu liệt kêStateở giữa.
Xem mã xuất hiệnSTATE_NA Nghĩa là đó là trạng thái bất thường.STATE_IDLE Nó ở trạng thái nhàn rỗi, nghĩa là trạng thái mà các hoạt động phòng ngừa rủi ro có thể được thực hiện.STATE_HOLD_LONG Trạng thái nắm giữ vị thế phòng ngừa tích cực.STATE_HOLD_SHORT Trạng thái nắm giữ vị thế phòng ngừa rủi ro.
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;
};
Lớp này chủ yếu chịu trách nhiệm xử lý dữ liệu tích tắc thu được thành đường K chênh lệch giá để thúc đẩy logic phòng ngừa chiến lược. Một số độc giả có thể thắc mắc tại sao chúng ta cần sử dụng dữ liệu tích tắc? Tại sao chúng ta cần xây dựng một trình tạo dữ liệu K-line như vậy? Sử dụng trực tiếp dữ liệu K-line có phải là tốt hơn không? Ba câu hỏi này nảy sinh trong đầu tôi khi tôi đang viết một số chiến lược phòng ngừa rủi ro. Tôi đã tìm ra câu trả lời khi viết chiến lược phòng ngừa rủi ro Bollinger. Bởi vì dữ liệu K-line của một hợp đồng đơn lẻ là số liệu thống kê về thay đổi giá của hợp đồng này trong một khoảng thời gian nhất định. Dữ liệu K-line về chênh lệch giá giữa hai hợp đồng là số liệu thống kê về chênh lệch giá trong một khoảng thời gian nhất định. Do đó, chúng ta không thể chỉ lấy dữ liệu K-line của hai hợp đồng để trừ và tính toán chênh lệch của từng dữ liệu trên mỗi Thanh K-line. Giá trị, là chênh lệch giá. Sai lầm dễ thấy nhất là giá cao nhất và giá thấp nhất của hai hợp đồng không nhất thiết phải ở cùng một thời điểm. Do đó, giá trị bị trừ không có nhiều ý nghĩa. Do đó, chúng ta cần sử dụng dữ liệu tích tắc theo thời gian thực, tính toán chênh lệch giá theo thời gian thực và thống kê về những thay đổi giá trong một khoảng thời gian nhất định theo thời gian thực (tức là giá mở cửa cao và giá đóng cửa thấp trên cột K-line). Theo cách này, chúng ta cần một trình tạo dữ liệu K-line như một lớp riêng biệt để phân tách logic xử lý.
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(""); // 图表对象,并初始化
};
Vì mã khá dài nên một phần của nó bị lược bỏ. Nó chủ yếu hiển thị cấu trúc của lớp hedge. Hàm tạo Hedge không được đề cập đến vì nó chủ yếu dùng để khởi tạo đối tượng. Về cơ bản còn lại hai chức năng.
Chức năng này chủ yếu xử lý việc phát hiện lệnh, hủy lệnh, phát hiện vị thế, cân bằng vị thế, v.v. Bởi vì trong quá trình giao dịch phòng ngừa rủi ro, tình huống một chân (tức là một hợp đồng được giao dịch và hợp đồng kia thì không) là không thể tránh khỏi. Nếu phát hiện ra trong logic lệnh và sau đó lệnh tiếp theo hoặc đóng vị thế được xử lý , logic chiến lược sẽ hỗn loạn. Vì vậy, chúng tôi đã áp dụng một cách tiếp cận khác khi thiết kế phần này. Nếu hoạt động phòng ngừa rủi ro được kích hoạt, một lệnh sẽ được đặt một lần. Bất kể có xảy ra phòng ngừa rủi ro một chân hay không, hoạt động phòng ngừa rủi ro được coi là thành công theo mặc định. Sau đó, số dư vị thế được kiểm tra trong hàm getState và logic để kiểm tra và xử lý cân bằng được tách ra.
Logic giao dịch của chiến lược được gói gọn trong hàm này, trong đó lệnh gọigetState , sử dụng đối tượng tạo dữ liệu K-line để tạo dữ liệu K-line về chênh lệch giá và đưa ra phán đoán về logic phòng ngừa rủi ro khi mở, đóng và thêm vị thế. Ngoài ra còn có một số thao tác cập nhật dữ liệu cho biểu đồ.
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接口 推送来的数据
...
}
Sau khi chiến lược được khởi động, nó bắt đầu thực thi từ hàm chính. Trong quá trình khởi tạo hàm chính, chiến lược đăng ký vào thị trường tick của giao diện websocket. Nhiệm vụ chính của hàm main là xây dựng một vòng lặp chính, liên tục nhận thông tin tích tắc được đẩy bởi giao diện websocket của sàn giao dịch, sau đó gọi hàm thành viên của đối tượng lớp hedge: hàm Loop. Logic giao dịch trong hàm Loop được điều khiển bởi dữ liệu thị trường. Một điều cần giải thích là thị trường tích tắc được đề cập ở trên thực chất là giao diện dữ liệu độ sâu sổ lệnh đã đăng ký, nơi thu thập dữ liệu sổ lệnh của từng cấp độ. Tuy nhiên, chiến lược này chỉ sử dụng dữ liệu từ cấp độ đầu tiên, thực tế tương tự như dữ liệu thị trường tick. Chiến lược này không sử dụng dữ liệu từ các cấp độ khác, cũng không sử dụng giá trị khối lượng lệnh từ cấp độ đầu tiên. Chúng ta hãy xem xét kỹ hơn cách chiến lược đăng ký dữ liệu của giao diện websocket và cách nó được thiết lập.
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");
Đầu tiên, bạn cần mã hóa URL tham số json của tin nhắn đăng ký được truyền bởi giao diện đăng ký, nghĩa là,payload Giá trị của tham số. Sau đó, bước quan trọng hơn là gọi hàm giao diện API của Nền tảng giao dịch định lượng InventorDial chức năng.Dial Chức năng này có thể được sử dụng để truy cập vào giao diện websocket trao đổi. Chúng tôi thực hiện một số thiết lập ở đây để cho phép đối tượng điều khiển kết nối websocket ws được tạo để tự động kết nối lại sau khi ngắt kết nối (tin nhắn đăng ký vẫn sử dụng chuỗi giá trị qs của tham số tải trọng). Để đạt được chức năng này, bạn cầnDial Thêm tùy chọn cấu hình vào chuỗi tham số của hàm.
DialCác tham số của hàm bắt đầu như sau:
wss://real.okex.com:10442/ws/v3
Là địa chỉ giao diện websocket cần được truy cập và sau đó sử dụng| Tách biệt.
compress=gzip_raw&mode=recv&reconnect=true&payload="+qs Đây đều là các thông số cấu hình.
| Tên tham số | Mô tả |
|---|---|
| compress | compress là phương pháp nén. Giao diện websocket OKEX sử dụng gzip_raw, do đó nó được đặt thành gzip_raw |
| mode | mode là chế độ và các tùy chọn là kép, gửi và nhận. kép có nghĩa là hai chiều, gửi dữ liệu nén và nhận dữ liệu nén. gửi là gửi dữ liệu đã nén. recv nhận dữ liệu đã nén và giải nén cục bộ. |
| reconnect | reconnect là liệu có nên thiết lập kết nối lại hay không. reconnect=true cho phép kết nối lại. Nếu không được thiết lập, kết nối lại sẽ bị tắt theo mặc định. |
| payload | Payload là tin nhắn đăng ký cần được gửi khi ws kết nối lại. |
Sau khi thiết lập này, ngay cả khi kết nối websocket bị ngắt kết nối, hệ thống cơ sở của đơn vị lưu ký Nền tảng giao dịch định lượng Inventor sẽ tự động kết nối lại và lấy dữ liệu thị trường mới nhất một cách kịp thời. Nắm bắt mọi biến động giá và nhanh chóng nắm bắt thị trường phòng ngừa rủi ro thích hợp.
Kiểm soát vị thế sử dụng tỷ lệ vị thế phòng ngừa tương tự như chuỗi “Porfinacci” để kiểm soát.
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数据结构中。
}
Có thể thấy rằng số vị trí được thêm vào mỗi lần là tổng của hai vị trí gần nhất. Kiểm soát vị thế như vậy có thể đạt được mức chênh lệch giá càng lớn thì lượng phòng ngừa chênh lệch giá tương đối lớn hơn và các vị thế có thể được phân tán, để nắm bắt được những biến động chênh lệch giá nhỏ với các vị thế nhỏ và tăng các vị thế một cách thích hợp với chênh lệch giá lớn. biến động.
Chênh lệch chốt lời và chênh lệch dừng lỗ cố định. Khi chênh lệch giá vị thế đạt đến vị thế chốt lời hoặc vị thế dừng lỗ, lệnh chốt lời hoặc dừng lỗ sẽ được thực hiện.
Khoảng thời gian được kiểm soát bởi tham số NPeriod cung cấp một mức độ kiểm soát động nhất định đối với việc mở và đóng các vị thế của chiến lược.
Chiến lược này tự động tạo biểu đồ nến chênh lệch giá và đánh dấu thông tin giao dịch có liên quan.
Hoạt động vẽ biểu đồ tùy chỉnh chiến lược C++ cũng rất đơn giản. Bạn có thể thấy rằng trong hàm tạo của lớp hedge, chúng ta sử dụng chuỗi cấu hình biểu đồ đã viết _cfgStr để cấu hình đối tượng biểu đồ _c._c là lớp hedge. Khi thành viên riêng được khởi tạo, hàm giao diện API biểu đồ tùy chỉnh định lượng của nhà phát minh sẽ được gọi.Chart Đối tượng biểu đồ được xây dựng bởi hàm.
_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); sử dụng _cfgStr Cấu hình cho đối tượng biểu đồ._c.reset(); Đặt lại dữ liệu biểu đồ.Khi mã chiến lược cần chèn dữ liệu vào biểu đồ, nó cũng được gọi trực tiếp_hàm thành viên của đối tượng c, hoặc_Tham chiếu của c được truyền dưới dạng tham số, sau đó hàm thành viên đối tượng (phương thức) của _c được gọi để thực hiện các hoạt động cập nhật và chèn dữ liệu biểu đồ. Ví dụ:
_c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}});
Sau khi đặt lệnh, hãy đánh dấu lệnh đó trên biểu đồ nến.
Như được hiển thị bên dưới, việc vẽ đường K được thực hiện bằng cách gọi hàm thành viên của lớp BarFeederfeed Khi biểu đồ đối tượng_Tham chiếu tới c được truyền dưới dạng tham số.
void feed(double price, Chart *c=nullptr, int chartIdx=0)
Ngay lập tứcfeedTham số c của hàm.
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)。
}
}
Bằng cách gọi đối tượng biểu đồ_caddHàm thành viên, chèn dữ liệu thanh K-line mới vào biểu đồ.
Mã số:c->add(chartIdx, point);



Chiến lược này chỉ để học tập và giao lưu, khi sử dụng trong giao dịch thực tế, vui lòng sửa đổi và tối ưu hóa theo tình hình thực tế của giao dịch.
Địa chỉ chiến lược: https://www.fmz.com/strategy/163447
Để biết thêm các chiến lược thú vị hơn, vui lòng truy cập “Nền tảng giao dịch định lượng Inventor”: https://www.fmz.com