Chiến lược phòng ngừa rủi ro là một chiến lược thực hành rất tốt cho người mới bắt đầu trong thiết kế chiến lược.
Trước hết, rõ ràng rằng chiến lược được thiết kế là một chiến lược phòng hộ tại chỗ tiền điện tử. Chúng tôi thiết kế chiến lược phòng hộ đơn giản nhất. Chúng tôi bán trên sàn giao dịch với giá cao hơn chỉ giữa hai sàn giao dịch tại chỗ, và mua trên sàn giao dịch với giá thấp hơn để lấy sự khác biệt. Khi các sàn giao dịch có giá cao hơn đều là đồng xu danh nghĩa (vì các đồng xu có giá cao hơn được bán), và các sàn giao dịch có giá thấp hơn là tất cả các đồng xu (những đồng xu có giá thấp hơn được mua), nó không thể được bảo hiểm. Tại thời điểm này, chúng tôi chỉ có thể chờ đợi sự đảo ngược giá để bảo hiểm.
Khi phòng ngừa rủi ro, giá và số lượng của lệnh được giới hạn bởi sàn giao dịch, và cũng có giới hạn về số lượng đơn đặt hàng tối thiểu. Ngoài giới hạn tối thiểu, chiến lược phòng ngừa rủi ro cũng cần xem xét khối lượng đơn đặt hàng tối đa cùng một lúc. Nếu khối lượng đơn đặt hàng quá lớn, sẽ không có khối lượng đơn đặt hàng đủ. Cũng cần phải xem xét cách chuyển đổi tỷ giá hối đoái nếu hai đồng tiền đổi tiền khác nhau. Khi phòng ngừa rủi ro, phí xử lý và trượt của người nhận đơn đặt hàng là tất cả chi phí giao dịch, không phải miễn là có sự khác biệt giá có thể được bảo hiểm. Do đó, sự khác biệt giá bảo hiểm cũng có giá trị kích hoạt. Nếu nó thấp hơn một sự khác biệt giá nhất định, việc bảo hiểm rủi ro sẽ mất.
Dựa trên những cân nhắc này, chiến lược cần phải được thiết kế với một số thông số:
hedgeDiffPrice
, khi chênh lệch vượt quá giá trị này, hoạt động phòng ngừa rủi ro được kích hoạt.minHedgeAmount
, số tiền đặt hàng tối thiểu (tài tiền) có thể được bảo hiểm.maxHedgeAmount
, số tiền đặt hàng tối đa (tiền xu) cho một lần phòng ngừa rủi ro.pricePrecisionA
, độ chính xác giá lệnh (số vị trí thập phân) được đặt bởi Exchange A.amountPrecisionA
, số tiền chính xác của lệnh được đặt bởi Exchange A (số vị trí thập phân).pricePrecisionB
, độ chính xác giá lệnh (số vị trí thập phân) được đặt bởi Exchange B.amountPrecisionB
, số tiền chính xác của lệnh được đặt bởi Exchange B (số vị trí thập phân).rateA
, chuyển đổi tỷ giá hối đoái của đối tượng hối đoái đầu tiên được thêm vào, mặc định là 1, không chuyển đổi.rateB
, chuyển đổi tỷ giá hối đoái của đối tượng hối đoái thứ hai được thêm vào, mặc định là 1, không chuyển đổi.Chiến lược phòng ngừa rủi ro cần giữ số lượng tiền xu trong hai tài khoản không thay đổi (tức là không giữ vị trí theo bất kỳ hướng nào và duy trì tính trung lập), vì vậy cần có một logic cân bằng trong chiến lược để luôn phát hiện sự cân bằng. Khi kiểm tra sự cân bằng, không thể tránh được việc lấy dữ liệu tài sản từ hai sàn giao dịch. Chúng ta cần viết một hàm để sử dụng.
function updateAccs(arrEx) {
var ret = []
for (var i = 0 ; i < arrEx.length ; i++) {
var acc = arrEx[i].GetAccount()
if (!acc) {
return null
}
ret.push(acc)
}
return ret
}
Sau khi đặt lệnh, nếu không có lệnh hoàn thành, chúng tôi cần phải hủy nó kịp thời, và lệnh không thể được giữ chờ.
function cancelAll() {
_.each(exchanges, function(ex) {
while (true) {
var orders = _C(ex.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
ex.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
}
})
}
Khi cân bằng số tiền xu, chúng ta cần tìm giá tích lũy cho một số tiền xu nhất định trong một dữ liệu độ sâu nhất định, vì vậy chúng ta cần một chức năng như vậy để xử lý nó.
function getDepthPrice(depth, side, amount) {
var arr = depth[side]
var sum = 0
var price = null
for (var i = 0 ; i < arr.length ; i++) {
var ele = arr[i]
sum += ele.Amount
if (sum >= amount) {
price = ele.Price
break
}
}
return price
}
Sau đó, chúng ta cần thiết kế và viết các hoạt động lệnh bảo hiểm cụ thể, mà cần phải được thiết kế để đặt lệnh đồng thời:
function hedge(buyEx, sellEx, price, amount) {
var buyRoutine = buyEx.Go("Buy", price, amount)
var sellRoutine = sellEx.Go("Sell", price, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Cuối cùng, hãy hoàn thành thiết kế của hàm cân bằng, một chút phức tạp.
function keepBalance(initAccs, nowAccs, depths) {
var initSumStocks = 0
var nowSumStocks = 0
_.each(initAccs, function(acc) {
initSumStocks += acc.Stocks + acc.FrozenStocks
})
_.each(nowAccs, function(acc) {
nowSumStocks += acc.Stocks + acc.FrozenStocks
})
var diff = nowSumStocks - initSumStocks
// Calculate the currency difference
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
var index = -1
var available = []
var side = diff > 0 ? "Bids" : "Asks"
for (var i = 0 ; i < nowAccs.length ; i++) {
var price = getDepthPrice(depths[i], side, Math.abs(diff))
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
available.push(i)
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
available.push(i)
}
}
for (var i = 0 ; i < available.length ; i++) {
if (index == -1) {
index = available[i]
} else {
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
index = available[i]
} else if (priceIndex && priceI && priceI < priceIndex) {
index = available[i]
}
}
}
if (index == -1) {
Log("unable to balance")
} else {
// balance order
var price = getDepthPrice(depths[index], side, Math.abs(diff))
if (price) {
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
tradeFunc(price, Math.abs(diff))
} else {
Log("invalid price", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("errors:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Sau khi thiết kế các chức năng này theo các yêu cầu chiến lược, sau đó bắt đầu thiết kế chức năng chính của chiến lược.
Trên nền tảng FMZ, chiến lược được thực hiện từmain
Các chức năngmain
chức năng, chúng ta phải làm một số công việc khởi tạo của chiến lược.
Tên đối tượng trao đổi Vì nhiều hoạt động trong chiến lược phải sử dụng các đối tượng trao đổi, chẳng hạn như nhận báo giá thị trường, đặt đơn đặt hàng v.v. Vì vậy, sẽ khó khăn khi sử dụng tên dài mỗi lần, mẹo là sử dụng tên đơn giản thay thế, ví dụ:
var exA = exchanges[0]
var exB = exchanges[1]
Điều này làm cho nó dễ dàng hơn để viết mã sau này.
Giá hối đoái, thiết kế liên quan đến độ chính xác
// precision, exchange rate settings
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Exchange A sets the exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Exchange B sets the exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Nếu các thông số tỷ giá hối đoáirateA
, rateB
được thiết lập là 1 (bên mặc định là 1), tức là,rateA != 1
hoặcrateB != 1
sẽ không kích hoạt, do đó chuyển đổi tỷ giá hối đoái sẽ không được thiết lập.
Lập lại tất cả dữ liệu
Đôi khi nó là cần thiết để xóa tất cả các nhật ký và xóa các dữ liệu được ghi lại khi chiến lược bắt đầu.isReset
, và thiết kế mã thiết lập lại trong phần khởi tạo của chiến lược, ví dụ:
if (isReset) { // When isReset is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#FF0000")
}
Khôi phục dữ liệu tài khoản ban đầu, cập nhật dữ liệu tài khoản hiện tại
Để đánh giá số dư, chiến lược cần phải ghi lại liên tục các tài sản tài khoản ban đầu để so sánh với tài sản hiện tại.nowAccs
được sử dụng để ghi lại các dữ liệu tài khoản vãng lai, sử dụng các chức năng chúng tôi vừa thiết kếupdateAccs
để có được dữ liệu tài khoản của sàn giao dịch hiện tại.initAccs
được sử dụng để ghi lại tình trạng tài khoản ban đầu (số lượng tiền xu, số lượng tiền xu được mệnh giá, v.v. trên sàn giao dịch A và B).initAccs
, sử dụng_G()
chức năng để khôi phục đầu tiên (công thức _G sẽ ghi lại dữ liệu liên tục, và có thể trả lại dữ liệu được ghi lại một lần nữa, xem tài liệu API để biết chi tiết: [link](https://www.fmz.com/api#_gk-v)), nếu truy vấn không hoạt động, sử dụng thông tin tài khoản vãng lai để gán giá trị và sử dụng_G
chức năng để ghi lại.
Ví dụ như mã sau:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Mã trong vòng lặp chính là quá trình của mỗi vòng thực thi logic chiến lược, được thực hiện nhiều lần để tạo thành vòng lặp chính của chiến lược.
Thu thập dữ liệu thị trường và đánh giá tính hợp lệ của dữ liệu thị trường
var ts = new Date().getTime()
var depthARoutine = exA.Go("GetDepth")
var depthBRoutine = exB.Go("GetDepth")
var depthA = depthARoutine.wait()
var depthB = depthBRoutine.wait()
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
Sleep(500)
continue
}
Ở đây chúng ta có thể thấy rằng hàm đồng thờiexchange.Go
của nền tảng FMZ được sử dụng để tạo các đối tượng đồng thờidepthARoutine
, depthBRoutine
mà gọi làGetDepth()
Khi hai đối tượng đồng thời này được tạo ra,GetDepth()
giao diện được gọi ngay lập tức, và cả hai yêu cầu về dữ liệu độ sâu được gửi đến trao đổi.
Vậy gọi chowait()
phương phápdepthARoutine
, depthBRoutine
đối tượng để thu thập dữ liệu độ sâu.
Sau khi thu thập dữ liệu độ sâu, cần phải kiểm tra dữ liệu độ sâu để xác định tính hợp lệ của nó.continue
lệnh được kích hoạt để thực hiện lại vòng lặp chính.
Sử dụngspread value
tham số hoặcspread ratio
Địa chỉ?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Về các thông số, chúng tôi đã thực hiện một thiết kế như vậy: các thông số của FMZ có thể đượcbiểu diễnhoặcẩndựa trên một tham số, vì vậy chúng ta có thể làm cho một tham số để quyết định xem có nên sử dụngprice spread
, hoặcspread ratio
.
Một tham sốdiffAsPercentage
đã được thêm vào các thông số của giao diện chiến lược. Hai cài đặt thông số khác để hiển thị hoặc ẩn dựa trên thông số này là:hedgeDiffPrice@!diffAsPercentage
, được hiển thị khidiffAsPercentage
là sai.hedgeDiffPercentage@diffAsPercentage
, được hiển thị khidiffAsPercentage
là sự thật.
Sau khi thiết kế này, chúng tôi kiểm tradiffAsPercentage
Điều kiện kích hoạt phòng hộ dựa trên tỷ lệ chênh lệch giá.diffAsPercentage
tham số được kiểm tra, phòng ngừa rủi ro được kích hoạt bởi sự khác biệt giá.
Xác định các điều kiện kích hoạt phòng ngừa rủi ro
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market conditions are met
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
Log("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // Tips
hedge(exB, exA, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A market conditions are met
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
Log("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // Tips
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Các điều kiện kích hoạt phòng ngừa rủi ro là như sau:
isTrade
Ở đây, nếu việc bảo hiểm được kích hoạt, biến được đặt thànhtrue
. Và đặt lại biến toàn cầulastKeepBalanceTS
đến 0 (lastKeepBalanceTS được sử dụng để đánh dấu dấu thời gian của hoạt động cân bằng cuối cùng, đặt nó thành 0 sẽ kích hoạt hoạt động cân bằng ngay lập tức), và sau đó hủy tất cả các lệnh đang chờ.Hoạt động cân bằng
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
}
}
}
Có thể thấy rằng chức năng cân bằng được thực hiện định kỳ, nhưng nếulastKeepBalanceTS
được đặt lại 0 sau khi hoạt động phòng ngừa được kích hoạt, hoạt động cân bằng sẽ được kích hoạt ngay lập tức. Lợi nhuận sẽ được tính sau khi cân bằng thành công.
Thông tin thanh trạng thái
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
"current A, Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"current B, Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initial A, Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initial B, Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Các thanh trạng thái không đặc biệt phức tạp trong thiết kế. Nó hiển thị thời gian hiện tại, sự khác biệt giá từ Sàn giao dịch A đến Sàn giao dịch B và sự khác biệt giá từ Sàn giao dịch B đến Sàn giao dịch A. Và nó hiển thị hiện tại phòng hộ mục tiêu chênh lệch, dữ liệu tài sản của sàn giao dịch A tài khoản và sàn giao dịch B tài khoản.
Về các tham số, chúng tôi thiết kế tham số giá trị tỷ giá chuyển đổi, và chúng tôi cũng thiết kế tỷ giá chuyển đổi trong hoạt động ban đầu củamain
Nên lưu ý rằngSetRate
Chức năng chuyển đổi tỷ giá hối đoái cần được thực hiện trước tiên.
Bởi vì chức năng này ảnh hưởng đến hai khía cạnh:
BTC_USDT
, đơn vị giá làUSDT
, và số tiền có sẵn trong tài sản tài khoản cũng làUSDT
Nếu tôi muốn chuyển đổi giá trị thành CNY, đặtexchange.SetRate(6.8)
trong mã để chuyển đổi dữ liệu thu được bởi tất cả các chức năng theoexchange
đổi đối tượng sang CNY.
Để chuyển đổi sang loại tiền tệ nào, chuyển vàotỷ giá hối đoái từ đồng tiền hiện tại thành đồng tiền mục tiêuđếnSetRate
function.Chiến lược hoàn chỉnh:Chiến lược phòng ngừa rủi ro tại chỗ của các loại tiền tệ khác nhau (Hướng dẫn)