
Trong bài viết trước, chúng ta đã triển khai một chiến lược phòng ngừa rủi ro đơn giản. Tiếp theo, chúng ta sẽ tìm hiểu cách nâng cấp chiến lược này. Những thay đổi về chiến lược sẽ không lớn, nhưng các chi tiết của những thay đổi này cần được chú ý. Định nghĩa của một số phần trong mã đã thay đổi so với trước và cần phải được hiểu rõ.
A交易所->B交易所,B交易所->A交易所Vẽ đường ngang kích hoạt sự lan truyền. Chúng tôi sử dụng trực tiếp画线类库Ưu điểm là nó đơn giản và dễ sử dụng. Ở đây chúng ta cũng sẽ học cách sử dụng FMZ模版类库Chức năng.Tiếp theo, chúng ta hãy triển khai từng thiết kế một.
Lấy giao dịch giao ngay Binance làm ví dụ, chuyển sang chế độ đòn bẩy giao ngay bằng cách sử dụng mãexchanges[i].IO, truyền vào tham sốtrade_normalChuyển sang vị trí đòn bẩy theo vị trí và chuyển vàotrade_super_marginChuyển sang vị thế đòn bẩy toàn phần, không hỗ trợ kiểm tra ngược. Điều này chỉ được sử dụng trong giao dịch thực tế.
hiện hữumainThêm giai đoạn chuẩn bị sau vào đầu hàm:
// 切换杠杆模式
for (var i = 0 ; i < exchanges.length ; i++) { // 遍历检测所有添加的交易所对象
if (exchanges[i].GetName() == "Binance" && marginType != 0) { // 如果当前i索引代表的交易所对象是币安现货,并且策略界面参数marginType选择的不是「普通币币」选项,执行切换
if (marginType == 1) {
Log(exchanges[i].GetName(), "设置为杠杆逐仓")
exchanges[i].IO("trade_normal")
} else if (marginType == 2) {
Log(exchanges[i].GetName(), "设置为杠杆全仓")
exchanges[i].IO("trade_super_margin")
}
}
}
Chiến lược ở đây chỉ thêm mã để chuyển đổi chế độ đòn bẩy coin-to-coin cho Binance spot, do đó, công tắc được thiết lập trong các tham số chiến lược chỉ có hiệu lực đối với Binance spot.
Rất đơn giản để sử dụng các mẫu bản vẽ đóng gói. Mẫu chúng tôi sử dụng được đặt tên là画线类库. Bạn có thể tìm kiếm và tải trực tiếp trên nền tảng chiến lược FMZ.

Hoặc nhấp trực tiếp vào liên kết: https://www.fmz.com/strategy/27293 để chuyển đến trang sao chép của mẫu này.

Nhấp vào nút để sao chép thư viện mẫu này vào thư viện chiến lược của riêng bạn.

Sau đó, trên trang chỉnh sửa chính sách, bạn có thể kiểm tra thư viện mẫu bạn cần trong cột mẫu. Sau khi chọn hộp và lưu chính sách, chính sách này sẽ tham chiếu đến mẫu này. Ở đây chúng tôi chỉ giải thích ngắn gọn về cách sử dụng thư viện mẫu. Chiến lược này đã tham chiếu đến mẫu này nên không cần phải lặp lại thao tác. Sau khi bạn sao chép chiến lược này vào Strategy Square, bạn có thể thấy nó trong cột mẫu của trang chỉnh sửa chiến lược.画线类库Đã được trích dẫn.
Chúng tôi chủ yếu học cách sử dụng画线类库chức năng vẽ đồ thị.

Chúng tôi đang có kế hoạchA->BSự chênh lệch giá,B->AĐường chênh lệch giá và đường kích hoạt chênh lệch giá được vẽ. Bạn cần vẽ hai đường cong (chênh lệch giá hiện tại từ A đến B và từ B đến A) và hai đường ngang (đường chênh lệch giá kích hoạt), như thể hiện trong hình trên.
Bởi vì chúng ta cần thiết kế một hàng rào đơn phương,A->BVàB->AĐường kích hoạt thì khác nhau. Thiết kế ở bài viết trước không thể sử dụng được nữa.
Trong bài viết trước:
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Chỉ có một sự lây lan kích hoạttargetDiffPrice。
Vì vậy, ở đây chúng ta cần sửa đổi mã, trước tiên hãy sửa đổi các tham số.

Sau đó sửa đổi mã:
var targetDiffPriceA2B = hedgeDiffPriceA2B
var targetDiffPriceB2A = hedgeDiffPriceB2A
if (diffAsPercentage) {
targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageA2B
targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageB2A
}
Theo cách này, đường kích hoạt chênh lệch giá thay đổi so với trước đótargetDiffPriceMột, trở thành haitargetDiffPriceA2B、targetDiffPriceB2A。
Tiếp theo, bạn có thể sử dụng chức năng vẽ đường của thư viện vẽ đường để vẽ dữ liệu này trên biểu đồ.
// 画图
$.PlotHLine(targetDiffPriceA2B, "A->B") // 该函数第一个参数是水平线在Y轴方向上的值,第二个参数是显示文本
$.PlotHLine(targetDiffPriceB2A, "B->A")
Khi chiến lược đang chạy, biểu đồ như thế này sẽ xuất hiện.

Tiếp theo, vẽ đường cong chênh lệch thời gian thực để tránh vẽ quá mức. Đưa mã để vẽ đường cong dữ liệu chênh lệch giá theo thời gian thực vào kiểm tra số dư.
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
$.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price) // 画实时差价曲线
$.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) // 第一个参数是曲线名称,第二个参数是曲线当前时刻的值,即当前时刻Y轴方向上的值
}
Theo cách này, chỉ với 4 dòng mã vẽ, chiến lược có thể hiển thị biểu đồ khi chạy.
Như đã đề cập ở trên, đường kích hoạt chênh lệch giá đã được chuyển đổi thành hai, kiểm soátA->BHàng rào kích hoạtB->AHàng rào đã được kích hoạt. Theo cách này, thuật toán giá lệnh trước đó không thể được sử dụng và thay vào đó, phương pháp giá thị trường cộng với trượt giá được sử dụng.
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
var priceSell = depthA.Bids[0].Price - slidePrice
var priceBuy = depthB.Asks[0].Price + slidePrice
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount)
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // 提示信息
hedge(exB, exA, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
var priceBuy = depthA.Asks[0].Price + slidePrice
var priceSell = depthB.Bids[0].Price - slidePrice
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount)
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Vì giá mua và giá bán được tách thành hai dữ liệu nên hàm phòng ngừahedgeNó cũng cần phải được sửa đổi.
function hedge(buyEx, sellEx, priceBuy, priceSell, amount) {
var buyRoutine = buyEx.Go("Buy", priceBuy, amount)
var sellRoutine = sellEx.Go("Sell", priceSell, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Ngoài ra còn có một số điều chỉnh nhỏ dựa trên những thay đổi này, tôi sẽ không đề cập ở đây. Bạn có thể xem mã để biết chi tiết.
Thêm tính tương tác vào chiến lược để chiến lược có thể sửa đổi đường kích hoạt chênh lệch giá theo thời gian thực. Đây là yêu cầu thiết kế cho chiến lược bán tự động, cũng được triển khai ở đây như một bài trình diễn giảng dạy. Thiết kế tương tác chiến lược cũng rất đơn giản. Đầu tiên, thêm các điều khiển tương tác vào chiến lược trên trang chỉnh sửa chiến lược.

Có thêm hai điều khiển, một được gọi là A2B và điều khiển còn lại được gọi là B2A. Sau khi nhập giá trị vào hộp nhập điều khiển, hãy nhấp vào nút ở bên phải hộp nhập. Một hướng dẫn sẽ được gửi đến chiến lược ngay lập tức, ví dụ: nhập giá trị vào hộp nhập123, nhấp chuộtA2BNút này sẽ ngay lập tức gửi hướng dẫn đến chiến lược.
A2B:123
Thiết kế mã phát hiện và xử lý tương tác trong mã chiến lược.
// 交互
var cmd = GetCommand() // 每次循环执行到这里时,都检测有没有交互指令过来,没有则返回空字符串
if (cmd) { // 检测到有交互指令,例如:A2B:123
Log("接收到命令:", cmd)
var arr = cmd.split(":") // 拆分出交互控件名称和输入框中的值,arr[0]就是A2B,arr[1]就是123
if (arr[0] == "A2B") { // 判断触发的交互控件是不是A2B
Log("修改A2B的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageB2A = parseFloat(arr[1]) // 修改触发差价线
} else {
hedgeDiffPriceA2B = parseFloat(arr[1]) // 修改触发差价线
}
} else if (arr[0] == "B2A") { // 检测到触发的控件是B2A
Log("修改B2A的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageA2B = parseFloat(arr[1])
} else {
hedgeDiffPriceB2A = parseFloat(arr[1])
}
}
}
Làm cho dữ liệu trên thanh trạng thái được sắp xếp hợp lý hơn và dễ quan sát hơn.
var tbl = {
"type" : "table",
"title" : "数据",
"cols" : ["交易所", "币", "冻结币", "计价币", "冻结计价币", "触发差价", "当前差价"],
"rows" : [],
}
tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)])
tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)])
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")

Backtesting chỉ là một bài kiểm tra chiến lược và phát hiện sơ bộ các chức năng. Nhiều lỗi thực sự có thể được kiểm tra trong giai đoạn backtesting. Không cần phải quá chú ý đến kết quả kiểm tra ngược. Chiến lược cuối cùng vẫn cần được kiểm tra trong môi trường thực tế.


Mã nguồn chiến lược: https://www.fmz.com/strategy/302834