Phân tích chiến lược máy thu hoạch cải bắp (1)

Tác giả:Giấc mơ nhỏ, Tạo: 2020-11-12 22:11:32, Cập nhật: 2023-09-26 21:04:43

img

Phân tích chiến lược máy thu hoạch cải bắp

Các nhà phát minh gần đây đã định lượng cuộc thảo luận trong nhóm WeChatprint moneyMột chiến thuật rất cũ đã trở lại tầm nhìn của những người rộng rãi:Máy thu hoạch cải xoănprint moneyCác quy tắc giao dịch của robot dựa trên chiến lược của máy thu hoạch sò, tự trách mình rằng lúc đó họ không hiểu rõ về chiến lược của máy thu hoạch sò. Vì vậy, một lần nữa xem xét nghiêm túc chiến lược gốc và xem lại phiên bản chuyển thể được định lượng bởi nhà phát minh.Tải OKCoin vào máy thu hoạch hạt cải◄ Các nhà phát minh đã định lượng chiến lược của máy thu hoạch cải bắp phiên bản chuyển thể của nền tảng, phân tích chiến lược đó và khai thác ý tưởng của chiến lược đó để người dùng nền tảng có thể học được ý tưởng này. Trong bài viết này, chúng tôi phân tích nhiều hơn về chiến lược, ý định và các khía cạnh khác, cố gắng giảm thiểu nội dung chán liên quan đến lập trình.

[Transport OKCoin to Cabbage Harvester] Chiến lược nguồn:

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("开始平衡", 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("开始平衡", 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)
    }
}

Chiến lược tổng quan

Thông thường, khi học một chính sách, khi đọc, trước tiên hãy xem xét cấu trúc chương trình tổng thể. Mã chính sách này không nhiều, chỉ có dưới 200 dòng mã, rất mỏng, và so với phiên bản gốc, mã chính sách hoàn thiện rất cao, về cơ bản là tương tự.main()Các hàm bắt đầu thực hiện, tất cả các mã chính sách, ngoại trừmain()Có một tên gọi làLeeksReaper()Và chúng ta có thể tính toánLeeksReaper()Các hàm cũng được hiểu rất tốt, hàm này có thể được hiểu như là một hàm cấu trúc của mô-đun logic chiến lược của máy thu hoạch cải (một đối tượng), đơn giản làLeeksReaper()Trong khi đó, các nhà phân tích khác cũng cho biết, một người phụ nữ có trách nhiệm xây dựng logic giao dịch của một máy thu hoạch hạt cải.

Từ khóa:img img

  • Chiến lượcmainDòng đầu tiên của hàm:var reaper = LeeksReaper()Có thể là một phần của một phần của một phần của một phần.reaper, sau đó gọi hàm LeeksReaper (), tạo ra một đối tượng logic chính sách, gán giá trị choreaper

  • Chiến lượcmainChức năng tiếp theo:

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

    vào mộtwhileChuỗi chết, không ngừng thi hànhreaperChức năng xử lý đối tượngpoll()poll()Chức năng chính là logic chính của chiến lược giao dịch, và toàn bộ quá trình chiến lược bắt đầu với logic thực hiện giao dịch liên tục. Còn vềSleep(TickInterval)Điều này được hiểu là để kiểm soát thời gian tạm dừng sau mỗi lần thực hiện logic giao dịch tổng thể, mục đích là để kiểm soát tần suất quay của logic giao dịch.

Phân tíchLeeksReaper()Xây dựng hàm

Nhìn kìa.LeeksReaper()Các hàm xây dựng một đối tượng logic chiến lược như thế nào?

LeeksReaper()Các hàm bắt đầu bằng cách tuyên bố một đối tượng trống.var self = {},LeeksReaper()Trong quá trình thực hiện hàm sẽ dần dần thêm một số phương pháp, thuộc tính vào đối tượng trống này, cuối cùng hoàn thành việc xây dựng đối tượng này và cuối cùng trả lại đối tượng này (tức làmain()Trong hàmvar reaper = LeeksReaper()Bước này, các đối tượng được trả về đã được gánreaper)。

Cho tôiselfThêm thuộc tính đối tượng

Sau đó làselfCó rất nhiều thuộc tính được thêm vào, và dưới đây tôi sẽ mô tả từng thuộc tính để có thể nhanh chóng hiểu được các thuộc tính, mục đích của biến, ý định, chiến lược dễ hiểu, tránh thấy bộ mã này trong đám mây bao quanh.

    self.numTick = 0         # 用来记录poll函数调用时未触发交易的次数,当触发下单并且下单逻辑执行完时,self.numTick重置为0
    self.lastTradeId = 0     # 交易市场已经成交的订单交易记录ID,这个变量记录市场当前最新的成交记录ID
    self.vol = 0             # 通过加权平均计算之后的市场每次考察时成交量参考(每次循环获取一次市场行情数据,可以理解为考察了行情一次)
    self.askPrice = 0        # 卖单提单价格,可以理解为策略通过计算后将要挂卖单的价格
    self.bidPrice = 0        # 买单提单价格
    self.orderBook = {Asks:[], Bids:[]}    # 记录当前获取的订单薄数据,即深度数据(卖一...卖n,买一...买n)
    self.prices = []                       # 一个数组,记录订单薄中前三档加权平均计算之后的时间序列上的价格,简单说就是每次储存计算得到的订单薄前三档加权平均价格,放在一个数组中,用于后续策略交易信号参考,所以该变量名是prices,复数形式,表示一组价格
    self.tradeOrderId = 0    # 记录当前提单下单后的订单ID
    self.p = 0.5             # 仓位比重,币的价值正好占总资产价值的一半时,该值为0.5,即平衡状态
    self.account = null      # 记录账户资产数据,由GetAccount()函数返回数据
    self.preCalc = 0         # 记录最近一次计算收益时的时间戳,单位毫秒,用于控制收益计算部分代码触发执行的频率
    self.preNet = 0          # 记录当前收益数值

Cho tôiselfPhương pháp thêm đối tượng

Sau khi thêm các thuộc tính này vào self, chúng ta bắt đầu choselfMột đối tượng được thêm phương pháp để cho phép đối tượng này thực hiện một số công việc và có một số chức năng.

Chức năng đầu tiên được thêm vào:

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)  # 调用FMZ封装的接口GetTrades,获取当前最新的市场成交数据
        if (self.prices.length == 0) {       # 当self.prices.length == 0时,需要给self.prices数组填充数值,只有策略启动运行时才会触发
            while (trades.length == 0) {     # 如果近期市场上没有更新的成交记录,这个while循环会一直执行,直到有最新成交数据,更新trades变量
                trades = trades.concat(_C(exchange.GetTrades))   # concat 是JS数组类型的一个方法,用来拼接两个数组,这里就是把“trades”数组和“_C(exchange.GetTrades)”返回的数组数据拼接成一个数组
            }
            for (var i = 0; i < 15; i++) {   # 给self.prices填充数据,填充15个最新成交价格
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {  # _.reduce 函数迭代计算,累计最新成交记录的成交量
            // 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)

    }

updateTradesNhiệm vụ của hàm này là lấy dữ liệu giao dịch thị trường mới nhất, và thực hiện một số tính toán và ghi lại dựa trên dữ liệu đó, cung cấp cho chiến lược sử dụng trong logic sau đó. Tôi đã viết trực tiếp trong mã trên. Đối với_.reduceCó thể những người không có kiến thức về lập trình sẽ bối rối, nhưng hãy nói một cách đơn giản:_.reducePhải.Underscore.jsCác hàm trong thư viện này, các chính sách FMZJS hỗ trợ thư viện này, vì vậy nó rất thuận tiện để sử dụng tính toán lặp đi lặp lại.Underscore.js资料链接

Có thể nói rằng: "Đây là một cách đơn giản để làm điều này".

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
}

Và chúng ta có thể tính toán[1, 2, 3, 4]Chúng ta sẽ tính toán các con số trong số đó.tradesMỗi dữ liệu đăng ký giao dịch trong số liệu được cộng lại với số lượng giao dịch được đăng ký.self.vol = 0.7 * self.vol + 0.3 * _.reduce(...)Xin phép tôi dùng...Thay vào đó là một đống mã.self.volTính toán này cũng được tính là trung bình trọng số; nghĩa là tổng giao dịch được tạo ra gần đây chiếm 30% trọng số, so với giao dịch được tính trọng số lần trước chiếm 70% trọng số. Tỷ lệ này được thiết lập bởi tác giả chiến lược và có thể liên quan đến việc quan sát các quy luật thị trường. Và nếu bạn hỏi tôi, nếu giao diện lấy dữ liệu giao dịch gần đây trả lại cho tôi dữ liệu cũ lặp đi lặp lại, dữ liệu mà tôi lấy ra là sai và có ý nghĩa không?

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

Phân tích chỉ được kích hoạt khi ID lớn hơn ID ghi trước đó hoặc nếu giao dịch không cung cấp ID.trade.Id == 0Trong khi đó, các công ty khác cũng có thể tham gia vào các hoạt động này.self.lastTradeIdTrong khi đó, các thông tin khác cũng được lưu trữ.

Chức năng thứ hai được thêm:

    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))
    }

Xem tiếp nhé.updateOrderBookChức năng này, theo nghĩa đen của tên của hàm, chức năng này có vai trò là cập nhật lệnh thin.GetDepth()Nhận dữ liệu thâm đơn hàng thị trường hiện tại (bán một... bán n, mua một... mua n) và ghi lại dữ liệu thâm đơn hàng trênself.orderBookTrong đó, nếu đơn đặt hàng ít dữ liệu, đơn đặt hàng ít hơn 3 mục, thì sẽ trả lại hàm vô hiệu trực tiếp.

Sau đó, hai số liệu được tính toán:

  • Tính toán giá đặt hàng Giá thanh toán cũng được tính bằng cách sử dụng đường cân trung bình, khi tính toán thanh toán, quyền mua là 61.8% ((0.618), quyền bán chiếm 38.2% ((0.382)). Khi tính toán giá bán đơn hàng cũng như vậy, cho và bán một quyền giá lớn hơn. Vì sao là 0.618, có thể là tác giả thích tỷ lệ chia vàng hơn. Đối với giá điểm cuối cùng cộng thêm và giảm một điểm ((0.01) là để di chuyển một chút về trung tâm chính của bảng xếp hạng.

  • Cập nhật thời gian theo thứ tự đặt hàng mỏng ba hàng đầu giá trung bình tăng cân Đối với các đơn đặt hàng nhỏ, giá bán đơn được tính theo đường cân bằng, trọng lượng hạng nhất là 0,7, trọng lượng hạng hai là 0,2, trọng lượng hạng ba là 0,1. Có thể có một số học sinh sẽ nói: Ồ, không đúng, trong mã có gỗ 0,7, 0,2, 0,1. Chúng ta hãy bắt đầu tính toán:

    (买一 + 卖一) * 0.35 + (买二 + 卖二) * 0.1 + (买三 + 卖三) * 0.05
    ->
    (买一 + 卖一) / 2 * 2 * 0.35 + (买二 + 卖二) / 2 * 2 * 0.1 + (买三 + 卖三) / 2 * 2 * 0.05
    ->
    (买一 + 卖一) / 2 * 0.7 + (买二 + 卖二) / 2 * 0.2 + (买三 + 卖三) / 2 * 0.1
    ->
    第一档平均的价格 * 0.7 + 第二档平均的价格 * 0.2 + 第三档平均的价格 * 0.1
    

    Ở đây, bạn có thể thấy rằng giá cuối cùng được tính toán thực sự là vị trí giá của các vị trí trung gian ba tầng trên thị trường trước đó. Và sau đó sử dụng giá tính toán này để cập nhật.self.pricesĐặt một số dữ liệu cũ nhất quashift()Chức năng), cập nhật vào một dữ liệu mới nhất ((thông quapush()Các hàm, shift, và push là các phương thức đối tượng toán học của ngôn ngữ JS, cụ thể là có thể truy vấn thông tin của JS).self.pricesMột mảng là một luồng dữ liệu theo thứ tự thời gian.

Cough, cough, drink mouth water, dissect here first, we'll see you next time. (tạm dịch: Tôi sẽ ho, ho, uống nước miệng, phân tích ở đây trước)


Thêm nữa

Tôi dần dần trở nên mạnh mẽChào bạn, tôi muốn hỏi bạn.self.prices 15 giá giao dịch lịch sử được điền trước, sau đó điền vào giá trung bình trọng số của 3 bảng trước khi đặt hàng.

snlnppTôi muốn khen ngợi giấc mơ của mình.

m0606Thật không may, nhiều sàn giao dịch đã giảm giá mua bán để chỉ còn lại một dấu chấm, khiến cho chiến lược này không có ý nghĩa khi cố gắng đưa vào giao dịch mua bán giữa.

mẹ ơiCảm ơn bạn, tôi đã viết một phiên bản của python chạy trên đồng xu, thật là một máy thu hoạch phí giấy tờ.

bwxiaokThật tuyệt vời, không có lời giải thích về giấc mơ, tôi thực sự không thể hiểu đầy đủ, cảm ơn sự kiên nhẫn của bạn!

Eddie.Phân chia vàng 0.618 0.382 được sử dụng trong Fibonacci Đồ bò mơ ước

Lee-hee-heeĐúng là một con bò, rất chi tiết

Evan1987Xin cảm ơn bạn đã chia sẻ câu trả lời chi tiết như vậy.

làm raƯớc mơ tổng cộng

chiyimjjcnĐúng vậy, chúng tôi đã làm điều đó. Trong khi đó, một số người cho rằng, "Đó là một sự kiện vô cùng phức tạp".

Chín mặt trờiĐúng vậy, đó là một điều đáng kinh ngạc.

Giấc mơ nhỏCó, có.

Giấc mơ nhỏTrong khi đó, các nhà đầu tư khác cũng có thể tham gia vào các hoạt động này.

Giấc mơ nhỏTrong khi đó, một số người cho rằng chiến lược cao tần số cần một số hỗ trợ.

Giấc mơ nhỏCảm ơn bạn đã ủng hộ. Nếu bạn thích, hãy chia sẻ nhé.

Giấc mơ nhỏCảm ơn sự ủng hộ của bạn!

Giấc mơ nhỏCảm ơn sự ủng hộ của bạn!

Giấc mơ nhỏCảm ơn sự ủng hộ của bạn!

Giấc mơ nhỏVào thời điểm đi học, tôi nhớ rõ tỷ lệ phân chia vàng này, và nói rằng hình vuông dài rộng này là hình vuông đẹp nhất~~ nhưng tôi không biết tại sao~~~~~~

Giấc mơ nhỏXin cảm ơn sự ủng hộ của bạn.

Giấc mơ nhỏTrong thực tế, không phức tạp, phần này của chú thích khá khó hiểu, từng dòng đều được mô tả theo cách dễ hiểu nhất.