gpt4 book ai didi

Ruby:装饰器模式大大降低了简单程序的速度

转载 作者:数据小太阳 更新时间:2023-10-29 07:59:14 25 4
gpt4 key购买 nike

我最近写了一个程序,从股市中返回一堆不健康的股票。基本算法是这样的:

  1. 查找交易所(NYSE 或 NASDAQ)中每只股票的所有报价

  2. 从步骤 1 中找出小于 5 美元的

  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

最佳答案

经过调试,我明白了。这是因为我在循环内部调用了 finder.results(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))) 之类的东西根本不添加功能(使用装饰器模式的主要功能),所以链接装饰器不会做任何事物。因此,我只是让它们成为方法,而不是增加不必要的复杂性。

关于Ruby:装饰器模式大大降低了简单程序的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32800238/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com