sqa 0.0.9 → 0.0.11

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: 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