最近,熱い議論が起こっています.print money
FMZ 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()
キーワード:
· 戦略の第一線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.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
プログラミングの基礎知識がない人は 混乱するかもしれません._.reduce
Underscore.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
タイムシリアル順序のデータストリームです.
休憩して 次の号で会おう~