2015-09-26 18 views
3

我最近編寫了一個程序,用於從股市中返回一堆不健康的股票。基本的算法是這樣的:Ruby:Decorator模式減緩了很多簡單的程序

  1. 查找每一個股票的所有報價在交換(無論是紐約證券交易所或納斯達克)

  2. 查找與步驟低於5美元的人1

  3. 找到步驟2中3天內有貨量較大(價格昂貴,因爲我必須爲每隻股票提出要求,這與目前納斯達克市場目前的約700點相同)。

  4. 掃描一步3.

我有這一切都在一個文件中返回的那些消息:

原執行(https://github.com/EdmundMai/minion/blob/aa14bc3234a4953e7273ec502276c6f0073b459d/lib/minion.rb):

require 'bundler/setup' 
require "minion/version" 
require "yahoo-finance" 
require "business_time" 
require 'nokogiri' 
require 'open-uri' 

module Minion 
    class << self 
    def query(exchange) 
     client = YahooFinance::Client.new 
     all_companies = CSV.read("#{exchange}.csv") 

     small_caps = [] 

     ticker_symbols = all_companies.map { |row| row[0] } 
     ticker_symbols.each_slice(200) do |batch| 
     data = client.quotes(batch, [:symbol, :last_trade_price, :average_daily_volume]) 
     small_caps << data.select { |stock| stock.last_trade_price.to_f < 5.0 } 
     end 

     attractive = [] 

     small_caps.flatten!.each_with_index do |small_cap, index| 
     begin 
      data = client.historical_quotes(small_cap.symbol, { start_date: 2.business_days.ago, end_date: Time.now }) 
      closing_prices = data.map(&:close).map(&:to_f) 
      volumes = data.map(&:volume).map(&:to_i) 

      negative_3_days_in_a_row = closing_prices == closing_prices.sort 
      larger_than_average_volume = volumes.reduce(:+)/volumes.count > small_cap.average_daily_volume.to_i 

      if negative_3_days_in_a_row && larger_than_average_volume 
      attractive << small_cap.symbol 
      puts "Qualified: #{small_cap.symbol}, finished with #{index} out of #{small_caps.count}" 
      else 
      puts "Not qualified: #{small_cap.symbol}, finished with #{index} out of #{small_caps.count}" 
      end 
     rescue => e 
      puts e.inspect 
     end 
     end 

     final_results = [] 

     attractive.each do |symbol| 
     rss_feed = Nokogiri::HTML(open("http://feeds.finance.yahoo.com/rss/2.0/headline?s=#{symbol}&region=US&lang=en-US")) 
     html_body = rss_feed.css('body')[0].text 
     diluting = false 
     ['warrant', 'cashless exercise'].each do |keyword| 
      diluting = true if html_body.match(/#{keyword}/i) 
     end 
     final_results << symbol if diluting 
     end 

     final_results 
    end 
    end 
end 

這是真的很快,並會在一分鐘或更短的時間內完成像〜700股的處理。

然後,我嘗試重構並將算法拆分成不同的類和文件,而根本不改變算法。我決定使用裝飾模式,因爲它似乎適合。但是,當我現在運行程序時,它會使每個請求真的很慢(15分鐘以上)。我知道這是因爲我的puts報表打印出來非常慢。

新的和更慢的執行(https://github.com/EdmundMai/minion/blob/master/lib/minion.rb

require 'bundler/setup' 
require "minion/version" 
require "yahoo-finance" 
require "minion/dilution_finder" 
require "minion/negative_finder" 
require "minion/small_cap_finder" 
require "minion/market_fetcher" 

module Minion 
    class << self 
    def query(exchange) 
     all_companies = CSV.read("#{exchange}.csv") 
     all_tickers = all_companies.map { |row| row[0] } 

     short_finder = DilutionFinder.new(NegativeFinder.new(SmallCapFinder.new(MarketFetcher.new(all_tickers)))) 
     short_finder.results 
    end 
    end 
end 

的部分它落後於根據我puts

require "yahoo-finance" 
require "business_time" 
require_relative "stock_finder" 

class NegativeFinder < StockFinder 
    def results 
    client = YahooFinance::Client.new 
    results = [] 
    finder.results.each_with_index do |stock, index| 
     begin 
     data = client.historical_quotes(stock.symbol, { start_date: 2.business_days.ago, end_date: Time.now }) 
     closing_prices = data.map(&:close).map(&:to_f) 
     volumes = data.map(&:volume).map(&:to_i) 

     negative_3_days_in_a_row = closing_prices == closing_prices.sort 
     larger_than_average_volume = volumes.reduce(:+)/volumes.count > stock.average_daily_volume.to_i 

     if negative_3_days_in_a_row && larger_than_average_volume 
      results << stock 
      puts "Qualified: #{stock.symbol}, finished with #{index} out of #{finder.results.count}" 
     else 
      puts "Not qualified: #{stock.symbol}, finished with #{index} out of #{finder.results.count}" 
     end 
     rescue => e 
     puts e.inspect 
     end 
    end 
    results 
    end 
end 

它落後第3步(使每隻股票一個請求)。不知道發生了什麼,所以任何建議,將不勝感激。如果您想克隆程序並運行它,只需在lib/minion.rb的最後一行註釋並鍵入ruby lib/minion.rb

+0

聽起來好像您的腳本對雅虎財務進行了大量調用。你確定你沒有收到費率限制嗎? –

+0

Yeh @SunilD。當我恢復了我的更改,然後再次運行它,它很快 – Edmund

回答

2

經過調試,我想通了。這是因爲我打電話finder.results環內(結果是裝飾方法),如下圖所示:

require 'bundler/setup' 
require "minion/version" 
require "yahoo-finance" 
require "minion/dilution_finder" 
require "minion/negative_finder" 
require "minion/small_cap_finder" 
require "minion/market_fetcher" 

module Minion 
    class << self 
    def query(exchange) 
     all_companies = CSV.read("#{exchange}.csv") 
     all_tickers = all_companies.map { |row| row[0] } 

     short_finder = DilutionFinder.new(NegativeFinder.new(SmallCapFinder.new(MarketFetcher.new(all_tickers)))) 
     short_finder.results 
    end 
    end 
end 

的部分它落後於根據我puts

require "yahoo-finance" 
require "business_time" 
require_relative "stock_finder" 

class NegativeFinder < StockFinder 
    def results 
    client = YahooFinance::Client.new 
    results = [] 
    finder.results.each_with_index do |stock, index| 
     begin 
     data = client.historical_quotes(stock.symbol, { start_date: 2.business_days.ago, end_date: Time.now }) 
     closing_prices = data.map(&:close).map(&:to_f) 
     volumes = data.map(&:volume).map(&:to_i) 

     negative_3_days_in_a_row = closing_prices == closing_prices.sort 
     larger_than_average_volume = volumes.reduce(:+)/volumes.count > stock.average_daily_volume.to_i 

     if negative_3_days_in_a_row && larger_than_average_volume 
      results << stock 
      // HERE!!!!!!!!!!!!!!!!!!!!!!!!! 
      puts "Qualified: #{stock.symbol}, finished with #{index} out of #{finder.results.count}" <------------------------------------ 
     else 
      // AND HERE!!!!!!!!!!!!!!!!!!!!!!!!! 
      puts "Not qualified: #{stock.symbol}, finished with #{index} out of #{finder.results.count}" <----------------------------------------------------------- 
     end 
     rescue => e 
     puts e.inspect 
     end 
    end 
    results 
    end 
end 

這引起了級聯每次迭代遍歷NegativeFinder中的循環時都會發出請求。刪除該呼叫修復了它。課程:使用裝飾模式時,只需調用一次裝飾方法,特別是在每次調用中要做一些昂貴的事時。無論是或者將返回的變量保存在一個實例變量中,這樣您就不必每次都計算它。

另外作爲一個方面說明,我決定不去裝飾者模式,因爲我不認爲它適用於這裏。像SmallCapFinder.new(SmallCapFinder.new(MarketFetcher.new(all_tickers)))這樣的東西根本不會添加功能(使用裝飾器模式的主要功能),所以鏈接裝飾器不會執行任何操作。因此,我只是想讓他們成爲方法而不是增加不必要的複雜性。

1

您給我們的代碼中缺少一些東西(Base class StockFinder,MarketFetcher)。但我認爲您現在可以立即使用一個以上的YahooFinance :: Client。對其他系統的輸入/輸出通常是速度問題的原因。

我建議你先封裝財務客戶端並獲取財務數據。當您想要切換您的財務數據提供者或者添加另一個財務數據提供者時,這可以更輕鬆。而不是裝飾者模式,我只會使用普通的舊方法來尋找小型帽子,發現負面的,等等。

+0

是的,我在計劃使用適配器模式後,我也修復了這個錯誤。感謝您的建議! – Edmund

+0

p.s.我想到了 – Edmund