sqa 0.0.22 → 0.0.31

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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/.goose/memory/development.txt +3 -0
  3. data/.semver +6 -0
  4. data/ARCHITECTURE.md +648 -0
  5. data/CHANGELOG.md +86 -0
  6. data/CLAUDE.md +653 -0
  7. data/COMMITS.md +196 -0
  8. data/DATAFRAME_ARCHITECTURE_REVIEW.md +421 -0
  9. data/NEXT-STEPS.md +154 -0
  10. data/README.md +833 -213
  11. data/TASKS.md +358 -0
  12. data/TEST_RESULTS.md +140 -0
  13. data/TODO.md +42 -0
  14. data/_notes.txt +25 -0
  15. data/bin/sqa-console +11 -0
  16. data/checksums/sqa-0.0.23.gem.sha512 +1 -0
  17. data/checksums/sqa-0.0.24.gem.sha512 +1 -0
  18. data/data/talk_talk.json +103284 -0
  19. data/develop_summary.md +313 -0
  20. data/docs/advanced/backtesting.md +206 -0
  21. data/docs/advanced/ensemble.md +68 -0
  22. data/docs/advanced/fpop.md +153 -0
  23. data/docs/advanced/index.md +112 -0
  24. data/docs/advanced/multi-timeframe.md +67 -0
  25. data/docs/advanced/pattern-matcher.md +75 -0
  26. data/docs/advanced/portfolio-optimizer.md +79 -0
  27. data/docs/advanced/portfolio.md +166 -0
  28. data/docs/advanced/risk-management.md +210 -0
  29. data/docs/advanced/strategy-generator.md +158 -0
  30. data/docs/advanced/streaming.md +209 -0
  31. data/docs/ai_and_ml.md +80 -0
  32. data/docs/api/dataframe.md +1115 -0
  33. data/docs/api/index.md +126 -0
  34. data/docs/assets/css/custom.css +88 -0
  35. data/docs/assets/js/mathjax.js +18 -0
  36. data/docs/concepts/index.md +68 -0
  37. data/docs/contributing/index.md +60 -0
  38. data/docs/data-sources/index.md +66 -0
  39. data/docs/data_frame.md +317 -97
  40. data/docs/factors_that_impact_price.md +26 -0
  41. data/docs/finviz.md +11 -0
  42. data/docs/fx_pro_bit.md +25 -0
  43. data/docs/genetic_programming.md +104 -0
  44. data/docs/getting-started/index.md +123 -0
  45. data/docs/getting-started/installation.md +229 -0
  46. data/docs/getting-started/quick-start.md +244 -0
  47. data/docs/i_gotta_an_idea.md +22 -0
  48. data/docs/index.md +163 -0
  49. data/docs/indicators/index.md +97 -0
  50. data/docs/indicators.md +110 -24
  51. data/docs/options.md +8 -0
  52. data/docs/strategies/bollinger-bands.md +146 -0
  53. data/docs/strategies/consensus.md +64 -0
  54. data/docs/strategies/custom.md +310 -0
  55. data/docs/strategies/ema.md +53 -0
  56. data/docs/strategies/index.md +92 -0
  57. data/docs/strategies/kbs.md +164 -0
  58. data/docs/strategies/macd.md +96 -0
  59. data/docs/strategies/market-profile.md +54 -0
  60. data/docs/strategies/mean-reversion.md +58 -0
  61. data/docs/strategies/rsi.md +95 -0
  62. data/docs/strategies/sma.md +55 -0
  63. data/docs/strategies/stochastic.md +63 -0
  64. data/docs/strategies/volume-breakout.md +54 -0
  65. data/docs/ta_lib.md +160 -0
  66. data/docs/tags.md +7 -0
  67. data/docs/true_strength_index.md +46 -0
  68. data/docs/weighted_moving_average.md +48 -0
  69. data/examples/README.md +354 -0
  70. data/examples/advanced_features_example.rb +350 -0
  71. data/examples/fpop_analysis_example.rb +191 -0
  72. data/examples/genetic_programming_example.rb +148 -0
  73. data/examples/kbs_strategy_example.rb +208 -0
  74. data/examples/pattern_context_example.rb +300 -0
  75. data/examples/rails_app/Gemfile +34 -0
  76. data/examples/rails_app/README.md +416 -0
  77. data/examples/rails_app/app/assets/javascripts/application.js +107 -0
  78. data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
  79. data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
  80. data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
  81. data/examples/rails_app/app/controllers/application_controller.rb +22 -0
  82. data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
  83. data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
  84. data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
  85. data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
  86. data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
  87. data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
  88. data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
  89. data/examples/rails_app/app/views/errors/show.html.erb +17 -0
  90. data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
  91. data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
  92. data/examples/rails_app/bin/rails +6 -0
  93. data/examples/rails_app/config/application.rb +45 -0
  94. data/examples/rails_app/config/boot.rb +5 -0
  95. data/examples/rails_app/config/database.yml +18 -0
  96. data/examples/rails_app/config/environment.rb +11 -0
  97. data/examples/rails_app/config/routes.rb +26 -0
  98. data/examples/rails_app/config.ru +8 -0
  99. data/examples/realtime_stream_example.rb +274 -0
  100. data/examples/sinatra_app/Gemfile +22 -0
  101. data/examples/sinatra_app/QUICKSTART.md +159 -0
  102. data/examples/sinatra_app/README.md +461 -0
  103. data/examples/sinatra_app/app.rb +344 -0
  104. data/examples/sinatra_app/config.ru +5 -0
  105. data/examples/sinatra_app/public/css/style.css +659 -0
  106. data/examples/sinatra_app/public/js/app.js +107 -0
  107. data/examples/sinatra_app/views/analyze.erb +306 -0
  108. data/examples/sinatra_app/views/backtest.erb +325 -0
  109. data/examples/sinatra_app/views/dashboard.erb +419 -0
  110. data/examples/sinatra_app/views/error.erb +58 -0
  111. data/examples/sinatra_app/views/index.erb +118 -0
  112. data/examples/sinatra_app/views/layout.erb +61 -0
  113. data/examples/sinatra_app/views/portfolio.erb +43 -0
  114. data/examples/strategy_generator_example.rb +346 -0
  115. data/hsa_portfolio.csv +11 -0
  116. data/justfile +0 -0
  117. data/lib/api/alpha_vantage_api.rb +462 -0
  118. data/lib/sqa/backtest.rb +329 -0
  119. data/lib/sqa/config.rb +22 -9
  120. data/lib/sqa/data_frame/alpha_vantage.rb +43 -65
  121. data/lib/sqa/data_frame/data.rb +92 -0
  122. data/lib/sqa/data_frame/yahoo_finance.rb +34 -41
  123. data/lib/sqa/data_frame.rb +148 -243
  124. data/lib/sqa/ensemble.rb +359 -0
  125. data/lib/sqa/fpop.rb +199 -0
  126. data/lib/sqa/gp.rb +259 -0
  127. data/lib/sqa/indicator.rb +5 -8
  128. data/lib/sqa/init.rb +16 -9
  129. data/lib/sqa/market_regime.rb +240 -0
  130. data/lib/sqa/multi_timeframe.rb +379 -0
  131. data/lib/sqa/pattern_matcher.rb +497 -0
  132. data/lib/sqa/plugin_manager.rb +20 -0
  133. data/lib/sqa/portfolio.rb +260 -6
  134. data/lib/sqa/portfolio_optimizer.rb +377 -0
  135. data/lib/sqa/risk_manager.rb +442 -0
  136. data/lib/sqa/seasonal_analyzer.rb +209 -0
  137. data/lib/sqa/sector_analyzer.rb +300 -0
  138. data/lib/sqa/stock.rb +67 -96
  139. data/lib/sqa/strategy/bollinger_bands.rb +42 -0
  140. data/lib/sqa/strategy/common.rb +0 -2
  141. data/lib/sqa/strategy/consensus.rb +5 -2
  142. data/lib/sqa/strategy/kbs_strategy.rb +470 -0
  143. data/lib/sqa/strategy/macd.rb +46 -0
  144. data/lib/sqa/strategy/mp.rb +1 -1
  145. data/lib/sqa/strategy/stochastic.rb +60 -0
  146. data/lib/sqa/strategy/volume_breakout.rb +57 -0
  147. data/lib/sqa/strategy.rb +5 -0
  148. data/lib/sqa/strategy_generator.rb +947 -0
  149. data/lib/sqa/stream.rb +361 -0
  150. data/lib/sqa/version.rb +1 -7
  151. data/lib/sqa.rb +41 -14
  152. data/main.just +81 -0
  153. data/mkdocs.yml +288 -0
  154. data/trace.log +0 -0
  155. metadata +279 -48
  156. data/bin/sqa +0 -6
  157. data/lib/sqa/activity.rb +0 -10
  158. data/lib/sqa/analysis.rb +0 -306
  159. data/lib/sqa/cli.rb +0 -173
  160. data/lib/sqa/constants.rb +0 -23
  161. data/lib/sqa/indicator/average_true_range.rb +0 -43
  162. data/lib/sqa/indicator/bollinger_bands.rb +0 -28
  163. data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
  164. data/lib/sqa/indicator/donchian_channel.rb +0 -29
  165. data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
  166. data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
  167. data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
  168. data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
  169. data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
  170. data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
  171. data/lib/sqa/indicator/market_profile.rb +0 -32
  172. data/lib/sqa/indicator/mean_reversion.rb +0 -37
  173. data/lib/sqa/indicator/momentum.rb +0 -28
  174. data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
  175. data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
  176. data/lib/sqa/indicator/predict_next_value.rb +0 -202
  177. data/lib/sqa/indicator/relative_strength_index.rb +0 -47
  178. data/lib/sqa/indicator/simple_moving_average.rb +0 -24
  179. data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
  180. data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
  181. data/lib/sqa/indicator/true_range.rb +0 -39
  182. data/lib/sqa/trade.rb +0 -26
  183. data/lib/sqa/web.rb +0 -159
@@ -1,57 +0,0 @@
1
- # lib/sqa/indicator/elliott_wave_theory.rb
2
-
3
- # TIDI: This is very simplistic. It may be completely wrong even
4
- # as simplistic as it is. Consider using the sma_trend to
5
- # acquire the up and down patterns. Run those through a
6
- # classifier. Might even have to review the concept of a
7
- # trend with regard to varying the periods to turn
8
- # many small patterns into fewer larger patterns. Then
9
- # maybe the 12345 and abc patterns will be extractable.
10
- # On the whole I think the consensus is that EWT is not
11
- # that useful for predictive trading.
12
-
13
- class SQA::Indicator; class << self
14
-
15
- def elliott_wave_theory(
16
- prices # Array of prices
17
- )
18
- waves = []
19
- wave_start = 0
20
-
21
- (1..prices.length-2).each do |x|
22
- turning_point = prices[x] > prices[x-1] && prices[x] > prices[x+1] ||
23
- prices[x] < prices[x-1] && prices[x] < prices[x+1]
24
-
25
- if turning_point
26
- waves << prices[wave_start..x]
27
- wave_start = x + 1
28
- end
29
- end
30
-
31
- analysis = []
32
-
33
- waves.each do |wave|
34
- analysis << {
35
- wave: wave,
36
- pattern: ewt_identify_pattern(wave)
37
- }
38
- end
39
-
40
- analysis
41
- end
42
-
43
-
44
- private def ewt_identify_pattern(wave)
45
- if wave.length == 5
46
- :impulse
47
- elsif wave.length == 3
48
- :corrective_zigzag
49
- elsif wave.length > 5
50
- :corrective_complex
51
- else
52
- :unknown
53
- end
54
- end
55
-
56
- end; end
57
-
@@ -1,25 +0,0 @@
1
- # lib/sqa/indicator/exponential_moving_average.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def exponential_moving_average(
6
- prices, # Array of prices
7
- period # Integer number of entries to consider
8
- )
9
-
10
- ema_values = []
11
- ema_values << prices.first
12
-
13
- multiplier = (2.0 / (period + 1))
14
-
15
- (1...prices.length).each do |x|
16
- ema = (prices[x] - ema_values.last) * multiplier + ema_values.last
17
- ema_values << ema
18
- end
19
-
20
- ema_values
21
- end
22
- alias_method :ema, :exponential_moving_average
23
-
24
- end; end
25
-
@@ -1,36 +0,0 @@
1
- # lib/sqa/indicator/exponential_moving_average_trend.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def exponential_moving_average_trend(
6
- prices, # Array of prices
7
- period # Integer number of entries to consider
8
- )
9
-
10
- ema_values = exponential_moving_average(
11
- prices,
12
- period
13
- )
14
-
15
- last_ema = ema_values.last
16
- previous_ema = ema_values[-2]
17
-
18
- trend = if last_ema > previous_ema
19
- :up
20
- elsif last_ema < previous_ema
21
- :down
22
- else
23
- :neutral
24
- end
25
-
26
- {
27
- ema: ema_values,
28
- trend: trend,
29
- support: ema_values.min,
30
- resistance: ema_values.max
31
- }
32
- end
33
- alias_method :ema_trend, :exponential_moving_average_trend
34
-
35
- end; end
36
-
@@ -1,23 +0,0 @@
1
- # lib/sqa/indicator/fibonacci_retracement.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def fibonacci_retracement(
6
- swing_high, # Float peak price in a period - peak
7
- swing_low # Float bottom price in a period - valley
8
- )
9
- retracement_levels = []
10
-
11
- fibonacci_levels = [0.236, 0.382, 0.5, 0.618, 0.786]
12
-
13
- fibonacci_levels.each do |level|
14
- retracement_levels << swing_low + (swing_high - swing_low) * level
15
- end
16
-
17
-
18
- retracement_levels # Array
19
- end
20
- alias_method :fr, :fibonacci_retracement
21
-
22
- end; end
23
-
@@ -1,26 +0,0 @@
1
- # lib/sqa/indicator/head_and_shoulders_pattern.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
-
6
- def head_and_shoulders_pattern?(
7
- prices # Array of prices
8
- )
9
-
10
- return false if prices.length < 5
11
-
12
- data = prices.last(5)
13
-
14
- left_shoulder = data[0]
15
- head = data[1]
16
- right_shoulder = data[2]
17
- neckline = data[3]
18
- right_peak = data[4]
19
-
20
- head > left_shoulder &&
21
- head > right_shoulder &&
22
- right_peak < neckline
23
- end
24
-
25
- end; end
26
-
@@ -1,32 +0,0 @@
1
- # lib/sqa/indicator/market_profile.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def market_profile(
6
- volumes, # Array of volumes
7
- prices, # Array of prices
8
- support_threshold, # Float stock's support price estimate
9
- resistance_threshold # Float stock's resistance price estimate
10
- )
11
- total_volume = volumes.sum
12
- average_volume = volumes.mean
13
- max_volume = volumes.max
14
-
15
- support_levels = prices.select { |price| price <= support_threshold }
16
- resistance_levels = prices.select { |price| price >= resistance_threshold }
17
-
18
- if support_levels.empty? &&
19
- resistance_levels.empty?
20
- :neutral
21
- elsif support_levels.empty?
22
- :resistance
23
- elsif resistance_levels.empty?
24
- :support
25
- else
26
- :mixed
27
- end
28
- end
29
- alias_method :mp, :market_profile
30
-
31
- end; end
32
-
@@ -1,37 +0,0 @@
1
- # lib/sqa/indicator/mean_reversion.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- # @param prices [Array]
6
- # @param lookback_period [Integer]
7
- # @param deviation_threshold [Float]
8
- #
9
- # @return [Boolean] True if the stock exhibits mean reversion behavior,
10
- # false otherwise.
11
- #
12
- def mean_reversion?(
13
- prices, # Array of prices
14
- lookback_period, # Integer number of events to consider
15
- deviation_threshold # Float delta change at which a price is considered to be over extended
16
- )
17
-
18
- return false if prices.length < lookback_period
19
-
20
- mean = mr_mean(prices, lookback_period)
21
- deviation = prices[-1] - mean
22
-
23
- if deviation.abs > deviation_threshold
24
- return true
25
- else
26
- return false
27
- end
28
- end
29
-
30
-
31
- def mr_mean(prices, lookback_period)
32
- prices.last(lookback_period).sum / lookback_period.to_f
33
- end
34
-
35
- end; end
36
-
37
-
@@ -1,28 +0,0 @@
1
- # lib/sqa/indicator/momentum.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- # @param prices [Array]
6
- # @param period [Integer]
7
- #
8
- # @return [Float]
9
- #
10
- def momentum(
11
- prices, # Array of prices
12
- period # Integer number of entries to consider
13
- )
14
-
15
- momentums = []
16
-
17
- prices.each_cons(period) do |window|
18
- current_price = window.last.to_f
19
- past_price = window.first.to_f
20
- momentums << 10.0 * ( (current_price - past_price) / past_price)
21
- end
22
-
23
- momentums # Array
24
- end
25
- alias_method :m, :momentum
26
-
27
- end; end
28
-
@@ -1,29 +0,0 @@
1
- # lib/sqa/indicator/moving_average_convergence_divergence.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def moving_average_convergence_divergence(
6
- prices,
7
- short_period,
8
- long_period,
9
- signal_period
10
- )
11
-
12
- short_ma = simple_moving_average(prices, short_period)
13
- long_ma = simple_moving_average(prices, long_period)
14
- signal_line = simple_moving_average(short_ma, signal_period)
15
- macd_line = []
16
-
17
- prices.size.times do |x|
18
- macd_line << short_ma[x] - long_ma[x]
19
- end
20
-
21
- {
22
- macd: macd_line, # Array
23
- signal: signal_line # Array
24
- }
25
- end
26
- alias_method :macd, :moving_average_convergence_divergence
27
-
28
- end; end
29
-
@@ -1,29 +0,0 @@
1
-
2
- class SQA::Indicator; class << self
3
-
4
- def peaks_and_valleys(
5
- prices, # Array of prices
6
- delta # Integer distance delta (# of higher or lower prices to either side)
7
- )
8
- peaks = []
9
- valleys = []
10
- period = 2 * delta + 1
11
-
12
- prices.each_cons(period) do |window|
13
- price = window[delta]
14
-
15
- next if window.count(price) == period
16
-
17
- peaks << price if window.max == price
18
- valleys << price if window.min == price
19
- end
20
-
21
- {
22
- period: period,
23
- peaks: peaks,
24
- valleys: valleys
25
- }
26
- end
27
- alias_method :pav, :peaks_and_valleys
28
-
29
- end; end
@@ -1,202 +0,0 @@
1
- # lib/sqa/indicator/predict_next_values.rb
2
-
3
- module SQA
4
- end
5
-
6
- class SQA::Indicator; class << self
7
-
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|
88
- result << b + (b - a)
89
- end
90
-
91
- if window > 0
92
- (1..window).each do |_|
93
- last_two_values = result.last(2)
94
- delta = last_two_values.last - last_two_values.first
95
- next_value = last_two_values.last + delta
96
- result << next_value
97
- end
98
- end
99
-
100
- prediction_test(known, result.last(window)) if testing
101
-
102
- result.last(window)
103
- end
104
- alias_method :pnv, :predict_next_values
105
-
106
-
107
- def pnv2(stock, window, testing=false)
108
- prices = stock.df.adj_close_price.to_a
109
- known = prices.pop(window) if testing
110
-
111
- result = []
112
- last_inx = prices.size - 1 # indexes are zero based
113
-
114
- window.times do |x|
115
- x += 1 # forecasting 1 day into the future needs 2 days of near past data
116
-
117
- # window is the near past values
118
- window = prices[last_inx-x..]
119
-
120
- high = window.max
121
- low = window.min
122
- midpoint = (high + low) / 2.0
123
-
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)
149
- end
150
-
151
- prediction_test(known, result) if testing
152
-
153
- result
154
- end
155
-
156
-
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
164
-
165
- # Loop through the prediction window size
166
- (1..window).each do |x|
167
-
168
- # Calculate the percentage change between the current price and its previous price
169
- percentage_change = (current_price - prices[-x]) / prices[-x]
170
-
171
- result << current_price + (current_price * percentage_change)
172
- end
173
-
174
- prediction_test(known, result) if testing
175
-
176
- result
177
- end
178
-
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
201
-
202
- end; end
@@ -1,47 +0,0 @@
1
- # lib/sqa/indicator/relative_strength_index.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def relative_strength_index(
6
- prices, # Array of prices
7
- period, # Integer how many to consider at a time
8
- over_sold = 30.0, # Float break over point in trend
9
- over_bought = 70.0 # Float break over point in trend
10
- )
11
- gains = []
12
- losses = []
13
-
14
- prices.each_cons(2) do |pair|
15
- change = pair[1] - pair[0]
16
-
17
- if change > 0
18
- gains << change
19
- losses << 0
20
- else
21
- gains << 0
22
- losses << change.abs
23
- end
24
- end
25
-
26
- avg_gain = gains.last(period).sum / period.to_f
27
- avg_loss = losses.last(period).sum / period.to_f
28
- rs = avg_gain / avg_loss
29
- rsi = 100 - (100 / (1 + rs))
30
-
31
- trend = if rsi >= over_bought
32
- :over_bought
33
- elsif rsi <= over_sold
34
- :over_sold
35
- else
36
- :normal
37
- end
38
-
39
- {
40
- rsi: rsi, # Float
41
- trend: trend # Symbol :normal, :over_bought, :over+sold
42
- }
43
- end
44
- alias_method :rsi, :relative_strength_index
45
-
46
- end; end
47
-
@@ -1,24 +0,0 @@
1
- # lib/sqa/indicator/simple_moving_average.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def simple_moving_average(
6
- prices, # Array of prices
7
- period # Integer how many to consider at a time
8
- )
9
- moving_averages = []
10
-
11
- (0..period-2).to_a.each do |x|
12
- moving_averages << prices[0..x].mean
13
- end
14
-
15
- prices.each_cons(period) do |window|
16
- moving_averages << window.mean
17
- end
18
-
19
- moving_averages # Array
20
- end
21
- alias_method :sma, :simple_moving_average
22
-
23
- end; end
24
-
@@ -1,32 +0,0 @@
1
- # lib/sqa/indicator/simple_moving_average_trend.rb
2
-
3
- class SQA::Indicator; class << self
4
-
5
- def simple_moving_average_trend(
6
- prices, # Array of prices
7
- period, # Integer number of entries to consider
8
- delta = 1.0 # Float defines the angle range(+/-) for :neutral trend
9
- )
10
- sma = simple_moving_average(prices, period)
11
- last_sma = sma.last
12
- prev_sma = sma.last(period).first
13
- angle = Math.atan((last_sma - prev_sma) / period) * (180 / Math::PI)
14
-
15
- trend = if angle > delta
16
- :up
17
- elsif angle < -delta
18
- :down
19
- else
20
- :neutral
21
- end
22
-
23
- {
24
- sma: sma, # Array
25
- trend: trend, # Symbol :up, :down, :neutral
26
- angle: angle # Float how step the trend
27
- }
28
- end
29
- alias_method :sma_trend, :simple_moving_average_trend
30
-
31
- end; end
32
-