リークスリーパー戦略分析 (1)

作者: リン・ハーンリディア作成日:2022年11月14日 15:42:26 更新日:2023年9月15日 21:08:48 更新日:2023年9月15日 21:08:48 更新日:2020年9月15日 21:08:48 更新日:2020年9月15日 21:08:48 更新日:2020年9月15日 21:08:48 更新日:2020年9月15日 21:08:48 更新日:2020年9月15日 21:08:48

img

リークスリーパー戦略分析 (1)

最近,熱い議論が起こっています.print moneyFMZ Quantの微信グループで ロボットです ロボット取引の原則print moneyそこで,私は元の戦略を注意深く再読し,移植されたOKCoinの移植版をFMZ Quantで見ました. FMZ Quantプラットフォームをベースにした移植した刈りの戦略を分析し,戦略のアイデアを探求します. この記事では,プログラミングに関連する退屈なコンテンツを最小限に抑えるために戦略のアイデアと意図の側面からさらに分析します.

[OKCoin LeeksReaperを移植する]戦略のソースコード:

function LeeksReaper() {
    var self = {}
    self.numTick = 0
    self.lastTradeId = 0
    self.vol = 0
    self.askPrice = 0
    self.bidPrice = 0
    self.orderBook = {Asks:[], Bids:[]}
    self.prices = []
    self.tradeOrderId = 0
    self.p = 0.5
    self.account = null
    self.preCalc = 0
    self.preNet = 0

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)
        if (self.prices.length == 0) {
            while (trades.length == 0) {
                trades = trades.concat(_C(exchange.GetTrades))
            }
            for (var i = 0; i < 15; i++) {
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }
    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }
    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)
                }
            }
        }
    }

    self.poll = function() {
        self.numTick++
        self.updateTrades()
        self.updateOrderBook()
        self.balanceAccount()
        
        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct
        var bull = false
        var bear = false
        var tradeAmount = 0
        if (self.account) {
            LogStatus(self.account, 'Tick:', self.numTick, ', lastPrice:', self.prices[self.prices.length-1], ', burstPrice: ', burstPrice)
        }
        
        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
        }
        if (self.vol < BurstThresholdVol) {
            tradeAmount *= self.vol / BurstThresholdVol
        }
        
        if (self.numTick < 5) {
            tradeAmount *= 0.8
        }
        
        if (self.numTick < 10) {
            tradeAmount *= 0.8
        }
        
        if ((!bull && !bear) || tradeAmount < MinStock) {
            return
        }
        var tradePrice = bull ? self.bidPrice : self.askPrice
        while (tradeAmount >= MinStock) {
            var orderId = bull ? exchange.Buy(self.bidPrice, tradeAmount) : exchange.Sell(self.askPrice, tradeAmount)
            Sleep(200)
            if (orderId) {
                self.tradeOrderId = orderId
                var order = null
                while (true) {
                    order = exchange.GetOrder(orderId)
                    if (order) {
                        if (order.Status == ORDER_STATE_PENDING) {
                            exchange.CancelOrder(orderId)
                            Sleep(200)
                        } else {
                            break
                        }
                    }
                }
                self.tradeOrderId = 0
                tradeAmount -= order.DealAmount
                tradeAmount *= 0.9
                if (order.Status == ORDER_STATE_CANCELED) {
                    self.updateOrderBook()
                    while (bull && self.bidPrice - tradePrice > 0.1) {
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    while (bear && self.askPrice - tradePrice < -0.1) {
                        tradeAmount *= 0.99
                        tradePrice -= 0.1
                    }
                }
            }
        }
        self.numTick = 0
    }
    return self
}

function main() {
    var reaper = LeeksReaper()
    while (true) {
        reaper.poll()
        Sleep(TickInterval)
    }
}

戦略の概要

一般的に,勉強する戦略を手に入れたとき,まず全体的なプログラム構造を見てください. 戦略コードは非常に長くない,コードの200行未満で,それは非常に簡潔で,元の戦略はほとんど同じで,非常に復元されています. 戦略コードはmain()戦略コードのすべて,を除いてmain(), は関数である.LeeksReaper().....LeeksReaper()簡単に言うと,この関数は,leeksreaper戦略論理モジュール (オブジェクト) のコンストラクターとして理解できます.LeeksReaper()を収穫する商取引の論理を構築する責任があります.

キーワード:

img

· 戦略の第一線main機能:var reaper = LeeksReaper(),コードはローカル変数を宣言しますreaperそして LeeksReaper ((() 関数を呼び出し,戦略論理オブジェクトを構成します.reaper.

戦略の次のステップmain機能:

while (true) {
    reaper.poll()
    Sleep(TickInterval)
}

A を入力してください.while処理機能を実行し続けます.poll()についてreaper対象として,poll()戦略プログラム全体が 繰り返し取引の論理を実行し始めます. ラインについてはSleep(TickInterval)簡単に理解できる,それは,取引論理の回転頻度を制御する目的で,全体的な取引論理の各実行後に休止時間を制御することです.

分析するLeeksReaper()コンストラクター

ほら,どうしたんだLeeksReaper()この関数は戦略論理オブジェクトを構成します

についてLeeksReaper()この関数は空のオブジェクトを宣言して起動します.var self = {}執行中にLeeksReaper()この空のオブジェクトにいくつかのメソッドと属性を徐々に追加し,最終的にこのオブジェクトの構築を完了し,それを返します (つまり,ステップmain()機能の内部でvar reaper = LeeksReaper()返されたオブジェクトは,reaper).

属性を追加するselfオブジェクト 追加しました.selfこれらの属性や変数の目的と意図を迅速に理解し,戦略を理解しやすくし,コードを見るときに混乱を避けることができます.

    self.numTick = 0         # It is used to record the number of transactions not triggered when the poll function is called. When the order is triggered and the order logic is executed, self.numTick is reset to 0
    self.lastTradeId = 0     # The transaction record ID of the order that has been transacted in the transaction market. This variable records the current transaction record ID of the market
    self.vol = 0             # Reference to the trading volume of each market inspection after weighted average calculation (market data is obtained once per loop, which can be interpreted as a time of market inspection)
    self.askPrice = 0        # The bill of lading price of the sales order can be understood as the price of the listing order after the strategy is calculated
    self.bidPrice = 0        # Purchase order bill of lading price
    self.orderBook = {Asks:[], Bids:[]}    # Record the currently obtained order book data, that is, depth data (sell one... sell n, buy one... buy n)
    self.prices = []                       # An array that records the prices on the time series after the calculation of the first three weighted averages in the order book, which means that each time the first three weighted averages of the order book are stored, they are placed in an array and used as a reference for subsequent strategy trading signals, so the variable name is prices, in plural form, indicating a set of prices
    self.tradeOrderId = 0    # Record the order ID after the current bill of lading is placed
    self.p = 0.5             # Position proportion: when the value of currency accounts for exactly half of the total asset value, the value is 0.5, that is, the equilibrium state
    self.account = null      # Record the account asset data, which is returned by the GetAccount() function
    self.preCalc = 0         # Record the timestamp of the last time when the revenue was calculated, in milliseconds, to control the frequency of triggering the execution of the revenue calculation code
    self.preNet = 0          # Record current return values

self オブジェクトにメソッドを追加する

方法を追加し始めます. 方法の追加は,その方法の追加から始まります.selfこのオブジェクトが何らかの作業を行い,いくつかの機能を持つことができます.

"つ目の関数には

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)  # Call the FMZ encapsulated interface GetTrades to obtain the latest market transaction data
        if (self.prices.length == 0) {       # When self.prices.length == 0, the self.prices array needs to be filled with numeric values, which will be triggered only when the strategy starts running
            while (trades.length == 0) {     # If there is no recent transaction record in the market, the while loop will keep executing until the latest transaction data is available and update the trades variable
                trades = trades.concat(_C(exchange.GetTrades))   # concat is a method of JS array type, which is used to concatenate two arrays, here is to concatenate the "trades" array and the array data returned by "_C(exchange.GetTrades)" into one array
            }
            for (var i = 0; i < 15; i++) {   # Fill in data to self.prices, and fill in 15 pieces of latest transaction prices
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {  # _. Reduce function is used for iterative calculation to accumulate the amount of the latest transaction records
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }

機能についてupdateTrades最新の市場取引データを入手し,データに基づいて計算を行い,それを記録し,戦略の後の論理に使用します. 直接上記のコードに書き込みました について_.reduceプログラミングの基礎知識がない人は 混乱するかもしれません._.reduceUnderscore.js ライブラリの関数です. FMZJS 戦略はこのライブラリをサポートしているため,繰り返しの計算に非常に便利です. Underscore.js データリンク (https://underscorejs.net/#reduce)

この意味もとてもシンプルです 例えば:

function main () {
   var arr = [1, 2, 3, 4]
   var sum = _.reduce(arr, function(ret, ele){
       ret += ele
       
       return ret
   }, 0)

   Log("sum:", sum)    # sum = 10
}

配列の各数を足すと,[1, 2, 3, 4]戦略に戻り,取引記録の各取引データの取引量値を合計します.trades最新のトランザクションボリュームの合計を取得self.vol = 0.7 * self.vol + 0.3 * _.reduce (...)この例では...コードを入れ替える.self.volこれは,新たに生成された取引量は合計の30%を占め,最後の重み取引量は70%を占める.この比率は,戦略作成者が人工的に設定され,市場のルールに関連している可能性があります. この問題は戦略設計で考慮されていたので,コードは: 暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータと暗号化されたデータです.

if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
    ...
}

取引記録のトランザクションIDに基づいて判断できます. IDが最後のレコードのIDよりも大きいとき,または交換インターフェイスがIDを提供していない場合,つまり,trade.Id == 0取引記録のタイムスタンプを使用して判断します.self.lastTradeId取引記録のタイムスタンプをIDの代わりに保存します.

2つ目の機能はこう付け加えた:

    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }

この関数について考えてみましょう.updateOrderBook. 関数の名前から,それはオーダーブックを更新するために使用されていることがわかります. しかし,それはオーダーブックのみを更新しません. 関数は FMZ API 関数を呼び始める.GetDepth()現在の市場オーダーブックデータ (1つ売る...nつ売る,1つ買う...nつ買う) を取得し,self.orderBook. 次に,注文簿の購入オーダーと販売オーダーが3未満であるかどうかを判断します. そうであれば,無効関数は直接返されます.

その後,次の2つのデータが計算されます.

· konosament価格を計算する 貨物表価格も,重度の平均方法を用いて計算されます. 購入注文を計算する際には,取引価格に最も近い購入価格に61.8% (0.618) と,取引価格に最も近い販売価格に38.2% (0.382) の重みがあります. 貨物請求書の代価を計算する際には,取引価格に最も近い販売価格に同じ重量を与えられる.なぜなら0.618である場合,著者は黄金部分比率を好む可能性がある.最後の価格 (0.01) については,開口の真ん中にわずかにオフセットする.

オーダーブックの最初の3つのレベルの重度の平均価格をタイムシリーズで更新する. 注文簿内の最初の3つのレベルの購入・販売注文価格については,重度の平均が計算されます.最初のレベルの重量は0.7,第2レベルの重量は0.2,第3レベルの重量は0.1.誰かが"ああ,いや,コードには0.7,0.2,0.1がある"と言うかもしれません. 計算を拡大してみましょう.

(Buy one+Sell one) * 0.35+(Buy two+Sell two) * 0.1+(Buy three+Sell three) * 0.05
->
(Buy one+sell one)/2 * 2 * 0.35+(Buy two+sell two)/2 * 2 * 0.1+(Buy three+sell three)/2 * 2 * 0.05
->
(Buy one+sell one)/2 * 0.7+(Buy two+sell two)/2 * 0.2+(Buy three+sell three)/2 * 0.1
->
Average price of the first level * 0.7+average price of the second level * 0.2+average price of the third level * 0.1

ここで見られるように,最終的な計算価格は,実際には現在の市場における第3の開口の真ん中の価格位置への反応です. この計算値を使用して配列を更新します.self.prices古いデータの一つを蹴り出す (shift()機能) を更新し,最新のデータの一つを更新します.push()JS言語の配列オブジェクトのメソッドである.詳細についてはJSデータをチェックできます).self.pricesタイムシリアル順序のデータストリームです.

休憩して 次の号で会おう~


関連性

もっと