Type/to search
8
Follow
1359
Followers
数字货币期货类马丁策略设计
Created 2021-07-02 14:35:34  Updated 2023-09-21 21:09:56
 22
 6485

img

数字货币期货类马丁策略设计

最近FMZ官方群里讨论马丁类型的策略比较多,平台上关于数字货币合约的马丁策略不多。所以,借此机会设计了一个简单的数字货币期货类马丁策略。为什么说是类马丁策略,因为马丁策略潜在风险确实不小,就没有完全按照马丁策略去设计。但是这类策略依然是有不小的风险的,并且马丁类型的策略参数设置和风险息息相关,对于风险是千万不能忽视的。

本篇文章主要从马丁类型策略的设计上讲解学习,策略思路本身已经很明了,作为FMZ的使用者我们更多考虑策略设计。

获取总权益

在设计数字货币期货策略时,经常要用到总权益这个数据。因为要计算收益,特别是需要计算浮动收益时。由于持仓占用保证金,挂单也占用。这个时候调用FMZ平台的API接口exchange.GetAccount()获取的是可用资产和挂单冻结资产。其实大部分数字货币期货交易所都提供了总权益这个数据,只不过FMZ上没有统一封装这个属性。

所以我们根据不同的交易所分别设计函数获取这个数据:

javascript
// 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就是我们需要的总权益。然后我们再写个函数作为调用入口,根据交易所名称去具体调用对应的函数。

javascript
function getTotalEquity() { var exName = exchange.GetName() if (exName == "Futures_OKCoin") { return getTotalEquity_OKEX_V5() } else if (exName == "Futures_Binance") { return getTotalEquity_Binance() } else { throw "不支持该交易所" } }

设计一些辅助函数

在设计主函数、主要逻辑之前。我们还需要做一些准备,设计一些辅助功能的函数。

  • 取消当前所有挂单

    javascript
    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策略广场上策略范例代码的都很熟悉,很多策略都用过类似的设计。作用就是获取当前挂单列表,然后逐个撤销。

  • 期货的下单操作

    javascript
    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。

    javascript
    var buyOrderId = null var sellOrderId = null

    然后策略界面参数里设计了使用OKEX_V5模拟盘的选项,所以代码里要做一些处理:

    javascript
    var exName = exchange.GetName() // 切换OKEX V5模拟盘 if (isSimulate && exName == "Futures_OKCoin") { exchange.IO("simulate", true) }

    界面参数里还设计了重置所有信息的选项,所以代码里也要有对应的处理:

    javascript
    if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("重置所有数据", "#FF0000") }

    我们只跑永续合约,所以这里写死了,只设置为永续合约。

    javascript
    exchange.SetContractType("swap")

    然后我们还要考虑到下单价格精度、下单量精度的问题,如果精度不设置好,策略计算过程中精度丢失,数据的小数位很多的话容易引起下单时被交易所接口拒绝。

    javascript
    exchange.SetPrecision(pricePrecision, amountPrecision) Log("设置精度", pricePrecision, amountPrecision)

    设计上简单的数据恢复功能

    javascript
    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,策略会读取储存的总权益数据,如果没有储存的总权益数据,就是以当前读取的总权益作为策略运行进度的最初总权益,之后总权益增加就说明赚了,总权益少了就说明亏了。如果读取到总权益数据,则使用这个数据继续运行。

  • 主要逻辑
    初始工作做完之后,终于来到了策略主要逻辑的部分了,为了方便讲解,我直接把说明写在代码注释上了。

    pine
    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日的行情。

img

img

可以看到,类马丁策略依然是有一定风险的。

实盘可以用OKEX V5模拟盘跑跑玩一下

img

策略地址:https://www.fmz.com/strategy/294957

策略主要用于学习,真金白银慎用 ~!

Related Recommendations
Comment
All comments (20)

    梦大,想要請問這句
    if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了
    偵測訂單時,若快速上下插針同時成交了買賣單,那是否會拋出錯誤?

    4 years ago

    不会抛出错误。依然会取消所有挂单,跳出当前循环,然后继续挂单逻辑。瞬间都成交了就相当于吃到价差利润了。

    4 years ago

    另外就是合约的模式是全仓还是逐仓怎么设定?目前是啥模式呢

    4 years ago

    一般要用全仓吧。

    4 years ago

    杠杆可以在交易所具体设置,根据自身风险偏好。

    4 years ago

    既然开的是合约,为啥没有合约倍数的设定呢?买卖都是多少倍的呢

    4 years ago

    谢谢梦大,我终于能从头到尾看懂了,
    然后学会了挂单监控了,然后写了一个双边的马丁。两天时间,写了580行。。。。。
    谢谢梦大[抱拳]

    5 years ago

    云总 666!

    5 years ago

    img
    false true 交换下?

    5 years ago

    那这个变量名叫 isFindBuyId 就不合适了吧。 应该叫isNotFindBuyId了。

    5 years ago

    如果

    5 years ago

    所有者权益合计

    5 years ago

    需要止损吗

    5 years ago

    这策略没设计止损。。所以跑出来的曲线可以看到是一路往上的。

    5 years ago

    马丁,回测下了,归0

    5 years ago

    哈哈,马丁的归宿。
    本篇主要是教学策略设计,不用太在意收益。

    5 years ago

    开单数量那里没看懂呢= =,那个n永远都等于1

    5 years ago

    那个N是为了做之后的改动用的,比如想n倍距离加仓,暂时可以定1。

    5 years ago

    策略是多空双开吗?还是单开的

    5 years ago

    单开的。

    5 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)