sqa 0.0.8 → 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: 9ed5a0bfe672b2a11993e5afd8ed8cbb103355f033c95e791c8c2ef57dbd8464
4
- data.tar.gz: 308d76cd23b50c057816ed7cde0815a51b4bb5e196ea14e231fd68ad06fe76b5
3
+ metadata.gz: b699ff032eee80858f9cfc3c3cba7e1ad5c0b537cd9b014085d184f4ce052451
4
+ data.tar.gz: 3c7211690c31b6486718306d7e62fdf71c9466a421cfc694955a64e0d0184e66
5
5
  SHA512:
6
- metadata.gz: 1e8297f1d895d4ae7509a24486dc5ae3f542e1f0aeead4308a37da0a9a240f9b8e7bc2858914d928277044be78aff8eb6880c3b86329457e1cc329903d2faf23
7
- data.tar.gz: d58c62150ae5ed500222704cf550f7f34d7740fe4a5e7761354962ce3fbd79c21353d9d5b6a017f6b3c2e636316c745e05b261507883bace63901769b1959049
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:
data/Rakefile CHANGED
@@ -1,4 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.warning = false
10
+ t.test_globs = ["test/**/*_test.rb"]
11
+ end
12
+
4
13
  task default: %i[]
@@ -0,0 +1 @@
1
+ 2df18c06074d6725d838fcda7a72d5053859cc677898f799215a223720eadfc6eb393ab765b074926040b7509b6bb8e0a0b13e1181b0d1713951445a87ac7a6e
@@ -0,0 +1 @@
1
+ 2a8a31c1dced7189c4b5c2a53a14fd222b28bad3de103bab088e5a1ee49c3dcb9f9632e8fa84ad11a28749477dfb9b6a79e3b86cb1cd26ac88517b27e3a5ad99
data/lib/sqa/cli.rb CHANGED
@@ -57,7 +57,7 @@ module SQA
57
57
  desc "Set the directory for the SQA data"
58
58
  end
59
59
 
60
- flag :dump_config do
60
+ option :dump_config do
61
61
  long "--dump-config path_to_file"
62
62
  desc "Dump the current configuration"
63
63
  end
@@ -156,7 +156,7 @@ module SQA
156
156
 
157
157
  def remove_temps(a_hash)
158
158
  temps = %i[ help version dump ]
159
- debug_me{[ :a_hash ]}
159
+ # debug_me{[ :a_hash ]}
160
160
  a_hash.reject{|k, _| temps.include? k}
161
161
  end
162
162
  end
data/lib/sqa/config.rb CHANGED
@@ -18,7 +18,9 @@ module SQA
18
18
  include Hashie::Extensions::Coercion
19
19
  include Hashie::Extensions::Dash::PredefinedValues
20
20
 
21
- property :config_file #, default: Nenv.home + "/.sqa.yml"
21
+ property :config_file #,a String filepath for the current config overriden by cli options
22
+ property :dump_config # a String filepath into which to dump the current config
23
+
22
24
  property :data_dir, default: Nenv.home + "/sqa_data"
23
25
 
24
26
  # TODO: If no path is given, these files will be in
@@ -163,7 +165,15 @@ module SQA
163
165
  def dump_json = File.open(config_file, "w") { |f| f.write JSON.pretty_generate(as_hash)}
164
166
  def dump_toml = File.open(config_file, "w") { |f| f.write TomlRB.dump(as_hash)}
165
167
  def dump_yaml = File.open(config_file, "w") { |f| f.write as_hash.to_yaml}
166
- end
168
+
169
+
170
+ #####################################
171
+ class << self
172
+ def reset
173
+ SQA.config = new
174
+ end
175
+ end
176
+ end
167
177
  end
168
178
 
169
- SQA.config = SQA::Config.new
179
+ SQA::Config.reset
@@ -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,9 +3,39 @@
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
- SQA::Config.data_dir + filename
38
+ Pathname.new SQA.config.data_dir + filename
9
39
  end
10
40
 
11
41
  def self.load(filename, options={}, &block)
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/strategy.rb CHANGED
@@ -54,4 +54,12 @@ class SQA::Strategy
54
54
  end
55
55
  end
56
56
 
57
+ require_relative 'strategy/common'
58
+ require_relative 'strategy/consensus'
59
+ require_relative 'strategy/ema'
60
+ require_relative 'strategy/mp'
61
+ require_relative 'strategy/mr'
62
+ require_relative 'strategy/random'
63
+ require_relative 'strategy/rsi'
64
+ require_relative 'strategy/sma'
57
65
 
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.8"
7
+ VERSION = "0.0.10"
8
8
 
9
9
  class << self
10
10
  def version
data/lib/sqa.rb CHANGED
@@ -30,7 +30,13 @@ module SQA
30
30
  # Ran at SQA::Config elaboration time
31
31
  # @@config = Config.new
32
32
 
33
- CLI.run(argv) if defined? CLI
33
+ if defined? CLI
34
+ CLI.run(argv)
35
+ else
36
+ # There are no real command line parameters
37
+ # because the sqa gem is be required within
38
+ # the context of a larger program.
39
+ end
34
40
 
35
41
  Daru.lazy_update = config.lazy_update
36
42
  Daru.plotting_library = config.plotting_library
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.8
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-05 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
@@ -285,6 +286,7 @@ files:
285
286
  - checksums/sqa-0.0.6.gem.sha512
286
287
  - checksums/sqa-0.0.7.gem.sha512
287
288
  - checksums/sqa-0.0.8.gem.sha512
289
+ - checksums/sqa-0.0.9.gem.sha512
288
290
  - docs/.gitignore
289
291
  - docs/README.md
290
292
  - docs/average_true_range.md