sqa 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.irbrc +3 -0
- data/checksums/sqa-0.0.2.gem.sha512 +1 -0
- data/docs/average_true_range.md +9 -0
- data/docs/data_frame.md +164 -0
- data/docs/fibonacci_retracement.md +30 -0
- data/docs/identify_wave_condition.md +14 -0
- data/docs/peaks_and_valleys.md +11 -0
- data/docs/requirements.md +22 -0
- data/docs/stochastic_oscillator.md +4 -0
- data/docs/strategy.md +5 -0
- data/lib/sqa/cli.rb +1 -1
- data/lib/sqa/data_frame/yahoo_finance.rb +24 -0
- data/lib/sqa/data_frame.rb +16 -0
- data/lib/sqa/indicator/average_true_range.rb +22 -9
- data/lib/sqa/indicator/bollinger_bands.rb +2 -2
- data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +1 -1
- data/lib/sqa/indicator/donchian_channel.rb +1 -1
- data/lib/sqa/indicator/double_top_bottom_pattern.rb +1 -1
- data/lib/sqa/indicator/elliott_wave_theory.rb +57 -0
- data/lib/sqa/indicator/exponential_moving_average.rb +25 -0
- data/lib/sqa/indicator/exponential_moving_average_trend.rb +36 -0
- data/lib/sqa/indicator/fibonacci_retracement.rb +5 -7
- data/lib/sqa/indicator/head_and_shoulders_pattern.rb +1 -1
- data/lib/sqa/indicator/{classify_market_profile.rb → market_profile.rb} +7 -8
- data/lib/sqa/indicator/mean_reversion.rb +1 -1
- data/lib/sqa/indicator/momentum.rb +9 -7
- data/lib/sqa/indicator/moving_average_convergence_divergence.rb +7 -3
- data/lib/sqa/indicator/peaks_and_valleys.rb +29 -0
- data/lib/sqa/indicator/{relative_strength_index.md.rb → relative_strength_index.rb} +2 -2
- data/lib/sqa/indicator/simple_moving_average.rb +6 -3
- data/lib/sqa/indicator/simple_moving_average_trend.rb +15 -14
- data/lib/sqa/indicator/stochastic_oscillator.rb +32 -3
- data/lib/sqa/indicator/true_range.rb +14 -12
- data/lib/sqa/indicator.rb +4 -4
- data/lib/sqa/stock.rb +6 -13
- data/lib/sqa/strategy.rb +65 -0
- data/lib/sqa/version.rb +3 -1
- data/lib/sqa.rb +44 -6
- metadata +34 -29
- data/lib/sqa/datastore/active_record.rb +0 -89
- data/lib/sqa/datastore/csv/yahoo_finance.rb +0 -51
- data/lib/sqa/datastore/csv.rb +0 -93
- data/lib/sqa/datastore/sqlite.rb +0 -7
- data/lib/sqa/datastore.rb +0 -6
- data/lib/sqa/indicator/average_true_range.md +0 -9
- data/lib/sqa/indicator/ema_analysis.rb +0 -70
- data/lib/sqa/indicator/fibonacci_retracement.md +0 -3
- data/lib/sqa/indicator/identify_wave_condition.md +0 -6
- data/lib/sqa/indicator/identify_wave_condition.rb +0 -40
- data/lib/sqa/indicator/stochastic_oscillator.md +0 -5
- /data/{lib/sqa/indicator → docs}/README.md +0 -0
- /data/{lib/sqa/indicator → docs}/bollinger_bands.md +0 -0
- /data/{lib/sqa/indicator → docs}/candlestick_pattern_recognizer.md +0 -0
- /data/{lib/sqa/indicator → docs}/donchian_channel.md +0 -0
- /data/{lib/sqa/indicator → docs}/double_top_bottom_pattern.md +0 -0
- /data/{lib/sqa/indicator/ema_analysis.md → docs/exponential_moving_average.md} +0 -0
- /data/{lib/sqa/indicator → docs}/head_and_shoulders_pattern.md +0 -0
- /data/{lib/sqa/indicator/classify_market_profile.md → docs/market_profile.md} +0 -0
- /data/{lib/sqa/indicator → docs}/mean_reversion.md +0 -0
- /data/{lib/sqa/indicator → docs}/momentum.md +0 -0
- /data/{lib/sqa/indicator → docs}/moving_average_convergence_divergence.md +0 -0
- /data/{lib/sqa/indicator → docs}/relative_strength_index.md +0 -0
- /data/{lib/sqa/indicator → docs}/simple_moving_average.md +0 -0
- /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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48ca0637f3792531412ee5d07c2fd49a28f167a04f797d535a5d81e8b47907fb
|
4
|
+
data.tar.gz: e03b6c57017dab18c86a7e162ee4f9ff8c19567df92b29b0f132349959f802a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d373732f21ea218e6b2614cd8ef45d93e23e59dbfada17bded86974559c0ca4a99426bcf131ddbd5a6286edbd27e7212f1fbe9c8c3ceaa6eeca1ef9b4888451
|
7
|
+
data.tar.gz: b1c2b23bc8abd33c20c1337481c6f8e51243fe946fd6738f4cdea4f3f0c746233730f4b3cfc13710fc0f9e11dce0b248d22acfb08b21a7200599edc3a7b12a4f
|
data/.irbrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ad94810d7678366fc822ec4169e38e67542ebfe442ad64bd03faa6af781854e5c5943eddde65cc5f9251d7d3f7a0242f41b0da423cdc62cb9df9c9581b3b987a
|
@@ -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.
|
data/docs/data_frame.md
ADDED
@@ -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.
|
data/docs/requirements.md
CHANGED
@@ -38,3 +38,25 @@ Most reliable way of getting data is the scrape the website. The gem financial_
|
|
38
38
|
## Extract Indicators
|
39
39
|
|
40
40
|
After sleeping on it, I think the original plan with the fin_tech gem is a better idea for how to package the indicators. I'm going to keep the name FinTech for now while I think of something better. These are indicators; but I want them to be class-level methods with established contracts in their API.
|
41
|
+
|
42
|
+
The indicators in lib/sqa/indicator are stand-alone class methods; but, its so handy to have them in this repo. I will keep them here for a while
|
43
|
+
|
44
|
+
## Configuration
|
45
|
+
|
46
|
+
SQA::Config is managed by the gem "mixlib-config" See the gem for full documentation.
|
47
|
+
|
48
|
+
The TL;DR is:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
require 'sqa'
|
52
|
+
# read a configuration file in yaml, toml, json, ruby
|
53
|
+
# or just accept the defaults
|
54
|
+
SQA::Config.from_file(path_to_file)
|
55
|
+
|
56
|
+
# Initialize the environment
|
57
|
+
SQA.init
|
58
|
+
```
|
59
|
+
|
60
|
+
## Strategy Framework
|
61
|
+
|
62
|
+
Got the first ideas for handling strategies. in place.
|
@@ -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/docs/strategy.md
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# Strategy
|
2
|
+
|
3
|
+
A strategy is a recipe that cooks all the indicators together to make a decision on a potential trade. The SQA::Strategy class provides the framework for executing multiple strategies.
|
4
|
+
|
5
|
+
You can also think of a strategy as a set of rules like in the old days of rule-based forward/backward chaining engines. The rules are evaluated to determine whether a specific decision to trade is good or bad.
|
data/lib/sqa/cli.rb
CHANGED
@@ -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,16 @@
|
|
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.path(filename)
|
8
|
+
SQA::Config.data_dir + filename
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_csv(ticker)
|
12
|
+
df = super(path("#{ticker.downcase}.csv"))
|
13
|
+
df[:ticker] = ticker
|
14
|
+
df
|
15
|
+
end
|
16
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# See Also: true_range
|
4
4
|
|
5
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
23
|
+
lower_band: lower_band # Array
|
24
24
|
}
|
25
25
|
end
|
26
26
|
alias_method :bb, :bollinger_bands
|
@@ -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
|
+
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
|
+
|
@@ -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
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
5
|
def fibonacci_retracement(
|
6
|
-
|
7
|
-
|
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
|
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,23 +1,21 @@
|
|
1
|
-
# lib/sqa/indicator/
|
1
|
+
# lib/sqa/indicator/market_profile.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
|
-
def
|
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 =
|
15
|
-
max_volume =
|
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
|
|