রিসোর্স লোড হচ্ছে... লোডিং...

লিক্স রিপার কৌশল বিশ্লেষণ (১)

লেখক:এফএমজেড-লিডিয়া, সৃষ্টিঃ ২০২২-১১-০৪ 15:42:26, আপডেটঃ ২০২৩-০৯-১৫ 21:08:48

img

লিক্স রিপার কৌশল বিশ্লেষণ (১)

সাম্প্রতিককালে, এই বিষয়ে একটি উত্তপ্ত বিতর্ক চলছে।print moneyএফএমজেড কোয়ান্টের ওয়েচ্যাট গ্রুপের একটি রোবট। রোবট ট্রেডিং নীতিprint moneyআমি মূল কৌশলটি পুনরায় মনোযোগ দিয়ে পড়েছি এবং FMZ Quant-এ 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 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()ফাংশন খুব সহজেই বোঝা যায়, এটি poresreaper কৌশল লজিক মডিউল (একটি বস্তু) এর নির্মাতা হিসাবে বোঝা যায়। সংক্ষেপে,LeeksReaper()এটি একটি লিকস রিপার ট্রেডিং লজিক তৈরির জন্য দায়ী।

কীওয়ার্ডঃ

img

· কৌশল প্রথম লাইনmainফাংশনঃvar reaper = LeeksReaper(), কোড একটি স্থানীয় পরিবর্তনশীল ঘোষণা করেreaperএবং তারপর LeeksReaper ((() ফাংশন কল একটি কৌশল লজিক অবজেক্ট যে একটি মান নির্ধারণ করে নির্মাণ করতেreaper.

পরিকল্পনার পরবর্তী পদক্ষেপmainফাংশনঃ

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

একটি লিখুন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, যে কেউ কোন প্রোগ্রামিং মৌলিক শিক্ষা আছে বিভ্রান্ত হতে পারে._.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)) {
    ...
}

বিচার. এটি লেনদেন রেকর্ডে লেনদেনের আইডি ভিত্তিতে বিচার করা যেতে পারে। যখন আইডি শেষ রেকর্ডের আইডি থেকে বড় হয় বা যদি এক্সচেঞ্জ ইন্টারফেস একটি আইডি সরবরাহ না করে, অর্থাৎ,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))
    }

পরবর্তী, চলুন ফাংশন তাকানupdateOrderBook. ফাংশনের নাম থেকে, আমরা দেখতে পাচ্ছি যে এটি অর্ডার বুক আপডেট করতে ব্যবহৃত হয়। তবে এটি কেবল অর্ডার বুক আপডেট করে না। ফাংশনটি এফএমজেড এপিআই ফাংশনটি কল করতে শুরু করেGetDepth()বর্তমান বাজার অর্ডার বুকের তথ্য (একটি বিক্রি করুন... n বিক্রি করুন, একটি কিনুন... n কিনুন) পেতে এবং অর্ডার বুকের তথ্য রেকর্ড করতেself.orderBook. পরবর্তী, বিচার যদি অর্ডার বইয়ের ক্রয় আদেশ এবং বিক্রয় আদেশ তথ্য 3 কম হয়, যদি তাই হয়, অবৈধ ফাংশন সরাসরি ফেরত দেওয়া হবে.

এর পরে, দুটি তথ্য গণনা করা হয়ঃ

· চালানপত্রের মূল্য গণনা করুন চালান মূল্যও ওজনযুক্ত গড় পদ্ধতি ব্যবহার করে গণনা করা হয়। ক্রয় আদেশ গণনা করার সময়, লেনদেনের দামের নিকটতম ক্রয় মূল্যের ওজন 61.8% (0.618) এবং লেনদেনের দামের নিকটতম বিক্রয় মূল্যের ওজন 38.2% (0.382) বিল অফ কন্ট্রাকশন বিল অফ বিক্রয় মূল্য গণনা করার সময়, লেনদেনের দামের নিকটতম বিক্রয় মূল্যকে একই ওজন দেওয়া হয়। কেন 0.618, এটি হতে পারে যে লেখক সোনার বিভাগ অনুপাত পছন্দ করেন। শেষ দামের জন্য (0.01) এটি খোলার কেন্দ্রে সামান্য অফসেট করা হয়।

সময় সিরিজের অর্ডার বইয়ের প্রথম তিন স্তরের ওজনযুক্ত গড় মূল্য আপডেট করুন অর্ডার বইয়ের প্রথম তিনটি স্তরের ক্রয় এবং বিক্রয় অর্ডার মূল্যের জন্য, ওজনযুক্ত গড় গণনা করা হয়। প্রথম স্তরের ওজন 0.7, দ্বিতীয় স্তরের ওজন 0.2, এবং তৃতীয় স্তরের ওজন 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

যেমনটি আমরা এখানে দেখতে পাচ্ছি, চূড়ান্ত হিসাবকৃত মূল্য আসলে বর্তমান বাজারে তৃতীয় খোলার মাঝামাঝি দামের অবস্থানের প্রতিক্রিয়া। তারপর অ্যারে আপডেট করার জন্য এই গণনা মূল্য ব্যবহার করুনself.prices, প্রাচীনতম তথ্যের একটি (মাধ্যমে) আউট kickingshift()(উপলব্ধ) এবং এটিতে সর্বশেষতম তথ্যগুলির একটি আপডেট করা ((push()ফাংশন, স্থানান্তর এবং ধাক্কা ফাংশন JS ভাষা অ্যারে অবজেক্টের পদ্ধতি, আপনি বিস্তারিত জানার জন্য JS তথ্য পরীক্ষা করতে পারেন) । এইভাবে অ্যারে গঠনself.prices, যা একটি টাইম সিরিজ অর্ডার সহ ডেটা স্ট্রিম।

তাই আসুন এখানে একটি বিশ্রাম আছে, এবং আমরা আপনাকে পরবর্তী সংখ্যা দেখতে ~


সম্পর্কিত

আরো