sqa 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b21ccda9b46c7125afbce0c1ac8f074b945a64e3ee946db5c44566e104bd34e9
4
- data.tar.gz: 5252167d8e7f7520bcbdd43a9cf0bae541466ee09b05b677817cb31d8d74ca71
3
+ metadata.gz: a524561e1371d56e98fb6f217ba88e377c142e06b93233eeae92cf9adeff3687
4
+ data.tar.gz: ce938d1e68f85f66da30e22428c166f28c8ea13cd93c9d8a42255390f645c034
5
5
  SHA512:
6
- metadata.gz: a9defe0ba2597a23c5edde2d2adb150e4d5b1e8f0b9005ed00b439b60e1c56af766ec959d305e57c416ebc6c8f701ffe6525050e2ff81258a4348430b3cc8ef8
7
- data.tar.gz: 40026c37bb437b44d1f25ebc868f8d166b2bebfcd3476ce7463f6f3534d8f846da5e8179fba2248e5387da0a8ea0f7acd8805c49952f049ea0ebfa1c75df112a
6
+ metadata.gz: f88857d621fc46c3d5af5e0e3735304ee1f390cf5dd9ceceab25741eed080287ecdd6f46113e91ebed832baef9e78c99222cd84f6e4c2e666b0bb8a1fa82bb97
7
+ data.tar.gz: b462fe96808649c282e4ff2eddde19b0dadb8ff22e598d6b64064262e47231504ae2bef27a21960a9660ce52dbd92e0691cc5f47eb0df857b50ff4f2d3779fc1
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
@@ -0,0 +1 @@
1
+ 9fad1def1db23b0f3f28e116cac5032d263921d31f336dba356c5f5fb5cfe911aa73c69615710fcc22662560b96bcc2b683dc5db1044b4acb3337726d6244a45
@@ -1,28 +1,115 @@
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 = [
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.children[0].text}
68
+
69
+ next unless 7 == cols.size
70
+ next if cols[1]&.include?("Dividend")
71
+
72
+ if cols.any?(nil)
73
+ debug_me('== ERROR =='){[
74
+ :cols
75
+ ]}
76
+ next
77
+ end
78
+
79
+ cols[0] = Date.parse(cols[0]).to_s
80
+ cols[6] = cols[6].tr(',','').to_i
81
+ (1..5).each {|x| cols[x] = cols[x].to_f}
82
+ data << HEADERS.zip(cols).to_h
83
+ end
84
+
85
+ Daru::DataFrame.new(data)
86
+ end
87
+
88
+
89
+ # Append update_df rows to the base_df
90
+ #
91
+ # base_df is ascending on timestamp
92
+ # update_df is descending on timestamp
93
+ #
94
+ # base_df content came from CSV file downloaded
95
+ # from Yahoo Finance.
96
+ #
97
+ # update_df came from scraping the webpage
98
+ # at Yahoo Finance for the recent history.
99
+ #
100
+ # Returns a combined DataFrame.
101
+ #
102
+ def self.append(base_df, updates_df)
103
+ last_timestamp = Date.parse base_df.timestamp.last
104
+ filtered_df = updates_df.filter_rows { |row| Date.parse(row[:timestamp]) > last_timestamp }
105
+
106
+ last_inx = filtered_df.size - 1
107
+
108
+ (0..last_inx).each do |x|
109
+ base_df.add_row filtered_df.row[last_inx-x]
110
+ end
111
+
112
+ base_df
113
+ end
27
114
  end
28
115
  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
@@ -60,4 +60,29 @@ class SQA::Indicator; class << self
60
60
  result
61
61
  end
62
62
 
63
+
64
+ def pnv3(prices, prediction_window)
65
+ predicted_prices = []
66
+ known = prices.last(prediction_window+2).dup
67
+
68
+ # Loop through the prediction window size
69
+ (1..prediction_window).each do |x|
70
+ current_price = known.last
71
+
72
+ # Calculate the percentage change between the current price and its previous price
73
+ percentage_change = (current_price - known[-1]) / known[-1] # .to_f
74
+
75
+ # Calculate the predicted price based on the percentage change
76
+ predicted_price = current_price + (current_price * percentage_change)
77
+ predicted_prices.unshift(predicted_price)
78
+
79
+ # Update the prices array for the next iteration
80
+ known.pop
81
+ end
82
+
83
+ predicted_prices
84
+ end
85
+
86
+
87
+
63
88
  end; end
data/lib/sqa/stock.rb CHANGED
@@ -8,10 +8,26 @@ 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
+ if @df.nrows > df1.nrows
25
+ @df.send("to_#{@type}", SQA::DataFrame.path(@filename))
26
+ end
27
+
28
+ # Adding a ticker vector in case I want to do
29
+ # some multi-stock analysis in the same data frame.
30
+ @df[:ticker] = @ticker
15
31
  end
16
32
 
17
33
  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.11"
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.11
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-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -278,6 +278,8 @@ 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
282
+ - checksums/sqa-0.0.11.gem.sha512
281
283
  - checksums/sqa-0.0.2.gem.sha512
282
284
  - checksums/sqa-0.0.3.gem.sha512
283
285
  - checksums/sqa-0.0.4.gem.sha512