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
|
@@ -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
|
-
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/stochastic_oscillator.rb
|
|
2
|
-
|
|
3
|
-
class SQA::Indicator; class << self
|
|
4
|
-
|
|
5
|
-
# @param high_prices [Array]
|
|
6
|
-
# @param low_prices [Array]
|
|
7
|
-
# @param close_prices [Array]
|
|
8
|
-
# @param period [Integer]
|
|
9
|
-
# @param smoothing_period [Integer]
|
|
10
|
-
#
|
|
11
|
-
# @return [Array] An array of %K and %D values.
|
|
12
|
-
#
|
|
13
|
-
def stochastic_oscillator(
|
|
14
|
-
high_prices, # Array of high prices
|
|
15
|
-
low_prices, # Array of low prices
|
|
16
|
-
closing_prices, # Array of closing prices
|
|
17
|
-
period, # Integer The period for calculating the Stochastic Oscillator
|
|
18
|
-
smoothing_period # Integer The smoothing period for %K line
|
|
19
|
-
)
|
|
20
|
-
k_values = []
|
|
21
|
-
d_values = []
|
|
22
|
-
|
|
23
|
-
closing_prices.each_cons(period) do |window|
|
|
24
|
-
highest_high = high_prices.max(period)
|
|
25
|
-
lowest_low = low_prices.min(period)
|
|
26
|
-
current_close = window.last
|
|
27
|
-
k_values << (current_close - lowest_low) / (highest_high - lowest_low) * 100 # Calculate the k_value
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
k_values.each_cons(smoothing_period) do |k_values_subset|
|
|
31
|
-
d_values << k_values_subset.sum / smoothing_period.to_f # Calculate the d_value
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
[k_values, d_values]
|
|
35
|
-
end
|
|
36
|
-
alias_method :so, :stochastic_oscillator
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def stochastic_oscillator2(
|
|
40
|
-
prices, # Array of prices
|
|
41
|
-
period # Integer number of events to consider
|
|
42
|
-
)
|
|
43
|
-
k_values = []
|
|
44
|
-
d_values = []
|
|
45
|
-
|
|
46
|
-
prices.each_cons(period) do |window|
|
|
47
|
-
low = window.min # Lowest price in the period
|
|
48
|
-
high = window.max # Highest price in the period
|
|
49
|
-
current_price = window.last # Current closing price
|
|
50
|
-
|
|
51
|
-
k_values << (current_price - low) * 100 / (high - low)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
k_values.each_cons(period) do |window|
|
|
55
|
-
d_values << window.mean
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
k: k_values,
|
|
60
|
-
d: d_values
|
|
61
|
-
}
|
|
62
|
-
end
|
|
63
|
-
alias_method :so2, :stochastic_oscillator2
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end; end
|
|
68
|
-
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# lib/sqa/indicator/true_range.rb
|
|
2
|
-
|
|
3
|
-
# See Also: average_true_range
|
|
4
|
-
|
|
5
|
-
class SQA::Indicator; class << self
|
|
6
|
-
|
|
7
|
-
# @param high_prices [Array]
|
|
8
|
-
# @param low_prices [Array]
|
|
9
|
-
# @param previous_closes [Array]
|
|
10
|
-
#
|
|
11
|
-
# @return [Array]
|
|
12
|
-
#
|
|
13
|
-
def true_range(
|
|
14
|
-
high_prices, # Array of high prices
|
|
15
|
-
low_prices, # Array of low prices
|
|
16
|
-
closing_prices # Array of closing prices
|
|
17
|
-
)
|
|
18
|
-
true_ranges = []
|
|
19
|
-
|
|
20
|
-
high_prices.each_with_index do |high, index|
|
|
21
|
-
if index > 0
|
|
22
|
-
low = low_prices[index]
|
|
23
|
-
previous_close = closing_prices[index - 1]
|
|
24
|
-
|
|
25
|
-
true_range = [
|
|
26
|
-
high - low,
|
|
27
|
-
(high - previous_close).abs,
|
|
28
|
-
(low - previous_close).abs
|
|
29
|
-
].max
|
|
30
|
-
|
|
31
|
-
true_ranges << true_range
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
true_ranges # Array of True Range values
|
|
36
|
-
end
|
|
37
|
-
alias_method :tr, :true_range
|
|
38
|
-
|
|
39
|
-
end; end
|
data/lib/sqa/trade.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# lib/sqa/trade.rb
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class SQA::Trade
|
|
5
|
-
attr_accessor :df
|
|
6
|
-
|
|
7
|
-
def initialize(
|
|
8
|
-
filename = SQA::Config.trades_filename
|
|
9
|
-
)
|
|
10
|
-
@df = SQA::DataFrame.load(filename)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def place(signal, ticker, shares, price=nil)
|
|
14
|
-
# TODO: insert row into @df
|
|
15
|
-
|
|
16
|
-
uuid = rand(100000) # FIXME: place holder
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def confirm(uuid, shares, price)
|
|
20
|
-
# TODO: update the row in the data frame
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def save
|
|
24
|
-
# TODO: save the data frame
|
|
25
|
-
end
|
|
26
|
-
end
|