मुनाफा कटाई करने वाली रणनीति का विश्लेषण (1)

लेखक:निनाबादास, बनाया गयाः 2022-04-26 11:31:51, अद्यतन किया गयाः

मुनाफा कटाई करने वाली रणनीति का विश्लेषण (1)

हाल ही में, FMZ Quant WeChat समूह के उपयोगकर्ताओं ने बॉट के बारे में चर्चा कीprint moneyयह चर्चा बहुत गर्म थी, और एक बहुत पुरानी रणनीति क्वांट्स की दृष्टि में फिर से प्रवेश कर गई हैःलाभ कटाई करनेवाला.. बॉट ट्रेडिंग का सिद्धांतprint moneyलाभ कटाई रणनीति पर आधारित है. मैं अपने आप को उस समय लाभ कटाई रणनीति बहुत अच्छी तरह से समझ में नहीं आया के लिए दोषी. तो, मैं मूल रणनीति फिर से गंभीरता से पढ़ा, और यह भी पोर्ट संस्करण पढ़ाःओकेकोइन लाभ कटाई करने वाला पोर्टिंग. उदाहरण के तौर पर एफएमजेड की लाभ कटाई रणनीति का पोर्ट किया हुआ संस्करण लेते हुए, हम इस रणनीति का विश्लेषण करने जा रहे हैं और रणनीति के विचारों को खोदने जा रहे हैं, ताकि हमारे प्लेटफॉर्म उपयोगकर्ता इस रणनीति के विचार को सीख सकें। इस लेख में, हम प्रोग्रामिंग से संबंधित उबाऊ सामग्री को कम करने के लिए रणनीतिक सोच, इरादे आदि के स्तर से अधिक विश्लेषण करते हैं।

[पोर्ट OKCoin लाभ कटाई] रणनीति स्रोत कोडः

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 to 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 to 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()लाभ कटाई करने वाले के व्यापारिक तर्क का निर्माण करने के लिए जिम्मेदार है।

  • पहली पंक्तिmainरणनीति में भूमिकाःvar reaper = LeeksReaper(); कोड एक स्थानीय चर घोषित करता हैreaper, और फिर एक रणनीति तर्क वस्तु का निर्माण करने के लिए LeeksReaper() फ़ंक्शन को कॉल करता है और इसे असाइन करता हैreaper.

  • निम्नलिखित भाग मेंmainकार्य:

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

    एक दर्ज करेंwhileअनंत लूप, लगातार प्रसंस्करण समारोह को निष्पादितreaperवस्तुpoll(), औरpoll()कार्य रणनीति का मुख्य तर्क है, तो पूरी रणनीति कार्यक्रम लगातार व्यापार तर्क को निष्पादित करने के लिए शुरू होता है।

    और लाइन के लिए के रूप मेंSleep(TickInterval), यह समझने में आसान है, जिसका उद्देश्य समग्र ट्रेडिंग लॉजिक के प्रत्येक निष्पादन के बाद विराम समय को नियंत्रित करना है, और जिसका उद्देश्य ट्रेडिंग लॉजिक की रोटेशन आवृत्ति को नियंत्रित करना है।

निर्माता का विश्लेषण करेंLeeksReaper()

चलो देखते हैं कि कैसे समारोहLeeksReaper()रणनीति तर्क में एक वस्तु का निर्माण करता है।

..LeeksReaper()फ़ंक्शन शुरू होता है और एक शून्य वस्तु घोषित करता है,var self = {}कार्यवाही के दौरानLeeksReaper()अंत में, ऑब्जेक्ट का निर्माण पूरा हो जाता है, और ऑब्जेक्ट लौटाया जाता है (यानी, ऑब्जेक्ट को वापस किया जाता है) ।var reaper = LeeksReaper()मेंmain()फ़ंक्शन, और लौटाया वस्तु के लिए सौंपा जाता हैreaper).

ऑब्जेक्ट में विशेषता जोड़ेंself

इसके बाद, मैं करने के लिए गुणों की एक बहुत जोड़ाself. मैं नीचे प्रत्येक विशेषता का वर्णन करेंगे. आप जल्दी से इन विशेषताओं और चर का उद्देश्य और इरादा समझ सकते हैं, जो यह रणनीति को समझने के लिए आसान बना देगा, और भ्रमित होने से बचने जब आप कोड के ढेर को देखते हैं.

    self.numTick = 0         # it is used to record the number of times that the trading is not triggered when the poll function is called. When placing an order is triggered and the order logic is executed, reset self.numTick to 0
    self.lastTradeId = 0     # the trading record ID of the order that has been executed in the trading market; this variable records the current latest execution record ID in the market
    self.vol = 0             # after the weighted average calculation, the trading volume reference of the market at each inspection (the market data is obtained once per time of the loop, which can be understood as the inspection of the market once)
    self.askPrice = 0        # ask price of delivery order, which can be understood as the price of the pending sell order calculated by the strategy 
    self.bidPrice = 0        # bid price of delivery order
    self.orderBook = {Asks:[], Bids:[]}    # record the currently obtained order book data, that is, depth data (sell 1...sell n, buy 1...buy n)
    self.prices = []                       # an array that records the price in the time series after the weighted average calculation of the first three levels in the order book. Simply put, it is the weighted average price of the first three levels of the order book obtained by storing each time, and put them in an array as reference of the subsequent strategic trading signals. Therefore, the variable name is "prices", plural, which means a set of prices
    self.tradeOrderId = 0    # record the order ID after currently lading and ordering 
    self.p = 0.5             # position proportion; when the currency value is exactly half of the total asset value, the value of it is 0.5, which means the balanced state 
    self.account = null      # record the account asset data, which will be returned by the function GetAccount()
    self.preCalc = 0         # record the timestamp when calculating the return of the latest time, in milliseconds, which is used to control the frequency of triggering execution of the return calculation code
    self.preNet = 0          # record the current return value  

ऑब्जेक्ट में विधि जोड़ेंself

self के लिए इन विशेषताओं को जोड़ने के बाद, विधि जोड़ने के लिए शुरूselfऑब्जेक्ट, ताकि यह ऑब्जेक्ट कुछ ऑपरेशन कर सकता है और कुछ कार्य कर सकता है।

पहला जोड़ा गया फ़ंक्शनः

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)  # call the encapsulated interface "GetTrades" of FMZ, to obtain the currently latest execution data in the market
        if (self.prices.length == 0) {       # when self.prices.length == 0, you need to fill values in the array of self.prices, which is only triggered when the strategy is started 
            while (trades.length == 0) {     # if there is no latest execution record in the market, the while loop will run infinitely, until there is new execution data; the, update the variable "trades"
                trades = trades.concat(_C(exchange.GetTrades))   # "concat" is a method of JS array type, which is used to match two arrays; here we use it to match the array "trades" and the array returned by "_C(exchange.GetTrades)" into one array 
            }
            for (var i = 0; i < 15; i++) {   # fill in values for "self.prices"; fill 15 latest execution prices 
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {  # _.reduce function iteratively calculates the accumulated execution volume of the latest execution record 
            // 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 का सूचना लिंक

उदाहरण के लिए, समझने में बहुत आसानः

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

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

यह सरणी में प्रत्येक संख्या जोड़ रहा है, अर्थात्[1, 2, 3, 4]. हमारी रणनीति के लिए वापस, यह में प्रत्येक निष्पादन रिकॉर्ड डेटा के वॉल्यूम मूल्य जोड़ने के लिए हैtradesarray, जिसके परिणामस्वरूप नवीनतम निष्पादन वॉल्यूम का कुल होता है।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)) {
    ...
}

निर्णय ट्रेडिंग निष्पादन रिकॉर्ड में निष्पादन आईडी पर आधारित हो सकता है. केवल जब आईडी पिछली बार की आईडी से बड़ी है, संचय ट्रिगर किया जाता है. या, अगर मंच इंटरफ़ेस एक आईडी प्रदान नहीं करता है, यानी, यदि कोई आईडी नहीं है, तो यह एक अलग पहचान है।trade.Id == 0, व्यापार निष्पादन रिकॉर्ड में समय टिकट का उपयोग करने का न्याय करने के लिए। इस समय, क्या द्वारा संग्रहीत हैself.lastTradeIdनिष्पादन रिकॉर्ड का टाइमस्टैम्प है, आईडी नहीं।

दूसरा जोड़ा गया कार्यः

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

अगला, आइए देखें किupdateOrderBookfunction. function के नाम के अर्थ से, यह देखा जा सकता है कि function order book को update करना है. हालांकि, यह केवल order book को update नहीं करता है. function, सबसे पहले FMZ API function को कॉल करता है.GetDepth()वर्तमान बाजार ऑर्डर बुक डेटा (बिक्री 1...बिक्री n, खरीद 1...खरीद n) प्राप्त करना और ऑर्डर बुक डेटा को रिकॉर्ड करनाin self.orderBookइसके बाद, यह आकलन करता है कि क्या खरीद आदेशों और बिक्री आदेशों के लिए ऑर्डर बुक डेटा का स्तर 3 स्तर से कम है; यदि हां, तो इसे अमान्य फ़ंक्शन माना जाता है, और सीधे लौटाता है।

इसके बाद दो डेटा गणनाएं की गईं:

  • वितरण आदेश की कीमत की गणना करें वितरण मूल्य की गणना भी भारित औसत गणना पर आधारित है। खरीद आदेश की बोली मूल्य की गणना करते समय, खरीद 1 मूल्य को 61.8% (0.618) अधिक वजन दें, और बेच 1 मूल्य शेष 38.2% (0.382) वजन के लिए जिम्मेदार है। वितरण आदेश की पूछ मूल्य की गणना करते समय भी यही सच है, बिक्री 1 मूल्य को अधिक वजन देते हुए। यह 0.618 क्यों है, यह हो सकता है कि डेवलपर स्वर्ण अनुपात पसंद करता है। अंत में जोड़ा या घटाया गया मूल्य (0.01) के लिए, यह बाजार के केंद्र के लिए थोड़ा और ऑफसेट करने के लिए है।

  • समय श्रृंखला पर ऑर्डर बुक के पहले तीन स्तरों की भारित औसत कीमत को अद्यतन करें ऑर्डर बुक में पहले तीन स्तर के खरीद ऑर्डर और बिक्री ऑर्डर के लिए, एक भारित औसत गणना की जाती है। पहले स्तर का वजन 0.7 है, दूसरे स्तर का वजन 0.2 है, और तीसरे स्तर का वजन 0.1 है। कुछ छात्र कह सकते हैंः ओह, गलत, कोड में 0.7, 0.2 या 0.1 नहीं है!

    आइए विस्तारित गणना देखें:

    (Buy 1 + Sell 1) * 0.35 + (Buy 2 + Sell 2) * 0.1 + (Buy 3 + Sell 3) * 0.05
    ->
    (Buy 1 + Sell 1) / 2 * 2 * 0.35 + (Buy 2 + Sell 2) / 2 * 2 * 0.1 + (Buy 3 + Sell 3) / 2 * 2 * 0.05
    ->
    (Buy 1 + Sell 1) / 2 * 0.7 + (Buy 2 + Sell 2) / 2 * 0.2 + (Buy 3 + Sell 3) / 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
    

    यह देखा जा सकता है कि अंतिम गणना की गई कीमत वास्तव में वर्तमान बाजार में तीसरे स्तर की औसत मूल्य स्थिति को दर्शाती है। फिर इस गणना की गई कीमत का उपयोग करेंself.pricesसरणी, सबसे पुराने डेटा बाहर लात मारो (द्वाराshift()कार्य) और इसे नवीनतम आंकड़ों के साथ अद्यतन करें।push()फ़ंक्शन; shift और push फ़ंक्शन JS सरणी वस्तुओं के सभी तरीके हैं, आप अधिक विवरण के लिए JS संबंधित सामग्री खोज सकते हैं) इस प्रकार उत्पन्न सरणीself.pricesसमय श्रृंखला क्रम में एक डेटा प्रवाह है।

    एक ब्रेक ले लो. चलो यहाँ विश्लेषण समाप्त करते हैं, और अगली बार मिलते हैं!


अधिक