In the previous article, we implemented a simple hedging strategy together, and then we will learn how to upgrade this strategy.
The strategy changes are not big, but the details of the changes need attention. The definitions of some places in the code have changed from the previous ones, which need to be understood.

The need to upgrade this strategy

  • Switching spot exchange object leverage mode
    This change is only related to the real bot. Some spot exchanges have spot leverage interfaces, which are also encapsulated on FMZ. For exchange objects that have been directly packaged on FMZ and support spot leverage, the mode can be switched directly.
  • Add spread chart display
    Add spread chart display, because it's just drawing the spread line of A exchange -> B exchange, B exchange -> A exchange, and drawing the horizontal line that triggers the spread. We use the line drawing class library to deal with directly, the advantage is that it is easy to use, here we also learn how to use the template class library function of FMZ.
  • One-sided hedging function
    This change can be quite significant, because it is difficult to completely reverse the price difference between the two exchanges during specific hedging transactions. Most of the time the price on one exchange is consistently higher than the price on another exchange. At this time, if our assets have been fully hedged (that is, the coins are all on exchanges with low prices, and the money is on exchanges with high prices). The hedging is stagnant, and it is no longer possible to rely on the fluctuation of the spread to make a profit. At this time, we need to make the strategy so that you can lose a little money to hedge the coins back (let the coins exist on the exchange with high price again), and when the price difference becomes larger again, we can continue to hedge and earn profit.
  • Interactively modify parameters such as hedging spread lines
    Add interactive function to the strategy to modify the spread trigger line in real time.
  • Organize status bar information and display it in a table format
    Arrange the data that needs to be displayed for easy viewing.

Next, let's implement these designs one by one.

Switch spot exchange object leverage mode

Take Binance spot real bot as an example, switch to spot leveraged mode, use the code exchanges[i].IO, input the parameter trade_normal to switch to leverage position by position, and input trade_super_margin to switch to leverage full position, backtesting is not supported. This is only used in real bot.

Add to the preparation phase at the beginning of the main function:

    // Switch leverage mode
    for (var i = 0 ; i < exchanges.length ; i++) {   // Traverse and detect all added exchange objects
        if (exchanges[i].GetName() == "Binance" && marginType != 0) {   //If the exchange object represented by the current i-index is Binance spot, and the parameter marginType of the strategy interface is not the option of "common currency", execute the switch operation
            if (marginType == 1) {
                Log(exchanges[i].GetName(), "Set to leveraged position-by-position")
                exchanges[i].IO("trade_normal")
            } else if (marginType == 2) {
                Log(exchanges[i].GetName(), "Set to leveraged full position")
                exchanges[i].IO("trade_super_margin")
            }
        }
    }

The strategy here only adds the code for switching the coin-to-coin leverage mode of Binance spot, so the switch setting on the strategy parameters is only valid for Binance spot.

Added spread chart display

It is very easy to use the already wrapped drawing template. The template name we use is Line Drawing Library. It can be obtained by searching directly on the FMZ platform strategy square.

/upload/asset/172ad53420be3a586e99.png

Or click the link directly: https://www.fmz.com/strategy/27293 to jump to the copy page for this template.

/upload/asset/165513895d34be075d86.png

Click the button to copy this template class library to your own strategy library.

/upload/asset/167f480d26199d6f9c7b.png

Then you can check the template class library to be used in the template column on the strategy editing page. Save the strategy after checking it, and the strategy will refer to this template. This is just a brief description of the use of the template class library. This strategy has already referenced this template, so there is no need to repeat the operation. When you copy this strategy in the strategy square, you can see that Line Drawing Library has been referenced in the template bar on the strategy editing page.

We will mainly learn how to use the functions of the Line Drawing Library to draw a graph.

/upload/asset/166a625790361d45e54c.png

We plan to draw the spread of A->B, the spread of B->A, and the trigger line of the spread. We need to draw two curves (current A to B, B to A spread), two horizontal lines (trigger spread line), as shown in the figure above.

Because we want to design unilateral hedging, the trigger lines of A->B and B->A are different. We cannot use the design in the previous article.
In the previous article:

      var targetDiffPrice = hedgeDiffPrice
      if (diffAsPercentage) {
          targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
      }

There is only one trigger spread targetDiffPrice.
So here we have to transform the code, transform the parameters first.

/upload/asset/15ec045fee06f57d5a2c.png

Then modify the code:

        var targetDiffPriceA2B = hedgeDiffPriceA2B
        var targetDiffPriceB2A = hedgeDiffPriceB2A
        if (diffAsPercentage) {
            targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageA2B
            targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageB2A
        }

In this way, the difference trigger line has changed from the previous targetDiffPrice to two targetDiffPriceA2B, targetDiffPriceB2A.
The next step is to draw this data on the chart by using the draw line function of the line drawing library.

        // drawing
        $.PlotHLine(targetDiffPriceA2B, "A->B")  // The first parameter of this function is the value of the horizontal line in the Y-axis direction, and the second parameter is the display text
        $.PlotHLine(targetDiffPriceB2A, "B->A")

When the strategy is running, there is a chart like this.

/upload/asset/16c484292df459589684.png

Next draw the real-time spread curve, to avoid overdrawing the line. Put the code that draws the curve of the real-time spread data in the balance check.

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

            $.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price)  // Draw real-time spread curves
            $.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price)  // The first parameter is the name of the curve, and the second parameter is the value of the curve at the current moment, that is, the value in the Y-axis direction at the current moment
        }

In this way, the drawing code is only 4 lines, allowing the strategy to have a graph displayed at runtime.

One-sided hedging function

As mentioned above, the spread trigger line has been modified into two, controlling the hedging trigger of A->B and B->A respectively. In this way, the previous order price algorithm cannot be used, and the method of adding slip price to the market price is used instead.

        if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) {          // A -> B market conditions are met            
            var priceSell = depthA.Bids[0].Price - slidePrice
            var priceBuy = depthB.Asks[0].Price + slidePrice
            var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
            if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) {
                amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount)
                Log("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks)  // Tips
                hedge(exB, exA, priceBuy, priceSell, amount)
                cancelAll()
                lastKeepBalanceTS = 0
                isTrade = true 
            }            
        } else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) {   // B -> A market conditions are met
            var priceBuy = depthA.Asks[0].Price + slidePrice
            var priceSell = depthB.Bids[0].Price - slidePrice
            var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
            if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) {
                amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount)
                Log("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks)  //Tips
                hedge(exA, exB, priceBuy, priceSell, amount)
                cancelAll()
                lastKeepBalanceTS = 0
                isTrade = true 
            }            
        }

Since the buying and selling prices are separated into two data, the hedging function hedge also needs to be modified.

function hedge(buyEx, sellEx, priceBuy, priceSell, amount) {
    var buyRoutine = buyEx.Go("Buy", priceBuy, amount)
    var sellRoutine = sellEx.Go("Sell", priceSell, amount)
    Sleep(500)
    buyRoutine.wait()
    sellRoutine.wait()
}

There are also some minor adjustments based on these changes, which will not be repeated here. You can look at the code for details.

Interactively modify parameters such as hedging spread lines

Add interaction to the strategy, so that the strategy can modify the spread trigger line in real time. This is the design requirement of a semi-automatic strategy, which is also implemented here as a teaching demonstration.
The strategy interaction design is also very simple. First, add interaction controls to the strategy on the strategy editing page.

/upload/asset/16ba34ddfe29dd4b86ca.png

Added two controls, one called A2B and one called B2A. After entering a value in the control input box, click the button to the right of the input box. An instruction will be sent to the strategy immediately, for example: enter the value 123 in the input box, click the A2B button, and an instruction will be sent to the strategy immediately.

A2B:123

Design interactive detection and processing code in the strategy code.

        // interact
        var cmd = GetCommand()   // Every time the loop is executed here, it checks whether there is an interactive command, and returns to an empty string if not.
        if (cmd) {               // An interactive command was detected, such as A2B:123
            Log("command received:", cmd)
            var arr = cmd.split(":")   // Split out the interactive control name and the value in the input box, arr[0] is A2B, arr[1] is 123
            if (arr[0] == "A2B") {     // Determine whether the triggered interactive control is A2B
                Log("Modify the parameters of A2B, ", diffAsPercentage ? "The parameter is the difference percentage" : "The parameter is the difference:", arr[1])
                if (diffAsPercentage) {
                    hedgeDiffPercentageB2A = parseFloat(arr[1])     // Modify the trigger spread line
                } else {
                    hedgeDiffPriceA2B = parseFloat(arr[1])          // Modify the trigger spread line
                }
            } else if (arr[0] == "B2A") {           // Triggered control detected is B2A     
                Log("Modify the parameters of B2A, ", diffAsPercentage ? "The parameter is the difference percentage" : "The parameter is the difference:", arr[1])
                if (diffAsPercentage) {
                    hedgeDiffPercentageA2B = parseFloat(arr[1])
                } else {
                    hedgeDiffPriceB2A = parseFloat(arr[1])
                }
            }
        }

Organize status bar information and display it in a table format

Make the status bar data display more organized and easy to observe.

        var tbl = {
            "type" : "table", 
            "title" : "data", 
            "cols" : ["exchange", "coin", "freeze coin", "denominated currency", "freeze denominated currency", "trigger spread", "current spread"], 
            "rows" : [], 
        }
        tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)])
        tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)])

        LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")

/upload/asset/16464f405c106ce54f3b.png

Backtesting

Backtesting is only a test strategy, a preliminary detection function, and many bugs can be tested out in the backtesting stage actually. It is not necessary to pay too much attention to the backtesting results. The final strategy still needs to be tested in the real environment.

/upload/asset/16ec60cc3fa9790ce6d0.png

/upload/asset/1731f543e6e347d5e880.png

Strategy source code: https://www.fmz.com/strategy/302834

Leave a Reply

Your email address will not be published.