sqa 0.0.11 → 0.0.12

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