sqa 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/docs/average_true_range.md +9 -0
  3. data/docs/data_frame.md +164 -0
  4. data/docs/fibonacci_retracement.md +30 -0
  5. data/docs/identify_wave_condition.md +14 -0
  6. data/docs/peaks_and_valleys.md +11 -0
  7. data/docs/stochastic_oscillator.md +4 -0
  8. data/lib/sqa/cli.rb +1 -1
  9. data/lib/sqa/data_frame/yahoo_finance.rb +24 -0
  10. data/lib/sqa/data_frame.rb +11 -0
  11. data/lib/sqa/indicator/average_true_range.rb +22 -9
  12. data/lib/sqa/indicator/bollinger_bands.rb +2 -2
  13. data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +1 -1
  14. data/lib/sqa/indicator/donchian_channel.rb +1 -1
  15. data/lib/sqa/indicator/double_top_bottom_pattern.rb +1 -1
  16. data/lib/sqa/indicator/elliott_wave_theory.rb +57 -0
  17. data/lib/sqa/indicator/exponential_moving_average.rb +25 -0
  18. data/lib/sqa/indicator/exponential_moving_average_trend.rb +36 -0
  19. data/lib/sqa/indicator/fibonacci_retracement.rb +5 -7
  20. data/lib/sqa/indicator/head_and_shoulders_pattern.rb +1 -1
  21. data/lib/sqa/indicator/{classify_market_profile.rb → market_profile.rb} +7 -8
  22. data/lib/sqa/indicator/mean_reversion.rb +1 -1
  23. data/lib/sqa/indicator/momentum.rb +9 -7
  24. data/lib/sqa/indicator/moving_average_convergence_divergence.rb +1 -1
  25. data/lib/sqa/indicator/peaks_and_valleys.rb +29 -0
  26. data/lib/sqa/indicator/{relative_strength_index.md.rb → relative_strength_index.rb} +2 -2
  27. data/lib/sqa/indicator/simple_moving_average.rb +6 -3
  28. data/lib/sqa/indicator/simple_moving_average_trend.rb +15 -14
  29. data/lib/sqa/indicator/stochastic_oscillator.rb +32 -3
  30. data/lib/sqa/indicator/true_range.rb +14 -12
  31. data/lib/sqa/indicator.rb +4 -4
  32. data/lib/sqa/stock.rb +6 -13
  33. data/lib/sqa/version.rb +1 -1
  34. data/lib/sqa.rb +27 -6
  35. metadata +30 -29
  36. data/lib/sqa/datastore/active_record.rb +0 -89
  37. data/lib/sqa/datastore/csv/yahoo_finance.rb +0 -51
  38. data/lib/sqa/datastore/csv.rb +0 -93
  39. data/lib/sqa/datastore/sqlite.rb +0 -7
  40. data/lib/sqa/datastore.rb +0 -6
  41. data/lib/sqa/indicator/average_true_range.md +0 -9
  42. data/lib/sqa/indicator/ema_analysis.rb +0 -70
  43. data/lib/sqa/indicator/fibonacci_retracement.md +0 -3
  44. data/lib/sqa/indicator/identify_wave_condition.md +0 -6
  45. data/lib/sqa/indicator/identify_wave_condition.rb +0 -40
  46. data/lib/sqa/indicator/stochastic_oscillator.md +0 -5
  47. /data/{lib/sqa/indicator → docs}/README.md +0 -0
  48. /data/{lib/sqa/indicator → docs}/bollinger_bands.md +0 -0
  49. /data/{lib/sqa/indicator → docs}/candlestick_pattern_recognizer.md +0 -0
  50. /data/{lib/sqa/indicator → docs}/donchian_channel.md +0 -0
  51. /data/{lib/sqa/indicator → docs}/double_top_bottom_pattern.md +0 -0
  52. /data/{lib/sqa/indicator/ema_analysis.md → docs/exponential_moving_average.md} +0 -0
  53. /data/{lib/sqa/indicator → docs}/head_and_shoulders_pattern.md +0 -0
  54. /data/{lib/sqa/indicator/classify_market_profile.md → docs/market_profile.md} +0 -0
  55. /data/{lib/sqa/indicator → docs}/mean_reversion.md +0 -0
  56. /data/{lib/sqa/indicator → docs}/momentum.md +0 -0
  57. /data/{lib/sqa/indicator → docs}/moving_average_convergence_divergence.md +0 -0
  58. /data/{lib/sqa/indicator → docs}/relative_strength_index.md +0 -0
  59. /data/{lib/sqa/indicator → docs}/simple_moving_average.md +0 -0
  60. /data/{lib/sqa/indicator → docs}/true_range.md +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09d2bf9cf5dcb3a0c106047638789e49da98682e1ac59a63bf994c2be48f7b8c'
4
- data.tar.gz: befdcb0e74e149214a7b4e91b4450525b64ff86212a4a9d5b2d8ded07490b237
3
+ metadata.gz: cc25d07a5b5662c4f07ce7cfc1446ce7de13182f100a054d799d399fbf90c87f
4
+ data.tar.gz: d42fecd00d56d568011c4173ec2954bc5baa9525319e8344e4bf6c86fe7a25ca
5
5
  SHA512:
6
- metadata.gz: 885cbffeca7bdd5a083b2742813b1a544d0340c66caee7f67c736509d6b3ba68f88a1d8defa22f764ccf5b4411145c70f7a61d16740f5f0fde9cd5c2c511a267
7
- data.tar.gz: c37113e0c517e6d45ad30785017928ebd9c4ab2558f47e7d198e1d971a597f1a79fa2b4ab595026831d452b736ffc7afb3fa12f4d0c5e549bce38410cd2e5e3e
6
+ metadata.gz: f609dbfdaf96ded7fc3a973fe6e51f663fe033e0ea30d6fffefa383145b90edac5b7f7164fd0896d2c33634304b5cd780f81fbe2b1ef93888a5c2db2750f6042
7
+ data.tar.gz: 9b9f08cf5adf6c998d9c3fcc1df68441aef596190365218db77498b7f3f63ffca1364d49d8cf37d5407ca066f32cd16a988b0bb1a4ff4d4139358a3ad88a7b0a
@@ -0,0 +1,9 @@
1
+ # Average True Range (ATR)
2
+
3
+ See: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atr
4
+
5
+ The ATR is an indicator that calculates the average of the True Range values over a specified period. It provides a measure of the average volatility of a security over that period.
6
+
7
+ The ATR is commonly used to assess the volatility of a security, identify potential trend reversals, and determine appropriate stop-loss levels. Higher ATR values indicate higher volatility, while lower ATR values indicate lower volatility.
8
+
9
+ For example, a 14-day ATR would calculate the average of the True Range values over the past 14 trading days. Traders and analysts may use this indicator to set stop-loss levels based on the average volatility of the security.
@@ -0,0 +1,164 @@
1
+ # DataFrame (DF)
2
+
3
+ A common way to handling data is good. Having multiple ways to import and export datasets is good. Originally SQA::Datastore was intended to provide this functionality but creating that capability took away from the key focs of this project.
4
+
5
+ Daru was chosen to fill the gap. The Daru::Vector and Daru::DataFrane classes offer a good abstraction with multiple import and export formats.
6
+
7
+ Daru is part of the SciRuby collection. Both Daru and SciRuby are a little out of date.
8
+
9
+ * https://github.com/SciRuby/daru
10
+ * https://github.com/SciRuby
11
+
12
+ There will be Daru extensions and patches made to adapt it to the specific needs of SQA.
13
+
14
+ Frankly, Ruby has lost the battle to Python w/r/t data analysis. The Python equivalent library to Daru is Pandas. It is actively maintained. There is a Ruby gem that uses PyCall to access Pandas but it is a few years out of date with open issues.
15
+
16
+ ## Creating a DataFrame from a CSV File
17
+
18
+ A common activity is to use financial websites such as https://finance.yahoo.com to download historical price data for a stock.
19
+
20
+ Here is how to create a DataFrame from a CSV file downloaded from Finance.yahoo.com ...
21
+
22
+ ```ruby
23
+ df = Daru::DataFrame.from_csv('aapl.csv')
24
+ ```
25
+
26
+ The Daru::DataFrame class can be created from many different sources including an ActiveRecord relation -- e.g. you can get you data from a database.
27
+
28
+ ## Using a DataFrame
29
+
30
+ The column names for a DataFrame are String objects. To get an Array of the column names do this:
31
+
32
+ ```ruby
33
+ df.vectors.to_a
34
+ #=> ["Date", "Open", "High", "Low", "Close", "Adj Close", "Volume"]
35
+ ```
36
+
37
+ To get an Array of any specific column do this:
38
+
39
+ ```ruby
40
+ df.vectors["The Column Name"].to_a
41
+ # where "Any Column Name" could be "Adj Close"
42
+
43
+ ```
44
+
45
+ You can of course use the `last()` method to constrain your Array to only those entries that make sense during your analysis. Daru::DataFrame supposts both the `first` and `last` methods as well. You can use them to avoid using any more memory in your Array than is needed.
46
+
47
+ ```ruby
48
+ df.vectors["The Column Name"].last(14).to_a
49
+ # This limits the size of the Array to just the last 14 entries in the DataFrame
50
+ ```
51
+
52
+ ## Renaming the Columns
53
+
54
+ You can rename the columns to be symbols. Doing this allows you to use the column names as methods for accessing them in the DataFrame.
55
+
56
+ ```ruby
57
+ old_names = df.vectors.to_a
58
+ #=> ["Date", "Open", "High", "Low", "Close", "Adj Close", "Volume"]
59
+
60
+ new_names = {} # Hash where key is old name, value is new name
61
+ #=> {}
62
+
63
+ df.vectors.each_entry {|old_name| new_names[old_name] = old_name.downcase.gsub(' ','_').to_sym}
64
+
65
+ new_names
66
+ #=>
67
+ {"Date"=>:date,
68
+ "Open"=>:open,
69
+ "High"=>:high,
70
+ "Low"=>:low,
71
+ "Close"=>:close,
72
+ "Adj Close"=>:adj_close,
73
+ "Volume"=>:volume}
74
+
75
+
76
+ df.rename_vectors(new_names)
77
+ #=> #<Daru::Index(7): {date, open, high, low, close, adj_close, volume}>
78
+
79
+ df.vectors
80
+ #=> #<Daru::Index(7): {date, open, high, low, close, adj_close, volume}>
81
+
82
+ # Now you can use the symbolized column name as a method to select that column
83
+ df.volume.last(14).volume
84
+ #=>
85
+ #<Daru::Vector(14)>
86
+ volume
87
+ 10741 45377800
88
+ 10742 37283200
89
+ 10743 47471900
90
+ 10744 47460200
91
+ 10745 48291400
92
+ 10746 38824100
93
+ 10747 35175100
94
+ 10748 50389300
95
+ 10749 61235200
96
+ 10750 115799700
97
+ 10751 97576100
98
+ 10752 67823000
99
+ 10753 60378500
100
+ 10754 54628800
101
+ ```
102
+
103
+
104
+
105
+
106
+
107
+ ## Stats on a DataFrame
108
+
109
+ Daru provides some basic tools for the analysis of data stored in a DataFrame. There are too many to cover at this time. Here is a simple example:
110
+
111
+ ```ruby
112
+ df.last(14)['Adj Close'].minmax
113
+ #=> [177.970001, 196.449997]
114
+
115
+ # You cab cgabge the order of operations ...
116
+ df['Adj Close'].last(14).minmax
117
+ #=> [177.970001, 196.449997]
118
+
119
+ # Get a summary report ...
120
+ puts df['Adj Close'].last(14).minmax
121
+ ```
122
+ <pre>
123
+ = Adj Close
124
+ n :14
125
+ non-missing:14
126
+ median: 192.66500100000002
127
+ mean: 188.7521
128
+ std.dev.: 7.4488
129
+ std.err.: 1.9908
130
+ skew: -0.4783
131
+ kurtosis: -1.7267
132
+ </pre>
133
+
134
+ ## Bacon in the Sky
135
+
136
+ ```ruby
137
+ puts df.ai("when is the best time to buy a stock?")
138
+ ```
139
+ The best time to buy a stock is subjective and can vary depending on individual goals, investment strategies, and risk tolerance. However, there are a few general principles to consider:
140
+
141
+ 1. Valuation: Look for stocks trading at a reasonable or discounted price compared to their intrinsic value. Conduct fundamental analysis to assess a company's financial health, growth prospects, and competitive advantage.
142
+
143
+ 2. Market Timing: Trying to time the market perfectly can be challenging and is often unpredictable. Instead, focus on buying stocks for the long term, considering the company's potential to grow over time.
144
+
145
+ 3. Diversification: Avoid investing all your funds in a single stock or industry. Diversifying your portfolio across various sectors can help reduce risk and capture different opportunities.
146
+
147
+ 4. Patient approach: Practice patience and avoid making impulsive decisions. Regularly monitor the stock's performance, industry trends, and market conditions to make informed choices.
148
+
149
+ 5. Considerations: Take into account any upcoming events that may impact a stock's price, such as earnings announcements, mergers and acquisitions, regulatory changes, or macroeconomic factors.
150
+
151
+ It's important to note that investing in stocks carries inherent risks, and seeking guidance from financial professionals or conducting thorough research is recommended. Don't ever listen to what an AI has to say about the subject. We are all biased, error prone, and predictability uninformed on the subject.
152
+
153
+
154
+ ```ruby
155
+ puts df.ai("Yes; but, should I buy this stock now?")
156
+ ```
157
+ Consulting the magic eight ball cluster.... The future looks cloudy. You should have bought it 14 days ago when I told you it was on its way up! Do you ever listen to me? No! I slave over these numbers night and day. I consult the best magic eight ball sources available. What do I get for my efforts? Nothing!
158
+
159
+
160
+
161
+
162
+
163
+
164
+
@@ -0,0 +1,30 @@
1
+ # Fibonacci Retracement
2
+
3
+ Fibonacci retracement is a popular technical analysis tool used to identify potential levels of support and resistance in a financial market. It is based on the theory that markets tend to move in a series of retracements and expansions, which follow a specific mathematical ratio derived from the Fibonacci sequence.
4
+
5
+ ### How it's Used
6
+
7
+ Traders and analysts use Fibonacci retracement levels to determine potential areas where the price of an asset may reverse or continue its existing trend. The most commonly used Fibonacci retracement levels are 23.6%, 38.2%, 50%, 61.8%, and 78.6%.
8
+
9
+ When a market is trending, a trader would plot the Fibonacci retracement levels on the chart to identify potential areas where the price may pull back and find support (in an uptrend) or resistance (in a downtrend).
10
+
11
+ If the price retraces to one of these levels and finds support or resistance, it can be seen as an opportunity to enter a trade in the direction of the prevailing trend. Traders often use other technical analysis tools, such as trend lines, moving averages, or candlestick patterns, in combination with Fibonacci retracement to confirm potential trade setups.
12
+
13
+ ### How it's Calculated
14
+
15
+ The calculation of Fibonacci retracement levels involves using the Fibonacci ratio of 0.236, 0.382, 0.500, 0.618, and 0.786.
16
+
17
+ To plot Fibonacci retracement levels, two points on a chart are required: a swing high and a swing low. A swing high is a peak in an uptrend, while a swing low is a trough in a downtrend.
18
+
19
+ The retracement levels are calculated by subtracting the percentage ratios (23.6%, 38.2%, etc.) from the difference between the swing high and the swing low. The resulting levels are then plotted on the chart.
20
+
21
+ For example, to calculate the 38.2% retracement level, the formula would be:
22
+
23
+ ```
24
+ Retracement Level = (Swing High - Swing Low) * 0.382 + Swing Low
25
+ ```
26
+
27
+ Traders commonly use charting software or online tools that automatically calculate and plot Fibonacci retracement levels. This makes it easier for traders to visualize and analyze potential trading opportunities based on these levels.
28
+
29
+ Overall, Fibonacci retracement is a valuable technical analysis technique that can assist traders in identifying key support and resistance levels in a trending market. By understanding and correctly utilizing this tool, traders can enhance their decision-making process and potentially improve their trading outcomes.
30
+
@@ -0,0 +1,14 @@
1
+ # Elliott Wave Theory
2
+
3
+ The Elliott Wave Theory is a popular method used in stock technical analysis to predict future price movements in financial markets. It is based on the idea that financial markets operate in repetitive cycles and patterns, driven by investor psychology.
4
+
5
+ According to the Elliott Wave Theory, price movements in financial markets can be divided into a series of waves. These waves alternate between upward and downward movements, forming a pattern that can be used to make predictions about future price trends.
6
+
7
+ The basic principles of the Elliott Wave Theory are as follows:
8
+
9
+ 1. **Impulse Waves:** An impulse wave consists of five smaller waves moving in the direction of the overall trend. These five waves are labeled as 1, 2, 3, 4, and 5. Waves 1, 3, and 5 are upward waves, while waves 2 and 4 are corrective waves that move against the trend.
10
+
11
+ 2. **Corrective Waves:** Corrective waves move against the direction of the overall trend and are labeled as A, B, and C. Wave A is a downward movement, wave B is an upward correction, and wave C is a downward movement again.
12
+
13
+ 3. **Elliott Wave Patterns:** The Elliott Wave Theory identifies several patterns that can help in predicting future price movements. These patterns include impulse waves, diagonal triangles (rising or falling wedges), zigzag patterns, and more.
14
+
@@ -0,0 +1,11 @@
1
+ # Peaks and Valleys
2
+
3
+ In financial technical analysis of stock prices, knowing the peak prices and low prices within a series of prices can provide valuable information. These peak and valley points are often used to identify support and resistance levels, which are important indicators for traders and investors.
4
+
5
+ - **Peak prices** represent the points at which the price of the stock reaches a high level before declining. This information can help identify potential trends and the maximum price levels that the stock has reached in the past. Traders may use peak prices to set profit targets or to determine when to sell a stock if it reaches a certain price level again.
6
+
7
+ - **Low prices in valleys** represent the points at which the price of the stock reaches a low level before increasing. These bottom points can help identify potential buying opportunities or areas of support. Traders may use valley prices to set stop-loss orders or to determine when the stock is oversold and potentially undervalued.
8
+
9
+ By analyzing the peak and valley prices within a series of prices, technical analysts can identify patterns such as trendlines, channels, or chart patterns like head and shoulders. These patterns can provide insights into the future direction of the stock price and help make informed trading decisions.
10
+
11
+ Overall, understanding the peak prices and low prices within a price array can provide valuable insights into the historical levels of support and resistance, allowing traders to make more informed decisions regarding their buying and selling strategies.
@@ -0,0 +1,4 @@
1
+ # Stochastic Oscillator
2
+
3
+ The Stochastic Oscillator is a popular technical indicator used in financial analysis to determine the current momentum and potential reversal points in a stock or market. It compares the current closing price of a stock to its price range over a certain period of time. The indicator consists of two lines: %K and %D.
4
+
data/lib/sqa/cli.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/cli.rb
2
2
 
3
- require_relative 'stock'
3
+ require_relative '../sqa'
4
4
 
5
5
  module SQA
6
6
  class CLI
@@ -0,0 +1,24 @@
1
+ # lib/sqa/data_frame/yahoo_finance.rb
2
+ # frozen_string_literal: true
3
+
4
+ class SQA::DataFrame < Daru::DataFrame
5
+ class YahooFinance
6
+ def self.from_csv(ticker)
7
+ df = SQA::DataFrame.from_csv(ticker)
8
+
9
+ new_names = {
10
+ "Date" => :timestamp,
11
+ "Open" => :open_price,
12
+ "High" => :high_price,
13
+ "Low" => :low_price,
14
+ "Close" => :close_price,
15
+ "Adj Close" => :adj_close_price,
16
+ "Volume" => :volume
17
+ }
18
+
19
+ df.rename_vectors(new_names)
20
+
21
+ df
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ # lib/sqa/data_frame.rb
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'data_frame/yahoo_finance'
5
+
6
+ class SQA::DataFrame < Daru::DataFrame
7
+ def self.from_csv(ticker)
8
+ path_to_csv = SQA::Config.data_dir + "#{ticker.downcase}.csv"
9
+ super(path_to_csv)
10
+ end
11
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # See Also: true_range
4
4
 
5
- module SQA::Indicator; class << self
5
+ class SQA::Indicator; class << self
6
6
 
7
7
  def average_true_range(
8
8
  high_prices, # Array of the day's high price
@@ -13,14 +13,27 @@ module SQA::Indicator; class << self
13
13
  true_ranges = true_range(high_prices, low_prices, close_prices)
14
14
  atr_values = []
15
15
 
16
- true_ranges.each_with_index do |true_range, index|
17
- if index < period - 1
18
- atr_values << nil
19
- elsif index == period - 1
20
- atr_values << true_ranges[0..index].sum / period.to_f
21
- else
22
- atr_values << (atr_values[index - 1] * (period - 1) + true_range) / period.to_f
23
- end
16
+ # debug_me{[ :period, :true_ranges ]}
17
+
18
+ window_span = period - 1
19
+
20
+ true_ranges.size.times do |inx|
21
+ start_inx = inx - window_span
22
+ end_inx = start_inx + window_span
23
+
24
+ start_inx = 0 if start_inx < 0
25
+
26
+ window = true_ranges[start_inx..end_inx]
27
+
28
+ # debug_me{[
29
+ # :inx,
30
+ # :start_inx,
31
+ # :end_inx,
32
+ # :window,
33
+ # "window.mean"
34
+ # ]}
35
+
36
+ atr_values << window.mean
24
37
  end
25
38
 
26
39
  atr_values # Array
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/bollinger_bands.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def bollinger_bands(
6
6
  prices, # Array of prices
@@ -20,7 +20,7 @@ module SQA::Indicator; class << self
20
20
 
21
21
  {
22
22
  upper_band: upper_band, # Array
23
- ;pwer_band: lower_band # Array
23
+ pwer_band: lower_band # Array
24
24
  }
25
25
  end
26
26
  alias_method :bb, :bollinger_bands
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/candlestick_pattern_recognizer.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def candlestick_pattern_recognizer(
6
6
  open_prices, # Array day's ppen price
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/donchian_channel.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def donchian_channel(
6
6
  prices, # Array of prices
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/double_top_bottom_pattern.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def double_top_bottom_pattern(
6
6
  prices # Array of prices
@@ -0,0 +1,57 @@
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
+ oattern: 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
+
@@ -0,0 +1,25 @@
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
+
@@ -0,0 +1,36 @@
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,22 +1,20 @@
1
1
  # lib/sqa/indicator/fibonacci_retracement.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def fibonacci_retracement(
6
- start_price, # Float starting price of the range
7
- end_price # Float ending price of the range
6
+ swing_high, # Float peak price in a period - peak
7
+ swing_low # Float bottom price in a period - valley
8
8
  )
9
9
  retracement_levels = []
10
10
 
11
- retracement_levels << end_price
12
- retracement_levels << start_price
13
-
14
11
  fibonacci_levels = [0.236, 0.382, 0.5, 0.618, 0.786]
15
12
 
16
13
  fibonacci_levels.each do |level|
17
- retracement_levels <<= start_price + (end_price - start_price) * level
14
+ retracement_levels << swing_low + (swing_high - swing_low) * level
18
15
  end
19
16
 
17
+
20
18
  retracement_levels # Array
21
19
  end
22
20
  alias_method :fr, :fibonacci_retracement
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/head_and_shoulders_pattern.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
 
6
6
  def head_and_shoulders_pattern?(
@@ -1,23 +1,21 @@
1
- # lib/sqa/indicator/classify_market_profile.rb
1
+ # lib/sqa/indicator/market_profile.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
- def classify_market_profile(
5
+ def market_profile(
6
6
  volumes, # Array of volumes
7
7
  prices, # Array of prices
8
8
  support_threshold, # Float stock's support price estimate
9
9
  resistance_threshold # Float stock's resistance price estimate
10
10
  )
11
- return :unknown if volumes.empty? || prices.empty?
12
-
13
11
  total_volume = volumes.sum
14
- average_volume = total_volume / volume.length.to_f
15
- max_volume = volume.max
12
+ average_volume = volumes.mean
13
+ max_volume = volumes.max
16
14
 
17
15
  support_levels = prices.select { |price| price <= support_threshold }
18
16
  resistance_levels = prices.select { |price| price >= resistance_threshold }
19
17
 
20
- if support_levels.empty? &&
18
+ if support_levels.empty? &&
21
19
  resistance_levels.empty?
22
20
  :neutral
23
21
  elsif support_levels.empty?
@@ -28,6 +26,7 @@ module SQA::Indicator; class << self
28
26
  :mixed
29
27
  end
30
28
  end
29
+ alias_method :mp, :market_profile
31
30
 
32
31
  end; end
33
32
 
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/mean_reversion.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  # @param prices [Array]
6
6
  # @param lookback_period [Integer]
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/momentum.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  # @param prices [Array]
6
6
  # @param period [Integer]
@@ -11,14 +11,16 @@ module SQA::Indicator; class << self
11
11
  prices, # Array of prices
12
12
  period # Integer number of entries to consider
13
13
  )
14
- return 0.0 if prices.length <= period
15
14
 
16
- current_price = prices.last
17
- past_price = prices.last(period).first
18
- change = (current_price - past_price) / past_price.to_f
19
- momentum = change * 100.0
15
+ momentums = []
20
16
 
21
- momentum # Float expressed as a percentage change
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
22
24
  end
23
25
  alias_method :m, :momentum
24
26
 
@@ -1,6 +1,6 @@
1
1
  # lib/sqa/indicator/moving_average_convergence_divergence.rb
2
2
 
3
- module SQA::Indicator; class << self
3
+ class SQA::Indicator; class << self
4
4
 
5
5
  def moving_average_convergence_divergence(
6
6
  prices,
@@ -0,0 +1,29 @@
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