sqa 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b21ccda9b46c7125afbce0c1ac8f074b945a64e3ee946db5c44566e104bd34e9
4
- data.tar.gz: 5252167d8e7f7520bcbdd43a9cf0bae541466ee09b05b677817cb31d8d74ca71
3
+ metadata.gz: b699ff032eee80858f9cfc3c3cba7e1ad5c0b537cd9b014085d184f4ce052451
4
+ data.tar.gz: 3c7211690c31b6486718306d7e62fdf71c9466a421cfc694955a64e0d0184e66
5
5
  SHA512:
6
- metadata.gz: a9defe0ba2597a23c5edde2d2adb150e4d5b1e8f0b9005ed00b439b60e1c56af766ec959d305e57c416ebc6c8f701ffe6525050e2ff81258a4348430b3cc8ef8
7
- data.tar.gz: 40026c37bb437b44d1f25ebc868f8d166b2bebfcd3476ce7463f6f3534d8f846da5e8179fba2248e5387da0a8ea0f7acd8805c49952f049ea0ebfa1c75df112a
6
+ metadata.gz: f8a319f9b59ce9af8cccf6ee0cd436e7847db90e95703da13ea50cf33d03cd5c5aeb996b87503f2ddc6f7991f18441be7c49a099377a19a89b6c8bd7ceb4eb3b
7
+ data.tar.gz: 9b7e5b5a16f21d256c44f8a491b6f4bc0b60cc507d9124b49b12d6da9c2e487f64b0f2388ecdc4e0d24e10eb4b4f2fd69f5ca190eb5a4f51af9aba7a1123d6ea
data/README.md CHANGED
@@ -8,6 +8,12 @@ The BUY/SELL signals that it generates are part of a game. **DO NOT USE** when
8
8
 
9
9
  I'm making use of lots of gems which may not be part of the gemspec at this time. I will be adding them as they make the final cut as to fitness for the intended function. Some gems are configurable. For example the default for the plotting library is `gruff`. There are several available that the `daru` gem can use.
10
10
 
11
+ ### DARU or RedAmber
12
+
13
+ I'm just really using `daru` for its data frame object; However, I just learned about the RedAmber data frame object in Ruby based off of Apache Arrow. I'm going to look at that product since it is actively maintained.
14
+
15
+ https://github.com/red-data-tools/red_amber
16
+
11
17
  ## Installation
12
18
 
13
19
  Install the gem and add to the application's Gemfile by executing:
@@ -0,0 +1 @@
1
+ 2df18c06074d6725d838fcda7a72d5053859cc677898f799215a223720eadfc6eb393ab765b074926040b7509b6bb8e0a0b13e1181b0d1713951445a87ac7a6e
@@ -1,28 +1,105 @@
1
1
  # lib/sqa/data_frame/yahoo_finance.rb
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'faraday'
5
+ require 'nokogiri'
6
+
4
7
  class SQA::DataFrame < Daru::DataFrame
5
8
  class YahooFinance
6
- def self.load(filename, options={}, &block)
7
- df = SQA::DataFrame.load(filename, options={}, &block)
9
+ CONNECTION = Faraday.new(url: 'https://finance.yahoo.com')
10
+ HEADERS = %i[
11
+ timestamp # 0
12
+ open_price # 1
13
+ high_price # 2
14
+ low_price # 3
15
+ close_price # 4
16
+ adj_close_price # 5
17
+ volume # 6
18
+ ]
8
19
 
9
- # ASSUMPTION: This is the column headers from Yahoo Finance for
10
- # CSV files. If the file type is something different from the
11
- # same source, they may not be the same.
20
+ # The Yahoo Finance Headers are being remapped so that
21
+ # the header can be used as a method name to access the
22
+ # vector.
12
23
  #
13
- new_names = {
14
- "Date" => :timestamp,
15
- "Open" => :open_price,
16
- "High" => :high_price,
17
- "Low" => :low_price,
18
- "Close" => :close_price,
19
- "Adj Close" => :adj_close_price,
20
- "Volume" => :volume
24
+ HEADER_MAPPING = {
25
+ "Date" => HEADERS[0],
26
+ "Open" => HEADERS[1],
27
+ "High" => HEADERS[2],
28
+ "Low" => HEADERS[3],
29
+ "Close" => HEADERS[4],
30
+ "Adj Close" => HEADERS[5],
31
+ "Volume" => HEADERS[6]
21
32
  }
22
33
 
23
- df.rename_vectors(new_names)
34
+ ################################################################
35
+ def self.load(filename, options={}, &block)
36
+ df = SQA::DataFrame.load(filename, options={}, &block)
37
+
38
+ headers = df.vectors
39
+
40
+ if headers.first == HEADERS.first.to_s
41
+ a_hash = {}
42
+ HEADERS.each {|k| a_hash[k.to_s] = k}
43
+ df.rename_vectors(a_hash)
44
+ else
45
+ df.rename_vectors(HEADER_MAPPING)
46
+ end
24
47
 
25
48
  df
26
49
  end
50
+
51
+
52
+ # Scrape the Yahoo Finance website to get recent
53
+ # historical prices for a specific ticker
54
+ #
55
+ # ticker String the security to retrieve
56
+ # returns a DataFrame
57
+ #
58
+ def self.recent(ticker)
59
+ response = CONNECTION.get("/quote/#{ticker.upcase}/history")
60
+ doc = Nokogiri::HTML(response.body)
61
+ table = doc.css('table').first
62
+ rows = table.css('tbody tr')
63
+
64
+ data = []
65
+
66
+ rows.each do |row|
67
+ cols = row.css('td').map{|c| c&.text}
68
+ next if cols[1]&.include?("Dividend")
69
+ cols[0] = Date.parse(cols[0]).to_s
70
+ cols[6] = cols[6].tr(',','').to_i
71
+ (1..5).each {|x| cols[x] = cols[x].to_f}
72
+ data << HEADERS.zip(cols).to_h
73
+ end
74
+
75
+ Daru::DataFrame.new(data)
76
+ end
77
+
78
+
79
+ # Append update_df rows to the base_df
80
+ #
81
+ # base_df is ascending on timestamp
82
+ # update_df is descending on timestamp
83
+ #
84
+ # base_df content came from CSV file downloaded
85
+ # from Yahoo Finance.
86
+ #
87
+ # update_df came from scraping the webpage
88
+ # at Yahoo Finance for the recent history.
89
+ #
90
+ # Returns a combined DataFrame.
91
+ #
92
+ def self.append(base_df, updates_df)
93
+ last_timestamp = Date.parse base_df.timestamp.last
94
+ filtered_df = updates_df.filter_rows { |row| Date.parse(row[:timestamp]) > last_timestamp }
95
+
96
+ last_inx = filtered_df.size - 1
97
+
98
+ (0..last_inx).each do |x|
99
+ base_df.add_row filtered_df.row[last_inx-x]
100
+ end
101
+
102
+ base_df
103
+ end
27
104
  end
28
105
  end
@@ -3,7 +3,37 @@
3
3
 
4
4
  require_relative 'data_frame/yahoo_finance'
5
5
 
6
+ class Daru::DataFrame
7
+
8
+ def to_csv(path_to_file, opts={})
9
+ options = {
10
+ converters: :numeric
11
+ }.merge(opts)
12
+
13
+ writer = ::CSV.open(path_to_file, 'wb')
14
+
15
+ writer << vectors.to_a unless options[:headers] == false
16
+
17
+ each_row do |row|
18
+ writer << if options[:convert_comma]
19
+ row.map { |v| v.to_s.tr('.', ',') }
20
+ else
21
+ row.to_a
22
+ end
23
+ end
24
+
25
+ writer.close
26
+ end
27
+ end
28
+
29
+
30
+
31
+
6
32
  class SQA::DataFrame < Daru::DataFrame
33
+
34
+
35
+ #################################################
36
+
7
37
  def self.path(filename)
8
38
  Pathname.new SQA.config.data_dir + filename
9
39
  end
data/lib/sqa/stock.rb CHANGED
@@ -8,10 +8,24 @@ class SQA::Stock
8
8
  def initialize(ticker:, source: :yahoo_finance, type: :csv)
9
9
  @ticker = ticker.downcase
10
10
  @company_name = "Company Name"
11
- klass = "SQA::DataFrame::#{source.to_s.camelize}".constantize
12
- filename = "#{@ticker}.#{type}"
13
- @df = klass.send(:load, filename)
14
- @df[:ticker] = ticker
11
+ @klass = "SQA::DataFrame::#{source.to_s.camelize}".constantize
12
+ @type = type
13
+ @filename = "#{@ticker}.#{type}"
14
+
15
+ update_the_dataframe
16
+ end
17
+
18
+
19
+ def update_the_dataframe
20
+ df1 = @klass.load(@filename)
21
+ df2 = @klass.recent(@ticker)
22
+ @df = @klass.append(df1, df2)
23
+
24
+ unless @df.size == df1.size
25
+ @df.send("to_#{@type}", SQA::DataFrame.path(@filename))
26
+ end
27
+
28
+ @df[:ticker] = @ticker
15
29
  end
16
30
 
17
31
  def to_s
data/lib/sqa/version.rb CHANGED
@@ -4,7 +4,7 @@ require 'sem_version'
4
4
  require 'sem_version/core_ext'
5
5
 
6
6
  module SQA
7
- VERSION = "0.0.9"
7
+ VERSION = "0.0.10"
8
8
 
9
9
  class << self
10
10
  def version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-08 00:00:00.000000000 Z
11
+ date: 2023-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -278,6 +278,7 @@ files:
278
278
  - Rakefile
279
279
  - bin/sqa
280
280
  - checksums/sqa-0.0.1.gem.sha512
281
+ - checksums/sqa-0.0.10.gem.sha512
281
282
  - checksums/sqa-0.0.2.gem.sha512
282
283
  - checksums/sqa-0.0.3.gem.sha512
283
284
  - checksums/sqa-0.0.4.gem.sha512