The resource loading... loading...

LeeksReaper Strategy Analysis (2)

Author: FMZ~Lydia, Created: 2022-11-07 16:38:41, Updated: 2025-01-11 18:16:30

LeeksReaper Strategy Analysis (2)

LeeksReaper Strategy Analysis (2)

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.


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


![LeeksReaper Strategy Analysis (2)](/upload/asset/28d855d193529106e2054.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 ofwhile (tradeAmount > = MinStock){…}, it indicates that the execution of this price burst transaction process is completed. Execute theself.numTick = 0, that is, reset theself.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.


Related

More