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.
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:
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:
function createOrders(e, symbol, side, ordersNum, beginPrice, firstAmount, spacing, pos) {
if (side == "buy" || side == "closesell") {
if (spacing > 0) {
throw "spacing error"
}
} else if (side == "sell" || side == "closebuy") {
if (spacing < 0) {
throw "spacing error"
}
} else {
throw "side error"
}
var holdAmount = 0
if (pos) {
holdAmount = pos.Amount
}
var amount = firstAmount
for (var i = 0 ; i < ordersNum ; i++) {
var id = null
amount = amount * 2
var 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)
}
}
function cancelAll(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)
}
}
function main() {
LogReset(1)
LogProfitReset()
exchange.SetBase("https://testnet-api.orderly.org")
// parameters
var symbol = "ETH_USDC.swap"
var ordersNum = 5
var orderAmount = 0.01
var 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)
// depth
var depth = _C(exchange.GetDepth, symbol)
if (depth.Bids.length == 0 || depth.Asks.length == 0) {
msg.push("invalid depth")
} else {
var bid1Price = depth.Bids[0].Price
var ask1Price = depth.Asks[0].Price
var longPos = null
var shortPos = null
for (var p of pos) {
if (p.Type == PD_LONG) {
longPos = p
} else if (p.Type == PD_SHORT) {
shortPos = p
}
}
// long
createOrders(exchange, symbol, "buy", buyOrdersNum, bid1Price, orderAmount, -priceSpace, shortPos)
// short
createOrders(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
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:
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:
function main() {
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].Close
if (close > up && i >= arrUp.length) {
exchange.CreateOrder(symbol, "sell", -1, 0.01 * (i + 1))
arrUp.push({"symbol": symbol, "amount": 0.01 * (i + 1)})
} else if (close < down && i >= arrDown.length) {
exchange.CreateOrder(symbol, "buy", -1, 0.01 * (i + 1))
arrDown.push({"symbol": symbol, "amount": 0.01 * (i + 1)})
} else if ((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)
} else if (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:
Deploy EdgeX test
The above strategies are for teaching and study purposes only. Please be cautious when use it in live trading. Thank you for reading.