sqa 0.0.24 → 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.
- checksums.yaml +4 -4
- data/.goose/memory/development.txt +3 -0
- data/.semver +6 -0
- data/ARCHITECTURE.md +648 -0
- data/CHANGELOG.md +82 -0
- data/CLAUDE.md +653 -0
- data/COMMITS.md +196 -0
- data/DATAFRAME_ARCHITECTURE_REVIEW.md +421 -0
- data/NEXT-STEPS.md +154 -0
- data/README.md +812 -262
- data/TASKS.md +358 -0
- data/TEST_RESULTS.md +140 -0
- data/TODO.md +42 -0
- data/_notes.txt +25 -0
- data/bin/sqa-console +11 -0
- data/data/talk_talk.json +103284 -0
- data/develop_summary.md +313 -0
- data/docs/advanced/backtesting.md +206 -0
- data/docs/advanced/ensemble.md +68 -0
- data/docs/advanced/fpop.md +153 -0
- data/docs/advanced/index.md +112 -0
- data/docs/advanced/multi-timeframe.md +67 -0
- data/docs/advanced/pattern-matcher.md +75 -0
- data/docs/advanced/portfolio-optimizer.md +79 -0
- data/docs/advanced/portfolio.md +166 -0
- data/docs/advanced/risk-management.md +210 -0
- data/docs/advanced/strategy-generator.md +158 -0
- data/docs/advanced/streaming.md +209 -0
- data/docs/ai_and_ml.md +80 -0
- data/docs/api/dataframe.md +1115 -0
- data/docs/api/index.md +126 -0
- data/docs/assets/css/custom.css +88 -0
- data/docs/assets/js/mathjax.js +18 -0
- data/docs/concepts/index.md +68 -0
- data/docs/contributing/index.md +60 -0
- data/docs/data-sources/index.md +66 -0
- data/docs/data_frame.md +317 -97
- data/docs/factors_that_impact_price.md +26 -0
- data/docs/finviz.md +11 -0
- data/docs/fx_pro_bit.md +25 -0
- data/docs/genetic_programming.md +104 -0
- data/docs/getting-started/index.md +123 -0
- data/docs/getting-started/installation.md +229 -0
- data/docs/getting-started/quick-start.md +244 -0
- data/docs/i_gotta_an_idea.md +22 -0
- data/docs/index.md +163 -0
- data/docs/indicators/index.md +97 -0
- data/docs/indicators.md +110 -24
- data/docs/options.md +8 -0
- data/docs/strategies/bollinger-bands.md +146 -0
- data/docs/strategies/consensus.md +64 -0
- data/docs/strategies/custom.md +310 -0
- data/docs/strategies/ema.md +53 -0
- data/docs/strategies/index.md +92 -0
- data/docs/strategies/kbs.md +164 -0
- data/docs/strategies/macd.md +96 -0
- data/docs/strategies/market-profile.md +54 -0
- data/docs/strategies/mean-reversion.md +58 -0
- data/docs/strategies/rsi.md +95 -0
- data/docs/strategies/sma.md +55 -0
- data/docs/strategies/stochastic.md +63 -0
- data/docs/strategies/volume-breakout.md +54 -0
- data/docs/tags.md +7 -0
- data/docs/true_strength_index.md +46 -0
- data/docs/weighted_moving_average.md +48 -0
- data/examples/README.md +354 -0
- data/examples/advanced_features_example.rb +350 -0
- data/examples/fpop_analysis_example.rb +191 -0
- data/examples/genetic_programming_example.rb +148 -0
- data/examples/kbs_strategy_example.rb +208 -0
- data/examples/pattern_context_example.rb +300 -0
- data/examples/rails_app/Gemfile +34 -0
- data/examples/rails_app/README.md +416 -0
- data/examples/rails_app/app/assets/javascripts/application.js +107 -0
- data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
- data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
- data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
- data/examples/rails_app/app/controllers/application_controller.rb +22 -0
- data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
- data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
- data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
- data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
- data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
- data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
- data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
- data/examples/rails_app/app/views/errors/show.html.erb +17 -0
- data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
- data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
- data/examples/rails_app/bin/rails +6 -0
- data/examples/rails_app/config/application.rb +45 -0
- data/examples/rails_app/config/boot.rb +5 -0
- data/examples/rails_app/config/database.yml +18 -0
- data/examples/rails_app/config/environment.rb +11 -0
- data/examples/rails_app/config/routes.rb +26 -0
- data/examples/rails_app/config.ru +8 -0
- data/examples/realtime_stream_example.rb +274 -0
- data/examples/sinatra_app/Gemfile +22 -0
- data/examples/sinatra_app/QUICKSTART.md +159 -0
- data/examples/sinatra_app/README.md +461 -0
- data/examples/sinatra_app/app.rb +344 -0
- data/examples/sinatra_app/config.ru +5 -0
- data/examples/sinatra_app/public/css/style.css +659 -0
- data/examples/sinatra_app/public/js/app.js +107 -0
- data/examples/sinatra_app/views/analyze.erb +306 -0
- data/examples/sinatra_app/views/backtest.erb +325 -0
- data/examples/sinatra_app/views/dashboard.erb +419 -0
- data/examples/sinatra_app/views/error.erb +58 -0
- data/examples/sinatra_app/views/index.erb +118 -0
- data/examples/sinatra_app/views/layout.erb +61 -0
- data/examples/sinatra_app/views/portfolio.erb +43 -0
- data/examples/strategy_generator_example.rb +346 -0
- data/hsa_portfolio.csv +11 -0
- data/justfile +0 -0
- data/lib/api/alpha_vantage_api.rb +462 -0
- data/lib/sqa/backtest.rb +329 -0
- data/lib/sqa/data_frame/alpha_vantage.rb +43 -65
- data/lib/sqa/data_frame/data.rb +92 -0
- data/lib/sqa/data_frame/yahoo_finance.rb +35 -43
- data/lib/sqa/data_frame.rb +148 -243
- data/lib/sqa/ensemble.rb +359 -0
- data/lib/sqa/fpop.rb +199 -0
- data/lib/sqa/gp.rb +259 -0
- data/lib/sqa/indicator.rb +5 -8
- data/lib/sqa/init.rb +15 -8
- data/lib/sqa/market_regime.rb +240 -0
- data/lib/sqa/multi_timeframe.rb +379 -0
- data/lib/sqa/pattern_matcher.rb +497 -0
- data/lib/sqa/portfolio.rb +260 -6
- data/lib/sqa/portfolio_optimizer.rb +377 -0
- data/lib/sqa/risk_manager.rb +442 -0
- data/lib/sqa/seasonal_analyzer.rb +209 -0
- data/lib/sqa/sector_analyzer.rb +300 -0
- data/lib/sqa/stock.rb +67 -125
- data/lib/sqa/strategy/bollinger_bands.rb +42 -0
- data/lib/sqa/strategy/consensus.rb +5 -2
- data/lib/sqa/strategy/kbs_strategy.rb +470 -0
- data/lib/sqa/strategy/macd.rb +46 -0
- data/lib/sqa/strategy/mp.rb +1 -1
- data/lib/sqa/strategy/stochastic.rb +60 -0
- data/lib/sqa/strategy/volume_breakout.rb +57 -0
- data/lib/sqa/strategy.rb +5 -0
- data/lib/sqa/strategy_generator.rb +947 -0
- data/lib/sqa/stream.rb +361 -0
- data/lib/sqa/version.rb +1 -7
- data/lib/sqa.rb +23 -16
- data/main.just +81 -0
- data/mkdocs.yml +288 -0
- data/trace.log +0 -0
- metadata +261 -51
- data/bin/sqa +0 -6
- data/lib/patches/dry-cli.rb +0 -228
- data/lib/sqa/activity.rb +0 -10
- data/lib/sqa/cli.rb +0 -62
- data/lib/sqa/commands/analysis.rb +0 -309
- data/lib/sqa/commands/base.rb +0 -139
- data/lib/sqa/commands/web.rb +0 -199
- data/lib/sqa/commands.rb +0 -22
- data/lib/sqa/constants.rb +0 -23
- data/lib/sqa/indicator/average_true_range.rb +0 -33
- data/lib/sqa/indicator/bollinger_bands.rb +0 -28
- data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
- data/lib/sqa/indicator/donchian_channel.rb +0 -29
- data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
- data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
- data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
- data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
- data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
- data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
- data/lib/sqa/indicator/market_profile.rb +0 -32
- data/lib/sqa/indicator/mean_reversion.rb +0 -37
- data/lib/sqa/indicator/momentum.rb +0 -28
- data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
- data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
- data/lib/sqa/indicator/predict_next_value.rb +0 -202
- data/lib/sqa/indicator/relative_strength_index.rb +0 -47
- data/lib/sqa/indicator/simple_moving_average.rb +0 -24
- data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
- data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
- data/lib/sqa/indicator/true_range.rb +0 -39
- data/lib/sqa/trade.rb +0 -26
data/lib/sqa/commands.rb
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# sqa/lib/sqa/commands.rb
|
|
2
|
-
|
|
3
|
-
# Adds command options to SQA.config
|
|
4
|
-
require_relative "plugin_manager"
|
|
5
|
-
|
|
6
|
-
module SQA::Commands
|
|
7
|
-
# Establish the command registry
|
|
8
|
-
extend Dry::CLI::Registry
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
Commands = SQA::Commands
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
load_these_first = [
|
|
15
|
-
"#{__dir__}/commands/base.rb",
|
|
16
|
-
].each { |file| require_relative file }
|
|
17
|
-
|
|
18
|
-
Dir.glob("#{__dir__}/commands/*.rb")
|
|
19
|
-
.reject{|file| load_these_first.include? file}
|
|
20
|
-
.each do |file|
|
|
21
|
-
require_relative file
|
|
22
|
-
end
|
data/lib/sqa/constants.rb
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# lib/sqa/constants.rb
|
|
2
|
-
|
|
3
|
-
module SQA
|
|
4
|
-
module Constants
|
|
5
|
-
Signal = {
|
|
6
|
-
hold: 0,
|
|
7
|
-
buy: 1,
|
|
8
|
-
sell: 2
|
|
9
|
-
}.freeze
|
|
10
|
-
|
|
11
|
-
Trend = {
|
|
12
|
-
up: 0,
|
|
13
|
-
down: 1
|
|
14
|
-
}.freeze
|
|
15
|
-
|
|
16
|
-
Swing = {
|
|
17
|
-
valley: 0,
|
|
18
|
-
peak: 1,
|
|
19
|
-
}.freeze
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
include Constants
|
|
23
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/average_true_range.rb
|
|
2
|
-
|
|
3
|
-
# See Also: true_range
|
|
4
|
-
|
|
5
|
-
class SQA::Indicator; class << self
|
|
6
|
-
|
|
7
|
-
def average_true_range(
|
|
8
|
-
high_prices, # Array of the day's high price
|
|
9
|
-
low_prices, # Array of the day's low price
|
|
10
|
-
close_prices, # Array of the day's closing price
|
|
11
|
-
period # Integer the number of days to consider
|
|
12
|
-
)
|
|
13
|
-
true_ranges = true_range(high_prices, low_prices, close_prices)
|
|
14
|
-
atr_values = []
|
|
15
|
-
|
|
16
|
-
window_span = period - 1
|
|
17
|
-
|
|
18
|
-
true_ranges.size.times do |inx|
|
|
19
|
-
start_inx = inx - window_span
|
|
20
|
-
end_inx = start_inx + window_span
|
|
21
|
-
|
|
22
|
-
start_inx = 0 if start_inx < 0
|
|
23
|
-
|
|
24
|
-
window = true_ranges[start_inx..end_inx]
|
|
25
|
-
|
|
26
|
-
atr_values << window.mean
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
atr_values # Array
|
|
30
|
-
end
|
|
31
|
-
alias_method :atr, :average_true_range
|
|
32
|
-
|
|
33
|
-
end; end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/bollinger_bands.rb
|
|
2
|
-
|
|
3
|
-
class SQA::Indicator; class << self
|
|
4
|
-
|
|
5
|
-
def bollinger_bands(
|
|
6
|
-
prices, # Array of prices
|
|
7
|
-
period, # Integer number of entries to consider
|
|
8
|
-
num_std_devs=2 # Integer number of standard deviations
|
|
9
|
-
)
|
|
10
|
-
moving_averages = simple_moving_average(prices, period)
|
|
11
|
-
standard_deviations = []
|
|
12
|
-
|
|
13
|
-
prices.each_cons(period) do |window|
|
|
14
|
-
standard_deviation = Math.sqrt(window.map { |price| (price - moving_averages.last) ** 2 }.sum / period)
|
|
15
|
-
standard_deviations << standard_deviation
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
upper_band = moving_averages.last + (num_std_devs * standard_deviations.last)
|
|
19
|
-
lower_band = moving_averages.last - (num_std_devs * standard_deviations.last)
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
upper_band: upper_band, # Array
|
|
23
|
-
lower_band: lower_band # Array
|
|
24
|
-
}
|
|
25
|
-
end
|
|
26
|
-
alias_method :bb, :bollinger_bands
|
|
27
|
-
|
|
28
|
-
end; end
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/candlestick_pattern_recognizer.rb
|
|
2
|
-
|
|
3
|
-
class SQA::Indicator; class << self
|
|
4
|
-
|
|
5
|
-
def candlestick_pattern_recognizer(
|
|
6
|
-
open_prices, # Array day's ppen price
|
|
7
|
-
high_prices, # Array day's high price
|
|
8
|
-
low_prices, # Array day's low price
|
|
9
|
-
close_prices # Array day's closing price
|
|
10
|
-
)
|
|
11
|
-
patterns = []
|
|
12
|
-
|
|
13
|
-
close_prices.each_with_index do |close, index|
|
|
14
|
-
if index >= 2
|
|
15
|
-
previous_close = close_prices[index - 1]
|
|
16
|
-
previous_open = open_prices[index - 1]
|
|
17
|
-
previous_high = high_prices[index - 1]
|
|
18
|
-
previous_low = low_prices[index - 1]
|
|
19
|
-
|
|
20
|
-
second_previous_close = close_prices[index - 2]
|
|
21
|
-
second_previous_open = open_prices[index - 2]
|
|
22
|
-
second_previous_high = high_prices[index - 2]
|
|
23
|
-
second_previous_low = low_prices[index - 2]
|
|
24
|
-
|
|
25
|
-
if close > previous_close &&
|
|
26
|
-
previous_close < previous_open &&
|
|
27
|
-
close < previous_open &&
|
|
28
|
-
close > previous_low &&
|
|
29
|
-
close > second_previous_close
|
|
30
|
-
patterns << :bullish_engulfing
|
|
31
|
-
|
|
32
|
-
elsif close < previous_close &&
|
|
33
|
-
previous_close > previous_open &&
|
|
34
|
-
close > previous_open &&
|
|
35
|
-
close < previous_high &&
|
|
36
|
-
close < second_previous_close
|
|
37
|
-
patterns << :bearish_engulfing
|
|
38
|
-
|
|
39
|
-
elsif close > previous_close &&
|
|
40
|
-
previous_close < previous_open &&
|
|
41
|
-
close < previous_open &&
|
|
42
|
-
close < previous_low &&
|
|
43
|
-
close < second_previous_close
|
|
44
|
-
patterns << :bearish_harami
|
|
45
|
-
|
|
46
|
-
elsif close < previous_close &&
|
|
47
|
-
previous_close > previous_open &&
|
|
48
|
-
close > previous_open &&
|
|
49
|
-
close > previous_high &&
|
|
50
|
-
close > second_previous_close
|
|
51
|
-
patterns << :bullish_harami
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
patterns
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
end; end
|
|
60
|
-
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/donchian_channel.rb
|
|
2
|
-
|
|
3
|
-
class SQA::Indicator; class << self
|
|
4
|
-
|
|
5
|
-
def donchian_channel(
|
|
6
|
-
prices, # Array of prices
|
|
7
|
-
period # Integer number of entries to consider
|
|
8
|
-
)
|
|
9
|
-
max = -999999999
|
|
10
|
-
min = 999999999
|
|
11
|
-
donchian_channel = []
|
|
12
|
-
|
|
13
|
-
prices.each_with_index do |value, index|
|
|
14
|
-
value = value.to_f
|
|
15
|
-
max = value if value > max
|
|
16
|
-
min = value if value < min
|
|
17
|
-
|
|
18
|
-
if index >= period - 1
|
|
19
|
-
donchian_channel << [max, min, (max + min) / 2]
|
|
20
|
-
max = -999999999
|
|
21
|
-
min = 999999999
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
donchian_channel
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
end; end
|
|
29
|
-
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/double_top_bottom_pattern.rb
|
|
2
|
-
|
|
3
|
-
class SQA::Indicator; class << self
|
|
4
|
-
|
|
5
|
-
def double_top_bottom_pattern(
|
|
6
|
-
prices # Array of prices
|
|
7
|
-
)
|
|
8
|
-
return :no_pattern if prices.length < 5
|
|
9
|
-
|
|
10
|
-
data = prices.last(5)
|
|
11
|
-
|
|
12
|
-
first_peak = data[0]
|
|
13
|
-
valley = data[1]
|
|
14
|
-
second_peak = data[2]
|
|
15
|
-
neckline = data[3]
|
|
16
|
-
confirmation_price = data[4]
|
|
17
|
-
|
|
18
|
-
if first_peak < second_peak &&
|
|
19
|
-
valley > first_peak &&
|
|
20
|
-
valley > second_peak &&
|
|
21
|
-
confirmation_price < neckline
|
|
22
|
-
:double_top
|
|
23
|
-
elsif first_peak > second_peak &&
|
|
24
|
-
valley < first_peak &&
|
|
25
|
-
valley < second_peak &&
|
|
26
|
-
confirmation_price > neckline
|
|
27
|
-
:double_bottom
|
|
28
|
-
else
|
|
29
|
-
:no_pattern
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
end; end
|
|
34
|
-
|
|
@@ -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
|