Digital currency futures class designed by Martin Strategy

Author: The Little Dream, Created: 2021-07-02 14:35:34, Updated: 2023-09-21 21:09:56

img

Digital currency futures class designed by Martin Strategy

Recently, the FMZ official group has been discussing more Martin-type strategies, and there are not many Martin-type strategies on the platform regarding digital currency contracts. So, I took the opportunity to design a simple digital currency futures Martin-type strategy. Why call it a Martin-type strategy, because the potential risk of the Martin strategy is not small, it is not completely designed according to the Martin strategy.

This article mainly discusses learning from the design of Martin-type strategies, the strategic thinking itself is already clear, and as FMZ users we are more concerned with strategic design.

Access to common interests

This data is often used when designing a digital currency futures strategy. This data is used to calculate returns, especially when calculating floating returns.exchange.GetAccount()Available assets and frozen assets are obtained. In fact, most digital currency futures exchanges provide this data, but FMZ does not uniformly package this attribute.

So we're going to get this data based on the different design functions of the different exchanges:

// 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
}

In the codetotalEquityThis is the total interest we need. Then we write a function as the input to call the corresponding function based on the name of the exchange.

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

Design some auxiliary functions

Before designing the main function, the main logic. We also need to do some preparation to design some auxiliary functions.

  • Remove all current listings

    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)
        }
    }
    

    This function is believed to be familiar to those who often read the policy paradigm code on the FMZ Policy Square, many of which have used a similar design. The function is to retrieve the current list of pending lists and then undo one by one.

  • Subscription of the futures

    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)
    }
    

    There are four directions of futures trading: open long, open short, cover long, and cover short. So we designed four sub-order functions to correspond to these operations. If only sub-orders are considered, there are several necessary factors: direction, sub-order price, sub-order quantity. So we also designed a project called:tradeThe function to handle when方向(distance)下单价格(price)下单量(amount)I'm not sure how to do this. OpenLong, OpenShort, CoverLong, and CoverShort are all called by the same function.tradeThe function performs the actual function, i.e. orders are placed on the futures exchange according to the established direction, price, quantity.

Primary functions

The strategic idea is simple: hang a sale ("do nothing") or a purchase ("do more") at a certain distance below the current price on the baseline. Once the transaction is completed, all remaining orders are canceled, and then a new placement order is hung at a certain distance according to the price held, and an increase order is hung at the current price after the update, but the added order does not double the order size.

  • Early work Since we want to list, we need two global variables to record the order ID.

    var buyOrderId = null
    var sellOrderId = null
    

    The policy interface parameters then design the option to use the OKEX_V5 analog drive, so some processing is done in the code:

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

    The interface parameters also design the option to reset all information, so there is also a corresponding processing in the code:

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

    We only run a permanent contract, so here it is written dead, only set up as a permanent contract.

    exchange.SetContractType("swap")
    

    Then we also have to consider the issue of price precision, order precision, if the precision is not set correctly, the precision is lost in the process of strategic calculation, a lot of data bits are easy to cause the exchange interface to reject the order.

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

    Designed for simple data recovery

    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
        }
    }
    

    If you want to specify the initial account total privileges when the policy runs, you can set the parameterstotalEqIf this parameter is set to -1, the strategy reads the stored total utility data, if no stored total utility data is read, the initial utility is the current read utility as the progress of the strategy, after which the increase in utility indicates a loss, the decrease in utility indicates a loss. If the total utility data is read, the use of this data continues.

  • Main logic After the initial work was done, it finally came to the main logic of the strategy, and for ease of explanation, I wrote the instructions directly in the code commentary.

      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)
      }
    

The whole logic and design is explained.

Reassessment

Let the strategy go through what happened on May 19th.

img

img

As you can see, the Martin-type tactic still carries some risks.

The real disk can be played with the OKEX V5 simulator

img

The policy address:https://www.fmz.com/strategy/294957

The strategy is mainly used for learning, real gold and silver are used wisely ~!


Related

More

lisa20231I'm going to ask you a question. if (!isFindSellId &&!isFindBuyId) { // detects that all purchase and sale orders have been processed If the order is picked up and sold at the same time, will the error be detected when the order is picked up?

Neo1898The other thing is the contract model is full stock or individual stock how is it set up?

Neo1898Why is it that the covenant is not set in double? How many times is the sale?

Light cloudsThanks Dreama, I can finally understand from beginning to end. Then I learned how to monitor hanging lists, and then I wrote a double-sided Martin. For two days, I wrote 580 lines. Thank you Dreama [clenched fist]

HK quantity/upload/asset/1a9ebf427c4e2cbf1c327.png This is a list of all the different ways Upload/Asset is credited in the database. False true in exchange?

Dreams are worth eight figures.What if

Dreams are worth eight figures.Owner rights and interests

nullDo we need to stop the damage?

btcrobotMartin, I've retested it and it's zero.

wqySo let's say that the number of digits is equal to the number of digits.

lvdaleiIs the strategy multi-space double open or single open?

The Little DreamIt doesn't throw out mistakes. It still cancels all pending orders, jumps out of the current cycle, and then continues the pending logic.

The Little DreamIn general, use the whole store.

The Little DreamLeverage can be set at the exchange, depending on your risk preferences.

The Little DreamThe total number of clouds is 666!

The Little DreamIf the variable is called isFindBuyId, it is not appropriate. It should be called isNotFindBuyId.

The Little DreamThis tactic is not designed to stop losses. So the curve running out can be seen as an upward curve.

The Little DreamHa ha, Martin's homecoming is here. This article is mainly about the design of teaching strategies without paying too much attention to the benefits.

The Little DreamThat N is for later modifications, such as thinking about n times the distance to the storage, which can be set to 1 temporarily.

The Little DreamI'm not going to tell you about it.