In the past few articles, we have discussed the access to mainstream DEXs, and this article will focus on actual practice and conduct actual strategy deployment tests. The FMZ platform has recently added support for WOOFi and EdgeX decentralized exchanges. In this article, we will practice running some simple teaching strategies on these two exchanges.

WOOFi

Connect the wallet on WOOFi, and then on the API KEY page, we can view the API key information, copy and paste it and configure it on FMZ.

Use FMZ's latest docker, which already supports WOOFi DEX and EdgeX DEX. After downloading and deploying, configure the exchange object on the page: https://www.fmz.com/m/platforms/add, and configure AccountId, AccessKey, and SecretKey of WOOFi.

In this test, we used a basic market-making strategy prototype, combined with the market volatility indicator (ATR) to calculate the order spacing dynamically, and implemented the intelligent identification of positions and the order logic of closing positions first. The strategy refreshes the order book every round, re-obtains the depth and position information, and places orders according to the set price interval and order amount. The whole process covers:

  • Real-time market information extraction and indicator analysis;
  • Logic control of pending orders in both long and short directions;
  • Judgment and diversion of closing and opening positions;
  • Visual output of positions and account status.

Through this strategy, we can observe the actual transaction efficiency, order delay, and matching experience on WOOFi, laying the foundation for the subsequent design of more complex strategies.

We use WOOFi's test environment and test network: Arbitrum Sepolia.

exchange.SetBase("https://testnet-api.orderly.org")

There is a Leaderboard on the WOOFi test network, which can easily obtain USDC for testing.

Strategy code:

functioncreateOrders(e, symbol, side, ordersNum, beginPrice, firstAmount, spacing, pos) {
    if (side == "buy" || side == "closesell") {
        if (spacing > 0) {
            throw"spacing error"
        }
    } elseif (side == "sell" || side == "closebuy") {
        if (spacing < 0) {
            throw"spacing error"
        }
    } else {
        throw"side error"
    }
    
    var holdAmount = 0if (pos) {
        holdAmount = pos.Amount
    }

    var amount = firstAmount
    for (var i = 0 ; i < ordersNum ; i++) {
        var id = null 
        amount = amount * 2var price = beginPrice + i * spacing

        if (price <= 0 || amount <= 0) {
            Log("continue loop:", price, amount, "#FF0000")
            continue 
        }

        if (holdAmount - amount >= 0) {
            id = e.CreateOrder(symbol, side == "buy" ? "closesell" : "closebuy", price, holdAmount)
            holdAmount = 0
        } else {
            id = e.CreateOrder(symbol, side, price, amount)
        }

        Sleep(100)
    }
}

functioncancelAll(e, symbol) {
    while (true) {
        var orders = _C(e.GetOrders, symbol)
        var sideOrders = []
        for (var o of orders) {
            sideOrders.push(o)
        }
        if (sideOrders.length == 0) {
            break
        }

        for (var o of sideOrders) {
            e.CancelOrder(o.Id, o)
        }

        Sleep(500)
    }
}

functionmain() {
    LogReset(1)
    LogProfitReset()
    exchange.SetBase("https://testnet-api.orderly.org")

    // parametersvar symbol = "ETH_USDC.swap"var ordersNum = 5var orderAmount = 0.01var priceSpace = 0// initialization
    exchange.SetPrecision(2, 3)    
    var msg = []
    var buyOrdersNum = ordersNum
    var sellOrdersNum = ordersNum

    while (true) {
        cancelAll(exchange, symbol)

        var r = _C(exchange.GetRecords, symbol, 60 * 5)
        var art = TA.ATR(r, 20)
        priceSpace = art[art.length - 1]
        var pos = _C(exchange.GetPositions, symbol)        

        // depthvar depth = _C(exchange.GetDepth, symbol)
        if (depth.Bids.length == 0 || depth.Asks.length == 0) {
            msg.push("invalid depth")
        } else {
            var bid1Price = depth.Bids[0].Pricevar ask1Price = depth.Asks[0].Pricevar longPos = nullvar shortPos = nullfor (var p of pos) {
                if (p.Type == PD_LONG) {
                    longPos = p
                } elseif (p.Type == PD_SHORT) {
                    shortPos = p
                }
            }

            // longcreateOrders(exchange, symbol, "buy", buyOrdersNum, bid1Price, orderAmount, -priceSpace, shortPos)
            // shortcreateOrders(exchange, symbol, "sell", sellOrdersNum, ask1Price, orderAmount, priceSpace, longPos)
        }
        
        var acc = _C(exchange.GetAccount)
        var orders = _C(exchange.GetOrders, symbol)
        LogProfit(acc.Equity, "&")

        var posTbl = {"type": "table", "title": "pos", "cols": ["Symbol", "Type", "Price", "Amount"], "rows": []}
        for (var p of pos) {
            posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
        }

        var ordersTbl = {"type": "table", "title": "orders", "cols": ["Symbol", "Type", "Price", "Amount"], "rows": []}
        for (var o of orders) {
            ordersTbl["rows"].push([o.Symbol, o.Type == ORDER_TYPE_BUY ? "buy" : "sell", o.Price, o.Amount])
        }

        LogStatus(_D(), "priceSpace:", priceSpace, "\n`" + JSON.stringify([posTbl, ordersTbl]) + "`")
        Sleep(1000 * 60)
        LogReset(1000)
    }
}

Strategy practice on WOOFi

img
img
img

EdgeX

The API information for configuring EdgeX on FMZ is basically the same as that for WOOFi, but different exchanges require different API information. On EdgeX, you only need to configure AccountId and SecretKey. These can also be viewed on the account API management page after using the wallet to connect to the EdgeX front end.

The strategy we want to practice on EdgeX is a quantitative trading logic based on the multi-layer Bollinger Band reverse opening + mid-track closing to achieve short-term volatility arbitrage.

The strategy is very simple, and the core idea is:

  • By utilizing multiple Bollinger standard deviations, the intensity of market volatility can be quantified.
  • There is a logic of opening and increasing positions. The stronger the breakthrough, the larger the position is.
  • There is a clear logic for closing positions, and we will withdraw when the position returns to the middle track.
  • Volume is proportional to the standard deviation multiple: stronger breakouts lead to larger positions.

You may not believe that it only takes 50 lines of code to write a complete strategy on FMZ. The development of AI big models has greatly lowered the threshold for strategy design. The strategy ideas we tested can be easily produced by AI, and the writing quality is also sufficient. The only thing is that manual correction is required, but it has greatly lowered the threshold for ordinary people to use quantitative trading technology.

Strategy code:

functionmain() {
    var symbol = "ETH_USDT.swap"var arrUp = []
    var arrDown = []
    let c = KLineChart({
        overlay: true
    }) 
    while (true) {
        var bolls = []
        var r = _C(exchange.GetRecords, symbol)
        for (var i = 0; i < 3; i++) {
            var boll = TA.BOLL(r, 20, i + 1)
            bolls.push(boll)
            var up = boll[0][boll[0].length - 1]
            var mid = boll[1][boll[1].length - 1]
            var down = boll[2][boll[2].length - 1]
            var close = r[r.length - 1].Closeif (close > up && i >= arrUp.length) {
                exchange.CreateOrder(symbol, "sell", -1, 0.01 * (i + 1))
                arrUp.push({"symbol": symbol, "amount": 0.01 * (i + 1)})
            } elseif (close < down && i >= arrDown.length) {
                exchange.CreateOrder(symbol, "buy", -1, 0.01 * (i + 1))
                arrDown.push({"symbol": symbol, "amount": 0.01 * (i + 1)})
            } elseif ((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) {
                var pos = exchange.GetPositions(symbol)
                for (var p of pos) {
                    if (p.Type == PD_LONG) {
                        exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
                    } elseif (p.Type == PD_SHORT) {
                        exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
                    }
                }
                arrUp = []
                arrDown = []
            }
        }
        r.forEach(function(bar, index) {
            c.begin(bar)
            for (var i in bolls) {
                var b = bolls[i]
                c.plot(b[0][index], 'up_' + (i + 1))
                c.plot(b[1][index], 'mid_' + (i + 1))
                c.plot(b[2][index], 'down_' + (i + 1))
            }
            c.close()
        })
        LogStatus(_D(), "\n", arrUp, "\n", arrDown)
        Sleep(500)
    }
}

Let's do a long-term backtest first:

img
img

Deploy EdgeX test

img

END

The above strategies are for teaching and study purposes only. Please be cautious when use it in live trading. Thank you for reading.

From: Quantitative Practice of DEX Exchanges (4) - Strategy Access Test of WOOFi / EdgeX

Leave a Reply

Your email address will not be published. Required fields are marked *