最近FMZ官方群里讨论马丁类型的策略比较多,平台上关于数字货币合约的马丁策略不多。所以,借此机会设计了一个简单的数字货币期货类马丁策略。为什么说是类马丁策略,因为马丁策略潜在风险确实不小,就没有完全按照马丁策略去设计。但是这类策略依然是有不小的风险的,并且马丁类型的策略参数设置和风险息息相关,对于风险是千万不能忽视的。
本篇文章主要从马丁类型策略的设计上讲解学习,策略思路本身已经很明了,作为FMZ的使用者我们更多考虑策略设计。
在设计数字货币期货策略时,经常要用到总权益这个数据。因为要计算收益,特别是需要计算浮动收益时。由于持仓占用保证金,挂单也占用。这个时候调用FMZ平台的API接口exchange.GetAccount()
获取的是可用资产和挂单冻结资产。其实大部分数字货币期货交易所都提供了总权益这个数据,只不过FMZ上没有统一封装这个属性。
所以我们根据不同的交易所分别设计函数获取这个数据:
// 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
}
代码中totalEquity
就是我们需要的总权益。然后我们再写个函数作为调用入口,根据交易所名称去具体调用对应的函数。
function getTotalEquity() {
var exName = exchange.GetName()
if (exName == "Futures_OKCoin") {
return getTotalEquity_OKEX_V5()
} else if (exName == "Futures_Binance") {
return getTotalEquity_Binance()
} else {
throw "不支持该交易所"
}
}
在设计主函数、主要逻辑之前。我们还需要做一些准备,设计一些辅助功能的函数。
取消当前所有挂单
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)
}
}
这个函数相信经常看FMZ策略广场上策略范例代码的都很熟悉,很多策略都用过类似的设计。作用就是获取当前挂单列表,然后逐个撤销。
期货的下单操作
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)
}
期货交易有四个方向:开多仓(openLong)、开空仓(openShort)、平多仓(coverLong)、平空仓(coverShort)。所以我们设计了四个下单函数对应这些操作。如果只考虑下单,那么有这样几个必要的因素:方向、下单价格、下单量。
所以我们还设计了一个名为:trade
的函数来处理当方向(distance)
、下单价格(price)
、下单量(amount)
都明确时的操作。
开多仓(openLong)、开空仓(openShort)、平多仓(coverLong)、平空仓(coverShort)这些函数调用最终都由trade
函数完成实际功能,也就是根据既定的方向、价格、数量在期货交易所下单。
策略思路很简单,以当前价格为基线上下一定距离挂卖出(做空)、买入单(做多)。一边成交了就取消剩下的所有订单,然后根据持仓的价格在一定距离挂出新的平仓订单,在更新后的当前价格挂出加仓订单,但是加仓订单不加倍下单量。
初始工作 因为要挂单,所以我们需要两个全局变量记录订单ID。
var buyOrderId = null
var sellOrderId = null
然后策略界面参数里设计了使用OKEX_V5模拟盘的选项,所以代码里要做一些处理:
var exName = exchange.GetName()
// 切换OKEX V5模拟盘
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
界面参数里还设计了重置所有信息的选项,所以代码里也要有对应的处理:
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
我们只跑永续合约,所以这里写死了,只设置为永续合约。
exchange.SetContractType("swap")
然后我们还要考虑到下单价格精度、下单量精度的问题,如果精度不设置好,策略计算过程中精度丢失,数据的小数位很多的话容易引起下单时被交易所接口拒绝。
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("设置精度", pricePrecision, amountPrecision)
设计上简单的数据恢复功能
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
}
}
如果想在策略运行时指定最初账户总权益,可以设置参数totalEq
,如果该参数设置为-1,策略会读取储存的总权益数据,如果没有储存的总权益数据,就是以当前读取的总权益作为策略运行进度的最初总权益,之后总权益增加就说明赚了,总权益少了就说明亏了。如果读取到总权益数据,则使用这个数据继续运行。
主要逻辑 初始工作做完之后,终于来到了策略主要逻辑的部分了,为了方便讲解,我直接把说明写在代码注释上了。
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)
}
整个逻辑和设计就讲解完了。
让策略经历一次5月19日的行情。
可以看到,类马丁策略依然是有一定风险的。
策略地址:https://www.fmz.com/strategy/294957
策略主要用于学习,真金白银慎用 ~!
lisa20231 梦大,想要請問這句 if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了 偵測訂單時,若快速上下插針同時成交了買賣單,那是否會拋出錯誤?
Neo1898 另外就是合约的模式是全仓还是逐仓怎么设定?目前是啥模式呢
Neo1898 既然开的是合约,为啥没有合约倍数的设定呢?买卖都是多少倍的呢
轻轻的云 谢谢梦大,我终于能从头到尾看懂了, 然后学会了挂单监控了,然后写了一个双边的马丁。两天时间,写了580行。。。。。 谢谢梦大[抱拳]
hk量 /upload/asset/1a9ebf427c4e2cbf1c327.png false true 交换下?
梦想身价八位数 如果
梦想身价八位数 所有者权益合计
null 需要止损吗
btcrobot 马丁,回测下了,归0
wqy 开单数量那里没看懂呢= =,那个n永远都等于1
lvdalei 策略是多空双开吗?还是单开的
发明者量化-小小梦 不会抛出错误。依然会取消所有挂单,跳出当前循环,然后继续挂单逻辑。瞬间都成交了就相当于吃到价差利润了。
发明者量化-小小梦 一般要用全仓吧。
发明者量化-小小梦 杠杆可以在交易所具体设置,根据自身风险偏好。
发明者量化-小小梦 云总 666!
发明者量化-小小梦 那这个变量名叫 isFindBuyId 就不合适了吧。 应该叫isNotFindBuyId了。
发明者量化-小小梦 这策略没设计止损。。所以跑出来的曲线可以看到是一路往上的。
发明者量化-小小梦 哈哈,马丁的归宿。 本篇主要是教学策略设计,不用太在意收益。
发明者量化-小小梦 那个N是为了做之后的改动用的,比如想n倍距离加仓,暂时可以定1。
发明者量化-小小梦 单开的。