sqa 0.0.11 → 0.0.12

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: a524561e1371d56e98fb6f217ba88e377c142e06b93233eeae92cf9adeff3687
4
- data.tar.gz: ce938d1e68f85f66da30e22428c166f28c8ea13cd93c9d8a42255390f645c034
3
+ metadata.gz: 39185fb1deccd8cc248d4ad776b724ea5ebe1a7e93fdc1d6ada4f53661a55bba
4
+ data.tar.gz: 3b394f997cdcdd67ec9eced2cd7bc08c1af2b407b8945e1a76e0383591ca3495
5
5
  SHA512:
6
- metadata.gz: f88857d621fc46c3d5af5e0e3735304ee1f390cf5dd9ceceab25741eed080287ecdd6f46113e91ebed832baef9e78c99222cd84f6e4c2e666b0bb8a1fa82bb97
7
- data.tar.gz: b462fe96808649c282e4ff2eddde19b0dadb8ff22e598d6b64064262e47231504ae2bef27a21960a9660ce52dbd92e0691cc5f47eb0df857b50ff4f2d3779fc1
6
+ metadata.gz: 3d805f6db11375738db0cdad8385f7a74c26c3fba39f0a7aa88593532e1127af7c4c690844eae56f6203696743e1b655bb9d996f2e71745cefb4f04227bbe622
7
+ data.tar.gz: c52327bfbf15f25159a14af31be0881112e169dc73dbdab7d7c7ac7519208e366314e4ab6bd31d44134fbda5238dc7b56e553dcc8c7d231938b78af3ec17d137
@@ -0,0 +1 @@
1
+ 2d4a5156118549d604963b8f4dd0b311459f5ad6a1747f2c4925d45f12fe94a863eb225d3c593f82face964773ccbf59478612a014ee9fc32814baf627f730c7
data/lib/sqa/config.rb CHANGED
@@ -86,17 +86,23 @@ module SQA
86
86
  # TODO: arrange order in mostly often used
87
87
 
88
88
  if ".json" == type
89
- form_json
89
+ incoming = form_json
90
90
 
91
91
  elsif %w[.yml .yaml].include?(type)
92
- from_yaml
92
+ incoming = from_yaml
93
93
 
94
94
  elsif ".toml" == type
95
- from_toml
95
+ incoming = from_toml
96
96
 
97
97
  else
98
98
  raise BadParameterError, "Invalid Config File: #{config_file}"
99
99
  end
100
+
101
+ if incoming.has_key? :data_dir
102
+ incoming[:data_dir] = incoming[:data_dir].gsub(/^~/, Nenv.home)
103
+ end
104
+
105
+ merge! incoming
100
106
  end
101
107
 
102
108
  def dump_file
@@ -142,19 +148,15 @@ module SQA
142
148
  ## override values from a config file
143
149
 
144
150
  def from_json
145
- incoming = ::JSON.load(File.open(config_file).read)
146
- debug_me{[ :incoming ]}
151
+ ::JSON.load(File.open(config_file).read).symbolize_keys
147
152
  end
148
153
 
149
154
  def from_toml
150
- incoming = TomlRB.load_file(config_file)
151
- debug_me{[ :incoming ]}
155
+ TomlRB.load_file(config_file).symbolize_keys
152
156
  end
153
157
 
154
158
  def from_yaml
155
- incoming = ::YAML.load_file(config_file)
156
- debug_me{[ :incoming ]}
157
- merge! incoming
159
+ ::YAML.load_file(config_file).symbolize_keys
158
160
  end
159
161
 
160
162
 
@@ -35,7 +35,7 @@ class SQA::DataFrame < Daru::DataFrame
35
35
  #################################################
36
36
 
37
37
  def self.path(filename)
38
- Pathname.new SQA.config.data_dir + filename
38
+ Pathname.new(SQA.config.data_dir) + filename
39
39
  end
40
40
 
41
41
  def self.load(filename, options={}, &block)
@@ -5,19 +5,91 @@ end
5
5
 
6
6
  class SQA::Indicator; class << self
7
7
 
8
- def predict_next_values(array, predictions)
9
- result = []
10
-
11
- array.each_cons(2) do |a, b|
12
- # TODO: take 3 at a time. compare 3rd
13
- # prediction. Generate an average delta
14
- # between predicted and actual. Return
15
- # the prediction as a range???
8
+ # Produce a Table show actual values and forecasted values
9
+ #
10
+ # actual .... Array of Float
11
+ # forecast .. Array of Float or Array of Array of Float
12
+ # entry is either a single value or
13
+ # an Array [high, guess, low]
14
+ #
15
+ def prediction_test(actual, forecast)
16
+
17
+ unless actual.size == forecast.size
18
+ debug_me("== ERROR =="){[
19
+ "actual.size",
20
+ "forecast.size"
21
+ ]}
22
+ end
23
+
24
+ # Method Under Test (MUT)
25
+ mut = caller[0][/`([^']*)'/, 1]
26
+ window = actual.size
27
+ hgl = forecast.first.is_a?(Array)
28
+
29
+ if hgl
30
+ headers = %w[ Actual Forecast Diff %off InRange? High Low ]
31
+ else
32
+ headers = %w[ Actual Forecast Diff %off ]
33
+ end
34
+
35
+ diff = []
36
+ percent = []
37
+ values = []
38
+
39
+ actual.map!{|v| v.round(3)}
40
+
41
+ if hgl
42
+ high = forecast.map{|v| v[0].round(3)}
43
+ guess = forecast.map{|v| v[1].round(3)}
44
+ low = forecast.map{|v| v[2].round(3)}
45
+ else
46
+ guess = forecast.map{|v| v.round(3)}
47
+ end
48
+
49
+ window.times do |x|
50
+ diff << (actual[x] - guess[x]).round(3)
51
+ percent << ((diff.last / guess[x])*100.0).round(3)
52
+
53
+ entry = [
54
+ actual[x], guess[x],
55
+ diff[x], percent[x],
56
+ ]
57
+
58
+ if hgl
59
+ entry << ( (high[x] >= actual[x] && actual[x] >= low[x]) ? "Yes" : "" )
60
+ entry << high[x]
61
+ entry << low[x]
62
+ end
63
+
64
+ values << entry
65
+ end
66
+
67
+ the_table = TTY::Table.new(headers, values)
68
+
69
+ puts "\n#{mut} Result Validation"
70
+
71
+ puts the_table.render(
72
+ :unicode,
73
+ {
74
+ padding: [0, 0, 0, 0],
75
+ alignments: [:right]*values.first.size,
76
+ }
77
+ )
78
+ puts
79
+ end
80
+
81
+
82
+ def predict_next_values(stock, window, testing=false)
83
+ prices = stock.df.adj_close_price.to_a
84
+ known = prices.pop(window) if testing
85
+ result = []
86
+
87
+ prices.each_cons(2) do |a, b|
16
88
  result << b + (b - a)
17
89
  end
18
90
 
19
- if predictions > 0
20
- (1..predictions).each do |_|
91
+ if window > 0
92
+ (1..window).each do |_|
21
93
  last_two_values = result.last(2)
22
94
  delta = last_two_values.last - last_two_values.first
23
95
  next_value = last_two_values.last + delta
@@ -25,64 +97,106 @@ class SQA::Indicator; class << self
25
97
  end
26
98
  end
27
99
 
28
- result.last(predictions)
100
+ prediction_test(known, result.last(window)) if testing
101
+
102
+ result.last(window)
29
103
  end
30
104
  alias_method :pnv, :predict_next_values
31
105
 
32
106
 
33
- # Returns a forecast for future values based on the near past
34
- #
35
- # When I wrote this I was thinking of hurricane forecasting and how
36
- # the cone of probability gets larger the further into the future
37
- # the forecast goes. This does not produce that kind of probability
38
- # cone; but, that was hwat I was thinking about
39
- #
40
- # array is an Array - for example historical price data
41
- # predictions is an Integer for how many predictions into the future
42
- #
43
- def pnv2(array, predictions)
107
+ def pnv2(stock, window, testing=false)
108
+ prices = stock.df.adj_close_price.to_a
109
+ known = prices.pop(window) if testing
110
+
44
111
  result = []
45
- last_inx = array.size - 1 # indexes are zero based
112
+ last_inx = prices.size - 1 # indexes are zero based
46
113
 
47
- predictions.times do |x|
114
+ window.times do |x|
48
115
  x += 1 # forecasting 1 day into the future needs 2 days of near past data
49
116
 
50
117
  # window is the near past values
51
- window = array[last_inx-x..]
118
+ window = prices[last_inx-x..]
52
119
 
53
120
  high = window.max
54
121
  low = window.min
55
122
  midpoint = (high + low) / 2.0
56
123
 
57
- result << [high, midpoint, low]
124
+ result << [high, midpoint, low]
125
+ end
126
+
127
+ prediction_test(known, result) if testing
128
+
129
+ result
130
+ end
131
+
132
+
133
+ def pnv3(stock, window, testing=false)
134
+ prices = stock.df.adj_close_price.to_a
135
+ known = prices.pop(window) if testing
136
+
137
+ result = []
138
+ known = prices.last(window)
139
+
140
+ last_inx = prices.size - 1
141
+
142
+ (0..window-1).to_a.reverse.each do |x|
143
+ curr_inx = last_inx - x
144
+ prev_inx = curr_inx - 1
145
+ current_price = prices[curr_inx]
146
+ percentage_change = (current_price - prices[prev_inx]) / prices[prev_inx]
147
+
148
+ result << current_price + (current_price * percentage_change)
58
149
  end
59
150
 
151
+ prediction_test(known, result) if testing
152
+
60
153
  result
61
154
  end
62
155
 
63
156
 
64
- def pnv3(prices, prediction_window)
65
- predicted_prices = []
66
- known = prices.last(prediction_window+2).dup
157
+ def pnv4(stock, window, testing=false)
158
+ prices = stock.df.adj_close_price.to_a
159
+ known = prices.pop(window) if testing
160
+
161
+ result = []
162
+ known = prices.last(window).dup
163
+ current_price = known.last
67
164
 
68
165
  # Loop through the prediction window size
69
- (1..prediction_window).each do |x|
70
- current_price = known.last
166
+ (1..window).each do |x|
71
167
 
72
168
  # Calculate the percentage change between the current price and its previous price
73
- percentage_change = (current_price - known[-1]) / known[-1] # .to_f
169
+ percentage_change = (current_price - prices[-x]) / prices[-x]
74
170
 
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
171
+ result << current_price + (current_price * percentage_change)
81
172
  end
82
173
 
83
- predicted_prices
174
+ prediction_test(known, result) if testing
175
+
176
+ result
84
177
  end
85
178
 
86
179
 
180
+ def pnv5(stock, window, testing=false)
181
+ prices = stock.df.adj_close_price.to_a
182
+ known = prices.pop(window) if testing
183
+
184
+ result = []
185
+ current_price = prices.last
186
+
187
+ rate = 0.9 # convert angle into percentage
188
+ sma_trend = stock.indicators.sma_trend
189
+ percentage_change = 1 + (sma_trend[:angle] / 100.0) * rate
190
+
191
+ # Assumes the SMA trend will continue
192
+ window.times do |_|
193
+ result << current_price * percentage_change
194
+ current_price = result.last
195
+ end
196
+
197
+ prediction_test(known, result) if testing
198
+
199
+ result
200
+ end
87
201
 
88
202
  end; end
data/lib/sqa/stock.rb CHANGED
@@ -4,6 +4,7 @@ class SQA::Stock
4
4
  attr_accessor :company_name
5
5
  attr_accessor :df # The DataFrane
6
6
  attr_accessor :ticker
7
+ attr_accessor :indicators
7
8
 
8
9
  def initialize(ticker:, source: :yahoo_finance, type: :csv)
9
10
  @ticker = ticker.downcase
@@ -11,6 +12,7 @@ class SQA::Stock
11
12
  @klass = "SQA::DataFrame::#{source.to_s.camelize}".constantize
12
13
  @type = type
13
14
  @filename = "#{@ticker}.#{type}"
15
+ @indicators = OpenStruct.new
14
16
 
15
17
  update_the_dataframe
16
18
  end
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.11"
7
+ VERSION = "0.0.12"
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.11
4
+ version: 0.0.12
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-13 00:00:00.000000000 Z
11
+ date: 2023-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -280,6 +280,7 @@ files:
280
280
  - checksums/sqa-0.0.1.gem.sha512
281
281
  - checksums/sqa-0.0.10.gem.sha512
282
282
  - checksums/sqa-0.0.11.gem.sha512
283
+ - checksums/sqa-0.0.12.gem.sha512
283
284
  - checksums/sqa-0.0.2.gem.sha512
284
285
  - checksums/sqa-0.0.3.gem.sha512
285
286
  - checksums/sqa-0.0.4.gem.sha512
@@ -390,7 +391,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
390
391
  - !ruby/object:Gem::Version
391
392
  version: '0'
392
393
  requirements: []
393
- rubygems_version: 3.4.18
394
+ rubygems_version: 3.4.19
394
395
  signing_key:
395
396
  specification_version: 4
396
397
  summary: sqa - Stock Qualitative Analysis