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