
Gần đây, có nhiều cuộc thảo luận về chiến lược Martingale trong nhóm chính thức của FMZ, nhưng lại không có nhiều chiến lược Martingale cho hợp đồng tiền kỹ thuật số trên nền tảng này. Do đó, tôi đã tận dụng cơ hội này để thiết kế một chiến lược Martingale đơn giản cho hợp đồng tương lai tiền điện tử. Tại sao lại gọi là chiến lược giống Martin? Bởi vì rủi ro tiềm ẩn của chiến lược Martin thực sự không nhỏ, do đó nó không được thiết kế hoàn toàn theo chiến lược Martin. Tuy nhiên, loại chiến lược này vẫn có những rủi ro đáng kể và các thiết lập tham số chiến lược Martingale có liên quan chặt chẽ đến rủi ro, không được bỏ qua rủi ro.
Bài viết này chủ yếu giải thích và học hỏi từ thiết kế chiến lược kiểu Martin. Bản thân ý tưởng chiến lược rất rõ ràng. Là người dùng FMZ, chúng tôi xem xét thiết kế chiến lược nhiều hơn.
Khi thiết kế các chiến lược tương lai tiền kỹ thuật số, dữ liệu tổng vốn chủ sở hữu thường được sử dụng. Bởi vì cần phải tính toán lợi nhuận, đặc biệt là khi cần tính toán lợi nhuận thả nổi. Vì các vị thế mở chiếm ký quỹ nên các lệnh chờ cũng chiếm ký quỹ. Lúc này, hãy gọi giao diện API của nền tảng FMZexchange.GetAccount()Những gì thu được là tài sản có sẵn và tài sản bị đóng băng theo lệnh đang chờ xử lý. Trên thực tế, hầu hết các sàn giao dịch tương lai tiền kỹ thuật số đều cung cấp dữ liệu về tổng vốn chủ sở hữu, nhưng FMZ không bao gồm thống nhất thuộc tính này.
Do đó, chúng tôi thiết kế các chức năng để thu thập dữ liệu này theo các trao đổi khác nhau:
// OKEX V5 获取总权益
function getTotalEquity_OKEX_V5() {
var totalEquity = null
var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT")
if (ret) {
try {
totalEquity = parseFloat(ret.data[0].details[0].eq)
} catch(e) {
Log("获取账户总权益失败!")
return null
}
}
return totalEquity
}
// 币安期货
function getTotalEquity_Binance() {
var totalEquity = null
var ret = exchange.GetAccount()
if (ret) {
try {
totalEquity = parseFloat(ret.Info.totalWalletBalance)
} catch(e) {
Log("获取账户总权益失败!")
return null
}
}
return totalEquity
}
Trong mãtotalEquityĐây chính là tổng số vốn chủ sở hữu mà chúng ta cần. Sau đó, chúng ta viết một hàm như mục gọi và gọi hàm tương ứng theo tên trao đổi.
function getTotalEquity() {
var exName = exchange.GetName()
if (exName == "Futures_OKCoin") {
return getTotalEquity_OKEX_V5()
} else if (exName == "Futures_Binance") {
return getTotalEquity_Binance()
} else {
throw "不支持该交易所"
}
}
Trước khi thiết kế chức năng chính và logic chính. Chúng ta vẫn cần phải chuẩn bị và thiết kế một số chức năng phụ trợ.
function cancelAll() {
while (1) {
var orders = _C(exchange.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
exchange.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
Sleep(500)
}
}
Tôi tin rằng những người thường xem các mã ví dụ chiến lược trên FMZ Strategy Square đều rất quen thuộc với chức năng này. Nhiều chiến lược đã sử dụng các thiết kế tương tự. Chức năng của nó là lấy danh sách các lệnh đang chờ xử lý hiện tại và sau đó hủy từng lệnh một.
function trade(distance, price, amount) {
var tradeFunc = null
if (distance == "buy") {
tradeFunc = exchange.Buy
} else if (distance == "sell") {
tradeFunc = exchange.Sell
} else if (distance == "closebuy") {
tradeFunc = exchange.Sell
} else {
tradeFunc = exchange.Buy
}
exchange.SetDirection(distance)
return tradeFunc(price, amount)
}
function openLong(price, amount) {
return trade("buy", price, amount)
}
function openShort(price, amount) {
return trade("sell", price, amount)
}
function coverLong(price, amount) {
return trade("closebuy", price, amount)
}
function coverShort(price, amount) {
return trade("closesell", price, amount)
}
Có bốn hướng trong giao dịch tương lai: mở vị thế mua (openLong), mở vị thế bán (openShort), đóng vị thế mua (coverLong) và đóng vị thế bán (coverShort). Vì vậy, chúng tôi đã thiết kế bốn hàm thứ tự tương ứng với các phép toán này. Nếu bạn chỉ cân nhắc đến việc đặt hàng, có một số yếu tố cần thiết: hướng, giá đặt hàng và số lượng đặt hàng.
Vì vậy chúng tôi cũng thiết kế một chương trình có tên là:tradeChức năng xử lý方向(distance)、下单价格(price)、下单量(amount)Mọi hoạt động đều rõ ràng.
Các lệnh gọi hàm mở vị thế mua (openLong), mở vị thế bán (openShort), đóng vị thế mua (coverLong) và đóng vị thế bán (coverShort) cuối cùng được thực hiện bởitradeChức năng này thực hiện chức năng thực tế là đặt lệnh trên sàn giao dịch tương lai theo hướng, giá và số lượng đã thiết lập.
Chiến lược này rất đơn giản. Sử dụng giá hiện tại làm cơ sở và đặt lệnh bán (short) và lệnh mua (long) ở một khoảng cách nhất định trên và dưới giá hiện tại. Khi một bên được thực hiện, tất cả các lệnh còn lại sẽ bị hủy và sau đó một lệnh đóng mới sẽ được đặt ở một khoảng cách nhất định dựa trên giá vị thế và một lệnh tăng sẽ được đặt ở mức giá hiện tại đã cập nhật, nhưng lệnh tăng sẽ không tăng gấp đôi số lượng đặt hàng.
var buyOrderId = null
var sellOrderId = null
Sau đó, tùy chọn sử dụng đĩa mô phỏng OKEX_V5 được thiết kế trong các tham số giao diện chiến lược, do đó cần thực hiện một số xử lý trong mã:
var exName = exchange.GetName()
// 切换OKEX V5模拟盘
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
Các tham số giao diện cũng bao gồm tùy chọn để thiết lập lại tất cả thông tin, do đó mã cũng phải có quá trình xử lý tương ứng:
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
Chúng tôi chỉ chạy hợp đồng vĩnh viễn, vì vậy nó được mã hóa cứng ở đây và chỉ được thiết lập thành hợp đồng vĩnh viễn.
exchange.SetContractType("swap")
Sau đó, chúng ta cũng cần xem xét độ chính xác của giá đặt hàng và số lượng đặt hàng. Nếu độ chính xác không được thiết lập đúng cách, độ chính xác sẽ bị mất trong quá trình tính toán chiến lược. Nếu dữ liệu có nhiều chữ số thập phân, thì lệnh dễ bị bị giao diện trao đổi từ chối.
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("设置精度", pricePrecision, amountPrecision)
Chức năng phục hồi dữ liệu đơn giản
if (totalEq == -1 && !IsVirtual()) {
var recoverTotalEq = _G("totalEq")
if (!recoverTotalEq) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
totalEq = currTotalEq
_G("totalEq", currTotalEq)
} else {
throw "获取初始权益失败"
}
} else {
totalEq = recoverTotalEq
}
}
Nếu bạn muốn chỉ định tổng vốn chủ sở hữu ban đầu của tài khoản khi chiến lược được chạy, bạn có thể đặt tham sốtotalEqNếu tham số này được đặt thành -1, chiến lược sẽ đọc dữ liệu vốn chủ sở hữu tổng thể đã lưu trữ. Nếu không có dữ liệu vốn chủ sở hữu tổng thể đã lưu trữ, vốn chủ sở hữu tổng thể đã đọc hiện tại sẽ được sử dụng làm vốn chủ sở hữu tổng thể ban đầu của tiến trình chiến lược đang chạy. Nếu tổng vốn chủ sở hữu tăng, điều đó có nghĩa là nếu bạn kiếm được tiền, nhưng tổng vốn chủ sở hữu của bạn ít hơn, điều đó có nghĩa là bạn đã mất tiền. Nếu dữ liệu tổng vốn chủ sở hữu được đọc, hãy tiếp tục chạy bằng dữ liệu này.
while (1) { // 策略主要逻辑设计为一个死循环
var ticker = _C(exchange.GetTicker) // 首先读取当前行情信息,主要用到最新成交价
var pos = _C(exchange.GetPosition) // 读取当前持仓数据
if (pos.length > 1) { // 判断持仓数据,由于这个策略的逻辑,是不太可能同时出现多空持仓的,所以发现同时出现多空持仓就抛出错误
Log(pos)
throw "同时有多空持仓" // 抛出错误,让策略停止
}
// 根据状态而定
if (pos.length == 0) { // 根据持仓状态做出不同操作,pos.length == 0是当没有持仓时
// 未持仓了,统计一次收益
if (!IsVirtual()) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq)
}
}
buyOrderId = openLong(ticker.Last - targetProfit, amount) // 挂开多仓的买单
sellOrderId = openShort(ticker.Last + targetProfit, amount) // 挂开空仓的卖单
} else if (pos[0].Type == PD_LONG) { // 有多头持仓,挂单位置、数量有所不同
var n = 1
var price = ticker.Last
buyOrderId = openLong(price - targetProfit * n, amount)
sellOrderId = coverLong(pos[0].Price + targetProfit, pos[0].Amount)
} else if (pos[0].Type == PD_SHORT) { // 有空头持仓,挂单位置、数量有所不同
var n = 1
var price = ticker.Last
buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount)
sellOrderId = openShort(price + targetProfit * n, amount)
}
if (!sellOrderId || !buyOrderId) { // 如果有一边挂单失败就取消所有挂单,重来
cancelAll()
buyOrderId = null
sellOrderId = null
continue
}
while (1) { // 挂单完成,开始监控订单
var isFindBuyId = false
var isFindSellId = false
var orders = _C(exchange.GetOrders)
for (var i = 0 ; i < orders.length ; i++) {
if (buyOrderId == orders[i].Id) {
isFindBuyId = true
}
if (sellOrderId == orders[i].Id) {
isFindSellId = true
}
}
if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了
cancelAll()
break
} else if (!isFindBuyId) { // 检测到买单成交
Log("买单成交")
cancelAll()
break
} else if (!isFindSellId) { // 检测到卖单成交
Log("卖单成交")
cancelAll()
break
}
LogStatus(_D())
Sleep(3000)
}
Sleep(500)
}
Toàn bộ logic và thiết kế đã được giải thích.
Hãy để chiến lược trải nghiệm tình hình thị trường vào ngày 19 tháng 5.


Có thể thấy rằng chiến lược Martingale vẫn có những rủi ro nhất định.

Địa chỉ chiến lược: https://www.fmz.com/strategy/294957
Các chiến lược chủ yếu được sử dụng để học, vì vậy hãy thận trọng khi sử dụng tiền thật ~!