LeeksReaper Strategy Analysis (2)
Tags: leeksreaper, strategy, fmzquant, trading
Published: 2024-05-06 07:56:00

Let's continue to explain the content of the last chapter (https://www.fmz.com/bbs-topic/9725).

The third added function:
```
    self.balanceAccount = function() {
        var account = exchange.GetAccount()
        if (!account) {
            return
        }
        self.account = account
        var now = new Date().getTime()
        if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {
            self.preCalc = now
            var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks))
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
        }
        self.btc = account.Stocks
        self.cny = account.Balance
        self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
        var balanced = false
        
        if (self.p < 0.48) {
            Log ( \"\" Start Balance \"\", self. P)
            self.cny -= 300
            if (self.orderBook.Bids.length >0) {
                exchange.Buy(self.orderBook.Bids[0].Price + 0.00, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.01, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.02, 0.01)
            }
        } else if (self.p > 0.52) {
            Log ( \"\" Start Balance \"\", self. P)
            self.btc -= 0.03
            if (self.orderBook.Asks.length >0) {
                exchange.Sell(self.orderBook.Asks[0].Price - 0.00, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.01, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.02, 0.01)
            }
        }
        Sleep(BalanceTimeout)
        var orders = exchange.GetOrders()
        if (orders) {
            for (var i = 0; i < orders.length; i++) {
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)
                }
            }
        }
    }
```

When the constructor LeeksReaper () constructs an object, the balanceAccount () function added to the object updates the account asset information stored in self.account, that is, the account attribute of the constructed object. Calculate the revenue value and print it on time. Then, according to the latest account asset information, calculate the spots currency balance ratio (spots position balance) , when triggering the offset threshold, close the position with a small order, so that the currency (position) back to the equilibrium state. Wait a certain time to deal, then cancel all the makers, the next round of execution of the function, it will check for the balance and make the corresponding processing again.

Let's look at the code of this function sentence by sentence:
First, the first sentence var account = exchange.GetAccount () declares a local variable account and calls the function of exchange.GetAccount on FMZ API interface. Get the latest data of the current account and assign it to the variable account. Then judge the variable account. If the variable is null (for example, timeout, network, exchange interface exception, etc.), it will return (corresponding to if (!account) {...}) directly.

self.account = account is to assign the local variable account to the account attribute of the constructed object to record the latest account information in the constructed object.

Var now = new Date().getTime () declares a local variable now and calls the getTime() function of the JavaScript language's time date object to return the current timestamp. Assigns a value to the variable now.

if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)){...} determines that if the difference between the current timestamp and the timestamp recorded last time exceeds the parameter CalcNet Interval * 1000, it means that it has been updated from the last time. Up to now, it has exceeded CalcNetInterval * 1000 milliseconds (CalcNetInterval seconds), which realizes the function of printing income at regular time. Because the price of buying one is used to calculate the income, the condition of self.orderBook.Bids.length > 0 is also defined in the condition (depth data, there must be valid level information in the order list). When the if statement condition is triggered, the self.PreCalc = now is executed to update the timestamp variable of the most recently printed return self.preCalc to the current timestamp now. Here, the net value calculation method is used in the return statistics. The code is var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), that is, convert the currency into money (denominated currency) according to the current buying one price, and then add it to the amount of money in the account and assign it to the declared local variable net. Judge whether the current total net value is consistent with the total net value recorded last time:
```
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
```

If it is not consistent, that is, net! = self.preNet is true, update the attribute of self.preNet used to record the net value with the variable net. Then print the total net of net data to the yield curve chart of the FMZ Quant Trading platform robot (the LogProfit function can be queried in the FMZ API document).

If the regular printing of earnings is not triggered, continue the following process to record the account.Stocks (currency available in the current account) and the account.Balance (currency available in the current account) in the self.BTC and self.CNY. Calculate the offset scale and record the assignment in the self.p.

```
self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
```

The algorithm is also very simple, which is to calculate the percentage of the current value of the currency to the total net value of the account.

What about judging when to trigger the balance of money (position)?
Here, I take 50% plus or minus 2 percentage points as the buffer, and executes the balance beyond the buffer, that is, if the self.p < 0.48, the money balance is triggered by deviation. If the money is less, the price will increase by 0.01 each time from the position of buying at the opening of the market, and three small orders will be arranged. Similarly, the money balance self.p > 0.52, if the currency is more, sell one and release small orders. Finally, cancel all orders after waiting for Sleep(BalanceTimeout) for a certain time according to the parameter settings.

```
        Var orders = exchange. Get Orders () # Get all current makers, with orders variable
        If (orders) { # If the variable orders used to obtain the current order data is not null
            for (var i = 0; i < orders.length; I + +) { # Loop through orders and cancel orders one by one
                if (orders[i].Id != self.tradeOrderId) {
                    Exchange. CancelOrder (orders [I]. Id) # Call exchange. CancelOrder to cancel orders based on orders [I]. Id
                }
            }
        }
```

The fourth added function:

In the core part of the strategy, here comes the main play. The self.poll = function(){...} function is the main logic of the entire strategy. As we said in the previous article, before the main() function starts to execute and enters the endless while loop, we use var reaper = LeeksReaper() to construct the leeksreaper object, and then execute the loop call of reaper.poll() in the main() function.

The self.poll function begins to execute, doing some preparatory work before each loop. The self.numTick++ increments the count. The self.updateTrades() updates the recent market trading records and calculates the relevant usage data. The self.updateOrderBook() updates the order data and calculates the relevant data. The self.balanceAccount() check the money (position) balance.

```
        Var burstPrice = self. Prices [self. Prices. Length-1] * BurstThresholdPct # Calculate Burst Price
        Var bull = false                   # Declare a bull-marked variable, initially false
        Var bear = false                  # Declare a bear marked variable, initially false
        Var tradeAmount = 0         # Declare the transaction amount variable, initially 0
```

The next step is to judge whether the current short-term market is a bull or a bear.

```
        if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -1)) > burstPrice ||
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -2)) > burstPrice && self.prices[self.prices.length-1] > self.prices[self.prices.length-2]
            )) {
            bull = true
            tradeAmount = self.cny / self.bidPrice * 0.99
        } else if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -1)) < -burstPrice ||
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -2)) < -burstPrice && self.prices[self.prices.length-1] < self.prices[self.prices.length-2]
            )) {
            bear = true
            tradeAmount = self.btc
        }
```

Do you remember the self.updateOrderBook() function from the previous article where we used a weighted average algorithm to construct a time-ordered prices array? Three new functions:  _.min, _.max, and slice are used in the code and they are easy to understand.

 · _. min: The function is to find the minimum value in the parameter array.

 · _.max: The function is to find the maximum value in the parameter array.

 · slice: The function is a member function of the JavaScript array object. It's used to return a part of the array according to the index. For example:

```
function main() {
    // index     .. -8 -7 -6 -5 -4 -3 -2 -1
    var arr = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    Log (arr. Slice (-5, -1))   // it will intercept the elements from 4 to 1 and return a new array: [4,3,2,1]
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08pfa5s619dz4ane5kfr.png)

The conditions for judging bear or bull market are:

 · The self.numTick > 2 must be true, that is to say, when a new round of detection price breaks out, it must be triggered after at least three rounds of detection, so as to avoid triggering at the beginning.
 · The difference between the last data in the self.prices of the price sequence, that is, the latest data, and the maximum or minimum price in the previous range in the self.prices array should exceed the burst price of burstPrice.

If all conditions are true, mark bull or bear as true, and assign a value to the variable tradeAmount to plan the Stud transaction.

Then, according to the self.vol updated and calculated in the previous self.updateTrades() function, the BurstThresholdVol parameter determines whether to reduce the transaction intensity (reduce the planned transaction volume).

```
        if (self.vol < BurstThresholdVol) {
            TradeAmount * = self. Vol/BurstThresholdVol      //Reduce the planned volume by self. Vol/BurstThresholdVol times of the previous volume
        }
        
        if (self.numTick < 5) {
            TradeAmount * = 0.8      // reduced to 80% of the plan
        }
        
        If (self. NumTick < 10) {       // reduce to 80% of the plan
            tradeAmount *= 0.8
        }
```

Next, judge whether the trading signal and volume meet the requirements:

```
        If ( (!Bull && !Bear) | | tradeAmount &lt; MinStock) {     # If it is not a bull market and not a bear market, or the amount tradeAmount planned to trade is less than the minimum trading volume MinStock set by the parameter, the poll function returns without trading operations directly
            return
        }
```

After the above judgment, execute var tradePrice = bull ? self.bidPrice: self.askPrice sets the transaction price according to whether it is a bear market or a bull market, and assigns the value with the corresponding bill of lading price.

Finally, a while loop is entered, and the only stop condition of the loop is that the planned trading volume of tradeAmount > = MinStock is less than the minimum trading volume.
In the loop, the order is executed according to the current market state. And record the order ID in the variable orderId. Sleep(200) waits for 200 milliseconds after placing an order in each loop. The loop then determines whether the orderId is true (if the order fails, the order ID will not be returned, and the if condition will not be triggered). If the condition is true. Get the order ID and assign it to the self.tradeOrderId.

Declare a variable order used to store order data, with an initial value of null. Then the order data of the ID is obtained in a loop, and judge whether the order is the maker state, if so, the order of the ID is cancelled, and if not, the detection loop is ended.

```
                Var order = null         // Declare a variable to hold the order data
                While (true) {             // a while loop
                    Order = exchange. GetOrder (orderId)          // Call GetOrder to query the order data whose order ID is orderId
                    If (order) {                                                   // If the order data is queried and the query fails and the order is null, the current if condition will not be triggered
                        If (order. Status = = ORDER _ STATE _ PENDING) {              // Judge whether the order status is maker
                            Exchange. CancelOrder (orderId)                                    // If the order is maker, cancel the order
                            Sleep(200)
                        } else {                                                                               // otherwise execute break to end the current while loop
                            break
                        }
                    }
                }
```
The following process is then performed:
```
                Self. TradeOrderId = 0                         // Reset self. TradeOrderId.
                TradeAmount-= order. DealAmount    // Update tradeAmount, subtract the quantity of the order on the bill of lading that has been completed
                TradeAmount * = 0.9                          //Decrease the order amount
                If (order. Status = = ORDER _ STATE _ CANCELED) {                   // if the order is already cancelled
                    Self. UpdateOrderBook ()                                                      // Update data such as order book
                    While (bull & & self. BidPrice-tradePrice &gt; 0.1) {               // In a bull market, if the updated bill of lading price exceeds the current trading price by 0.1, the trading amount will be reduced and the trading price will be adjusted slightly
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    While (bear & & self. AskPrice-tradePrice &lt; -0.1) {             // In a bear market, if the updated bill of lading price exceeds the current trading price by 0.1, the trading amount will be reduced and the trading price will be adjusted slightly
                        tradeAmount *= 0.99
                        tradePrice -= 0.1
                    }
                }
```
When the program process ends of the loop of while (tradeAmount > = MinStock){...}, it indicates that the execution of this price burst transaction process is completed.
Execute the self.numTick = 0, that is, reset the self.numTick to 0.

The LeeksReaper() constructor returns the self object at the end of execution, that is, when var reaper = LeeksReaper(), it is returned to reaper.

So far, we have analyzed how the LeeksReaper() constructor constructs the LeeksReaper object, each method of the LeeksReaper object, and the execution process of the main logic functions. I believe that you will have a clear understanding of this high-frequency strategy algorithm process after reading this article.

From: https://blog.mathquant.com/2022/11/07/leeksreaper-strategy-analysis-2.html