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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.goose/memory/development.txt +3 -0
  3. data/.semver +6 -0
  4. data/ARCHITECTURE.md +648 -0
  5. data/CHANGELOG.md +82 -0
  6. data/CLAUDE.md +653 -0
  7. data/COMMITS.md +196 -0
  8. data/DATAFRAME_ARCHITECTURE_REVIEW.md +421 -0
  9. data/NEXT-STEPS.md +154 -0
  10. data/README.md +812 -262
  11. data/TASKS.md +358 -0
  12. data/TEST_RESULTS.md +140 -0
  13. data/TODO.md +42 -0
  14. data/_notes.txt +25 -0
  15. data/bin/sqa-console +11 -0
  16. data/data/talk_talk.json +103284 -0
  17. data/develop_summary.md +313 -0
  18. data/docs/advanced/backtesting.md +206 -0
  19. data/docs/advanced/ensemble.md +68 -0
  20. data/docs/advanced/fpop.md +153 -0
  21. data/docs/advanced/index.md +112 -0
  22. data/docs/advanced/multi-timeframe.md +67 -0
  23. data/docs/advanced/pattern-matcher.md +75 -0
  24. data/docs/advanced/portfolio-optimizer.md +79 -0
  25. data/docs/advanced/portfolio.md +166 -0
  26. data/docs/advanced/risk-management.md +210 -0
  27. data/docs/advanced/strategy-generator.md +158 -0
  28. data/docs/advanced/streaming.md +209 -0
  29. data/docs/ai_and_ml.md +80 -0
  30. data/docs/api/dataframe.md +1115 -0
  31. data/docs/api/index.md +126 -0
  32. data/docs/assets/css/custom.css +88 -0
  33. data/docs/assets/js/mathjax.js +18 -0
  34. data/docs/concepts/index.md +68 -0
  35. data/docs/contributing/index.md +60 -0
  36. data/docs/data-sources/index.md +66 -0
  37. data/docs/data_frame.md +317 -97
  38. data/docs/factors_that_impact_price.md +26 -0
  39. data/docs/finviz.md +11 -0
  40. data/docs/fx_pro_bit.md +25 -0
  41. data/docs/genetic_programming.md +104 -0
  42. data/docs/getting-started/index.md +123 -0
  43. data/docs/getting-started/installation.md +229 -0
  44. data/docs/getting-started/quick-start.md +244 -0
  45. data/docs/i_gotta_an_idea.md +22 -0
  46. data/docs/index.md +163 -0
  47. data/docs/indicators/index.md +97 -0
  48. data/docs/indicators.md +110 -24
  49. data/docs/options.md +8 -0
  50. data/docs/strategies/bollinger-bands.md +146 -0
  51. data/docs/strategies/consensus.md +64 -0
  52. data/docs/strategies/custom.md +310 -0
  53. data/docs/strategies/ema.md +53 -0
  54. data/docs/strategies/index.md +92 -0
  55. data/docs/strategies/kbs.md +164 -0
  56. data/docs/strategies/macd.md +96 -0
  57. data/docs/strategies/market-profile.md +54 -0
  58. data/docs/strategies/mean-reversion.md +58 -0
  59. data/docs/strategies/rsi.md +95 -0
  60. data/docs/strategies/sma.md +55 -0
  61. data/docs/strategies/stochastic.md +63 -0
  62. data/docs/strategies/volume-breakout.md +54 -0
  63. data/docs/tags.md +7 -0
  64. data/docs/true_strength_index.md +46 -0
  65. data/docs/weighted_moving_average.md +48 -0
  66. data/examples/README.md +354 -0
  67. data/examples/advanced_features_example.rb +350 -0
  68. data/examples/fpop_analysis_example.rb +191 -0
  69. data/examples/genetic_programming_example.rb +148 -0
  70. data/examples/kbs_strategy_example.rb +208 -0
  71. data/examples/pattern_context_example.rb +300 -0
  72. data/examples/rails_app/Gemfile +34 -0
  73. data/examples/rails_app/README.md +416 -0
  74. data/examples/rails_app/app/assets/javascripts/application.js +107 -0
  75. data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
  76. data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
  77. data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
  78. data/examples/rails_app/app/controllers/application_controller.rb +22 -0
  79. data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
  80. data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
  81. data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
  82. data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
  83. data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
  84. data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
  85. data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
  86. data/examples/rails_app/app/views/errors/show.html.erb +17 -0
  87. data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
  88. data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
  89. data/examples/rails_app/bin/rails +6 -0
  90. data/examples/rails_app/config/application.rb +45 -0
  91. data/examples/rails_app/config/boot.rb +5 -0
  92. data/examples/rails_app/config/database.yml +18 -0
  93. data/examples/rails_app/config/environment.rb +11 -0
  94. data/examples/rails_app/config/routes.rb +26 -0
  95. data/examples/rails_app/config.ru +8 -0
  96. data/examples/realtime_stream_example.rb +274 -0
  97. data/examples/sinatra_app/Gemfile +22 -0
  98. data/examples/sinatra_app/QUICKSTART.md +159 -0
  99. data/examples/sinatra_app/README.md +461 -0
  100. data/examples/sinatra_app/app.rb +344 -0
  101. data/examples/sinatra_app/config.ru +5 -0
  102. data/examples/sinatra_app/public/css/style.css +659 -0
  103. data/examples/sinatra_app/public/js/app.js +107 -0
  104. data/examples/sinatra_app/views/analyze.erb +306 -0
  105. data/examples/sinatra_app/views/backtest.erb +325 -0
  106. data/examples/sinatra_app/views/dashboard.erb +419 -0
  107. data/examples/sinatra_app/views/error.erb +58 -0
  108. data/examples/sinatra_app/views/index.erb +118 -0
  109. data/examples/sinatra_app/views/layout.erb +61 -0
  110. data/examples/sinatra_app/views/portfolio.erb +43 -0
  111. data/examples/strategy_generator_example.rb +346 -0
  112. data/hsa_portfolio.csv +11 -0
  113. data/justfile +0 -0
  114. data/lib/api/alpha_vantage_api.rb +462 -0
  115. data/lib/sqa/backtest.rb +329 -0
  116. data/lib/sqa/data_frame/alpha_vantage.rb +43 -65
  117. data/lib/sqa/data_frame/data.rb +92 -0
  118. data/lib/sqa/data_frame/yahoo_finance.rb +35 -43
  119. data/lib/sqa/data_frame.rb +148 -243
  120. data/lib/sqa/ensemble.rb +359 -0
  121. data/lib/sqa/fpop.rb +199 -0
  122. data/lib/sqa/gp.rb +259 -0
  123. data/lib/sqa/indicator.rb +5 -8
  124. data/lib/sqa/init.rb +15 -8
  125. data/lib/sqa/market_regime.rb +240 -0
  126. data/lib/sqa/multi_timeframe.rb +379 -0
  127. data/lib/sqa/pattern_matcher.rb +497 -0
  128. data/lib/sqa/portfolio.rb +260 -6
  129. data/lib/sqa/portfolio_optimizer.rb +377 -0
  130. data/lib/sqa/risk_manager.rb +442 -0
  131. data/lib/sqa/seasonal_analyzer.rb +209 -0
  132. data/lib/sqa/sector_analyzer.rb +300 -0
  133. data/lib/sqa/stock.rb +67 -125
  134. data/lib/sqa/strategy/bollinger_bands.rb +42 -0
  135. data/lib/sqa/strategy/consensus.rb +5 -2
  136. data/lib/sqa/strategy/kbs_strategy.rb +470 -0
  137. data/lib/sqa/strategy/macd.rb +46 -0
  138. data/lib/sqa/strategy/mp.rb +1 -1
  139. data/lib/sqa/strategy/stochastic.rb +60 -0
  140. data/lib/sqa/strategy/volume_breakout.rb +57 -0
  141. data/lib/sqa/strategy.rb +5 -0
  142. data/lib/sqa/strategy_generator.rb +947 -0
  143. data/lib/sqa/stream.rb +361 -0
  144. data/lib/sqa/version.rb +1 -7
  145. data/lib/sqa.rb +23 -16
  146. data/main.just +81 -0
  147. data/mkdocs.yml +288 -0
  148. data/trace.log +0 -0
  149. metadata +261 -51
  150. data/bin/sqa +0 -6
  151. data/lib/patches/dry-cli.rb +0 -228
  152. data/lib/sqa/activity.rb +0 -10
  153. data/lib/sqa/cli.rb +0 -62
  154. data/lib/sqa/commands/analysis.rb +0 -309
  155. data/lib/sqa/commands/base.rb +0 -139
  156. data/lib/sqa/commands/web.rb +0 -199
  157. data/lib/sqa/commands.rb +0 -22
  158. data/lib/sqa/constants.rb +0 -23
  159. data/lib/sqa/indicator/average_true_range.rb +0 -33
  160. data/lib/sqa/indicator/bollinger_bands.rb +0 -28
  161. data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
  162. data/lib/sqa/indicator/donchian_channel.rb +0 -29
  163. data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
  164. data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
  165. data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
  166. data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
  167. data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
  168. data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
  169. data/lib/sqa/indicator/market_profile.rb +0 -32
  170. data/lib/sqa/indicator/mean_reversion.rb +0 -37
  171. data/lib/sqa/indicator/momentum.rb +0 -28
  172. data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
  173. data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
  174. data/lib/sqa/indicator/predict_next_value.rb +0 -202
  175. data/lib/sqa/indicator/relative_strength_index.rb +0 -47
  176. data/lib/sqa/indicator/simple_moving_average.rb +0 -24
  177. data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
  178. data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
  179. data/lib/sqa/indicator/true_range.rb +0 -39
  180. data/lib/sqa/trade.rb +0 -26
data/lib/sqa/activity.rb DELETED
@@ -1,10 +0,0 @@
1
- # lib/sqa/activity.rb
2
-
3
- # Historical daily stock activity
4
- # primary id is [ticker, date]
5
-
6
- class SQA::Activity < ActiveRecord::Base
7
- # belongs_to :stock using ticker as the foreign key
8
- # need a unique constraint on [ticker, date]
9
- # should date be saved as a Date object or string?
10
- end
data/lib/sqa/cli.rb DELETED
@@ -1,62 +0,0 @@
1
- # lib/sqa/cli.rb
2
-
3
- require 'dry/cli'
4
-
5
- require_relative '../sqa'
6
- require_relative 'commands'
7
-
8
- module SQA::CLI
9
- class << self
10
- def run!
11
- Dry::CLI.new(SQA::Commands).call
12
- end
13
- end
14
- end
15
-
16
-
17
-
18
-
19
- __END__
20
-
21
-
22
-
23
-
24
- # header "Stock Quantitative Analysis (SQA)"
25
- # footer "WARNING: This is a toy, a play thing, not intended for serious use."
26
-
27
- # program "sqa"
28
- # desc "A collection of things"
29
-
30
-
31
-
32
-
33
- class << self
34
-
35
- ##################################################
36
- def run(argv = ARGV)
37
-
38
-
39
- elsif params[:dump_config]
40
- SQA.config.config_file = params[:dump_config]
41
- `touch #{SQA.config.config_file}`
42
- SQA.config.dump_file
43
- exit(0)
44
-
45
- elsif params[:config_file]
46
- # Override the defaults <- envars <- config file content
47
- params[:config_file] = SQA.homify params[:config_file]
48
- SQA.config.config_file = params[:config_file]
49
- SQA.config.from_file
50
- end
51
-
52
-
53
-
54
- # Override the defaults <- envars <- config file <- cli parameters
55
- SQA.config.merge!(remove_temps params.to_h)
56
-
57
-
58
- end
59
- end
60
- end
61
- end
62
-
@@ -1,309 +0,0 @@
1
- # sqa/lib/sqa/commands/analysis.rb
2
-
3
- class Commands::Analysis < Commands::Base
4
- VERSION = "0.0.1-analysis"
5
-
6
- Commands.register "analysis", self
7
-
8
- desc "Provide an Analysis of a Portfolio"
9
-
10
-
11
- def initialize
12
- # TODO: something
13
- end
14
-
15
- def call(params)
16
- config = super
17
-
18
- puts <<~EOS
19
- ##################################
20
- ## Running the Analysis Command ##
21
- ##################################
22
- EOS
23
- end
24
- end
25
-
26
- __END__
27
-
28
-
29
-
30
- ###################################################
31
- ## This is the old thing that got me started ...
32
-
33
- #!/usr/bin/env ruby
34
- # experiments/stocks/analysis.rb
35
- #
36
- # Some technical indicators from FinTech gem
37
- #
38
- # optional date CLI option in format YYYY-mm-dd
39
- # if not present uses Date.today
40
- #
41
-
42
-
43
- INVEST = 1000.00
44
-
45
- require 'pathname'
46
-
47
- require_relative 'stock'
48
- require_relative 'datastore'
49
-
50
-
51
- STOCKS = Pathname.pwd + "stocks.txt"
52
- TRADES = Pathname.pwd + "trades.csv"
53
-
54
- TRADES_FILE = File.open(TRADES, 'a')
55
-
56
- unless STOCKS.exist?
57
- puts
58
- puts "ERROR: The 'stocks.txt' file does not exist."
59
- puts
60
- exot(-1)
61
- end
62
-
63
- require 'debug_me'
64
- include DebugMe
65
-
66
- require 'csv'
67
- require 'date'
68
- require 'tty-table'
69
-
70
- require 'fin_tech'
71
- require 'previous_dow'
72
-
73
- class NilClass
74
- def blank?() = true
75
- end
76
-
77
- class String
78
- def blank?() = strip().empty?
79
- end
80
-
81
- class Array
82
- def blank?() = empty?
83
- end
84
-
85
-
86
- def tickers
87
- return @tickers unless @tickers.blank?
88
-
89
- @tickers = []
90
-
91
- STOCKS.readlines.each do |a_line|
92
- ticker_symbol = a_line.chomp.strip.split()&.first&.downcase
93
- next if ticker_symbol.blank? || '#' == ticker_symbol
94
- @tickers << ticker_symbol unless @tickers.include?(ticker_symbol)
95
- end
96
-
97
- @tickers.sort!
98
- end
99
-
100
- given_date = ARGV.first ? Date.parse(ARGV.first) : Date.today
101
-
102
- start_date = Date.new(2019, 1, 1)
103
- end_date = previous_dow(:friday, given_date)
104
-
105
- ASOF = end_date.to_s.tr('-','')
106
-
107
-
108
- #######################################################################
109
- # download a CSV file from https://query1.finance.yahoo.com
110
- # given a stock ticker symbol as a String
111
- # start and end dates
112
- #
113
- # For ticker "aapl" the downloaded file will be named "aapl.csv"
114
- # That filename will be renamed to "aapl_YYYYmmdd.csv" where the
115
- # date suffix is the end_date of the historical data.
116
- #
117
- def download_historical_prices(ticker, start_date, end_date)
118
- data_path = Pathname.pwd + "#{ticker}_#{ASOF}.csv"
119
- return if data_path.exist?
120
-
121
- mew_path = Pathname.pwd + "#{ticker}.csv"
122
-
123
- start_timestamp = start_date.to_time.to_i
124
- end_timestamp = end_date.to_time.to_i
125
- ticker_upcase = ticker.upcase
126
- filename = "#{ticker.downcase}.csv"
127
-
128
- `curl -o #{filename} "https://query1.finance.yahoo.com/v7/finance/download/#{ticker_upcase}?period1=#{start_timestamp}&period2=#{end_timestamp}&interval=1d&events=history&includeAdjustedClose=true"`
129
-
130
- mew_path.rename data_path
131
- end
132
-
133
-
134
- #######################################################################
135
- # Read the CSV file associated with the give ticker symbol
136
- # and the ASOF date.
137
- #
138
- def read_csv(ticker)
139
- filename = "#{ticker.downcase}_#{ASOF}.csv"
140
- data = []
141
-
142
- CSV.foreach(filename, headers: true) do |row|
143
- data << row.to_h
144
- end
145
-
146
- data
147
- end
148
-
149
- ##########################
150
- # record a recommend trade
151
-
152
- def trade(ticker, signal, shares, price)
153
- TRADES_FILE.puts "#{ticker},#{ASOF},#{signal},#{shares},#{price}"
154
- end
155
-
156
- #######################################################################
157
- ###
158
- ## Main
159
- #
160
-
161
-
162
- tickers.each do |ticker|
163
- download_historical_prices(ticker, start_date, end_date)
164
- end
165
-
166
- result = {}
167
-
168
- mwfd = 14 # moving_window_forcast_days
169
-
170
- headers = %w[ Ticker AdjClose Trend Slope M'tum RSI Analysis MACD Target Signal $]
171
- values = []
172
-
173
- tickers.each do |ticker|
174
-
175
- data = read_csv ticker
176
- prices = data.map{|r| r["Adj Close"].to_f}
177
- volumes = data.map{|r| r["volume"].to_f}
178
-
179
- if data.blank?
180
- puts
181
- puts "ERROR: cannot get data for #{ticker}"
182
- puts
183
- next
184
- end
185
-
186
-
187
- result[ticker] = {
188
- date: data.last["Date"],
189
- adj_close: data.last["Adj Close"].to_f
190
- }
191
-
192
- result[ticker][:market] = FinTech.classify_market_profile(
193
- volumes.last(mwfd),
194
- prices.last(mwfd),
195
- prices.last(mwfd).first,
196
- prices.last
197
- )
198
-
199
- fr = FinTech.fibonacci_retracement( prices.last(mwfd).first,
200
- prices.last).map{|x| x.round(3)}
201
-
202
-
203
- puts "\n#{result[ticker][:market]} .. #{ticker}\t#{fr}"
204
- print "\t"
205
- print FinTech.head_and_shoulders_pattern?(prices.last(mwfd))
206
- print "\t"
207
- print FinTech.double_top_bottom_pattern?(prices.last(mwfd))
208
- print "\t"
209
- mr = FinTech.mean_reversion?(prices, mwfd, 0.5)
210
- print mr
211
-
212
- if mr
213
- print "\t"
214
- print FinTech.mr_mean(prices, mwfd).round(3)
215
- end
216
-
217
- print "\t"
218
- print FinTech.identify_wave_condition?(prices, 2*mwfd, 1.0)
219
- puts
220
-
221
- print "\t"
222
- print FinTech.ema_analysis(prices, mwfd).except(:ema_values)
223
-
224
- puts
225
-
226
- row = [ ticker ]
227
-
228
- # result[ticker][:moving_averages] = FinTech.sma(data, mwfd)
229
- result[ticker][:trend] = FinTech.sma_trend(data, mwfd)
230
- result[ticker][:momentum] = FinTech.momentum(prices, mwfd)
231
- result[ticker][:rsi] = FinTech.rsi(data, mwfd)
232
- result[ticker][:bollinger_bands] = FinTech.bollinger_bands(data, mwfd, 2)
233
- result[ticker][:macd] = FinTech.macd(data, mwfd, 2*mwfd, mwfd/2)
234
-
235
- price = result[ticker][:adj_close].round(3)
236
-
237
- row << price
238
- row << result[ticker][:trend][:trend]
239
- row << result[ticker][:trend][:angle].round(3)
240
- row << result[ticker][:momentum].round(3)
241
- row << result[ticker][:rsi][:rsi].round(3)
242
- row << result[ticker][:rsi][:meaning]
243
- row << result[ticker][:macd].first.round(3)
244
- row << result[ticker][:macd].last.round(3)
245
-
246
- analysis = result[ticker][:rsi][:meaning]
247
-
248
- signal = ""
249
- macd_diff = result[ticker][:macd].first
250
- target = result[ticker][:macd].last
251
- current = result[ticker][:adj_close]
252
-
253
- trend_down = "down" == result[ticker][:trend][:trend]
254
-
255
- if current < target
256
- signal = "buy" unless "Over Bought" == analysis
257
- elsif (current > target) && trend_down
258
- signal = "sell" unless "Over Sold" == analysis
259
- end
260
-
261
- if "buy" == signal
262
- pps = target - price
263
- shares = INVEST.to_i / price.to_i
264
- upside = (shares * pps).round(2)
265
- trade(ticker, signal, shares, price)
266
- elsif "sell" == signal
267
- pps = target - price
268
- shares = INVEST.to_i / price.to_i
269
- upside = (shares * pps).round(2)
270
- trade(ticker, signal, shares, price)
271
- else
272
- upside = ""
273
- end
274
-
275
- row << signal
276
- row << upside
277
-
278
- values << row
279
- end
280
-
281
- the_table = TTY::Table.new(headers, values)
282
-
283
- puts
284
- puts "Analysis as of Friday Close: #{end_date}"
285
-
286
- puts the_table.render(
287
- :unicode,
288
- {
289
- padding: [0, 0, 0, 0],
290
- alignments: [
291
- :left, # ticker
292
- :right, # adj close
293
- :center, # trend
294
- :right, # slope
295
- :right, # momentum
296
- :right, # rsi
297
- :center, # meaning / analysis
298
- :right, # macd
299
- :right, # target
300
- :center, # signal
301
- :right # upside
302
- ],
303
- }
304
- )
305
- puts
306
-
307
- TRADES_FILE.close
308
-
309
-
@@ -1,139 +0,0 @@
1
- # .../sqa/cli/commands/base.rb
2
-
3
- # SQA.config will be built with its defaults
4
- # and envar over-rides BEFORE a command is
5
- # process. This means that options do not
6
- # need to have a "default" value.
7
-
8
- # Establish a Base command class that has global options
9
- # available to all commands.
10
-
11
- class Commands::Base < Dry::CLI::Command
12
- # keys from Dry::Cli options which we do not want in the
13
- # config object.
14
- IGNORE_OPTIONS = %i[ version ]
15
-
16
- global_header <<~EOS
17
-
18
- SQA - Stock Quantitative Analysis
19
- by: MadBomber
20
-
21
- This is a work in progress. It is not fit for anything
22
- other than play time. ** Do not ** use it to make any
23
- kind of serious trading decisions.
24
-
25
- EOS
26
-
27
- global_footer <<~EOS
28
-
29
- SARNING: This product is a work in progress. DO NOT USE
30
- for serious trading decisions.
31
-
32
- Copyright (c) 2023 - MadBomber Software
33
-
34
- EOS
35
-
36
- option :debug,
37
- required: false,
38
- type: :boolean,
39
- desc: 'Print debug information',
40
- aliases: %w[-d --debug]
41
-
42
- option :verbose,
43
- required: false,
44
- type: :boolean,
45
- desc: 'Print verbose information',
46
- aliases: %w[-v --verbose]
47
-
48
-
49
- option :version,
50
- required: false,
51
- type: :boolean,
52
- default: false,
53
- desc: 'Print version(s) and exit',
54
- aliases: %w[--version]
55
-
56
-
57
- option :config_file,
58
- required: false,
59
- type: :string,
60
- desc: "Path to the config file"
61
-
62
-
63
- option :log_level,
64
- required: false,
65
- type: :string,
66
- values: %w[debug info warn error fatal ],
67
- desc: "Set the log level"
68
-
69
-
70
- option :portfolio,
71
- required: false,
72
- aliases: %w[ --portfolio --folio --file -f ],
73
- type: :string,
74
- desc: "Set the filename of the portfolio"
75
-
76
-
77
- option :trades,
78
- required: false,
79
- aliases: %w[ --trades ],
80
- type: :string,
81
- desc: "Set the filename into which trades are stored"
82
-
83
-
84
- option :data_dir,
85
- required: false,
86
- aliases: %w[ --data-dir --data --dir ],
87
- type: :string,
88
- desc: "Set the directory for the SQA data"
89
-
90
-
91
- option :dump_config,
92
- required: false,
93
- type: :string,
94
- desc: "Dump the current configuration to a file"
95
-
96
-
97
- # All command class call methods should start with
98
- # super so that this method is invoked.
99
- #
100
- # params is a Hash from Dry::CLI where keys are Symbol
101
-
102
- def call(params)
103
- show_versions_and_exit if params[:version]
104
-
105
- unless params[:config_file].nil? || params[:config_file].empty?
106
- SQA.config.config_file = params[:config_file]
107
- SQA.config.from_file
108
- end
109
-
110
- update_config(params)
111
-
112
- unless params[:dump_config].nil? || params[:dump_config].empty?
113
- SQA.config.config_file = params[:dump_config]
114
- SQA.config.dump_file
115
- end
116
-
117
- SQA.config
118
- end
119
-
120
- ################################################
121
- private
122
-
123
- def show_versions_and_exit
124
- self.class.ancestors.each do |ancestor|
125
- next unless ancestor.const_defined?(:VERSION)
126
- puts "#{ancestor}: #{ancestor::VERSION}"
127
- end
128
-
129
- puts "SQA: #{SQA::VERSION}" if SQA.const_defined?(:VERSION)
130
-
131
- exit(0)
132
- end
133
-
134
- def update_config(params)
135
- SQA.config.inject_additional_properties
136
- my_hash = params.reject { |key, _| IGNORE_OPTIONS.include?(key) }
137
- SQA.config.merge!(my_hash)
138
- end
139
- end
@@ -1,199 +0,0 @@
1
- # sqa/lib/sqa/commands/web.rb
2
-
3
- class Commands::Web < Commands::Base
4
- VERSION = "0.0.1-web"
5
-
6
- Commands.register "web", self
7
-
8
- desc "Start a web application"
9
-
10
- option :image,
11
- required: true,
12
- type: :string,
13
- desc: "The name of the image to use"
14
-
15
- SQA::PluginManager.new_property(:restart, default: 'no', coerce: String)
16
-
17
- option :restart,
18
- aliases: %w[ --restart ],
19
- type: :string,
20
- default: "no",
21
- values: %w[ no on-failure always unless-stopped ],
22
- desc: "Restart policy to apply when a container exits"
23
-
24
- SQA::PluginManager.new_property(:detach, default: 'no', coerce: String)
25
-
26
- option :detach,
27
- aliases: %w[ --detach ],
28
- type: :boolean,
29
- default: false,
30
- desc: "Run container in background and print container ID"
31
-
32
- SQA::PluginManager.new_property(:port, default: 4567, coerce: Integer)
33
-
34
- option :port,
35
- aliases: %w[ -p --port ],
36
- type: :integer,
37
- default: 4567,
38
- desc: "The port where the web app will run"
39
-
40
-
41
- def initialize
42
- # TODO: make it happen
43
- end
44
-
45
-
46
- # params is Object from the ARGV parser
47
- def call(params)
48
- config = super
49
-
50
- puts <<~EOS
51
- ###############################
52
- ## Running the Web Interface ##
53
- ###############################
54
- EOS
55
- end
56
- end
57
-
58
-
59
-
60
- __END__
61
-
62
- require 'sinatra/base'
63
-
64
- module SQA
65
- class Web < Sinatra::Base
66
- set :port, SQA.config.port || 4567
67
-
68
- get '/' do
69
- "Welcome to SQA Web Interface!"
70
- end
71
-
72
-
73
- get '/stocks/:ticker' do
74
- ticker = params[:ticker]
75
- stock = SQA::Stock.new(ticker: ticker, source: :alpha_vantage)
76
-
77
- "Stock: #{stock.data.name}, Ticker: #{stock.data.ticker}"
78
- end
79
-
80
-
81
- get '/stocks/:ticker/indicators/:indicator' do
82
- ticker = params[:ticker]
83
- indicator = params[:indicator]
84
- stock = SQA::Stock.new(ticker: ticker, source: :alpha_vantage)
85
-
86
- indicator_value = SQA::Indicator.send(indicator, stock.df.adj_close_price, 14)
87
-
88
- "Indicator #{indicator} for Stock #{ticker} is #{indicator_value}"
89
- end
90
-
91
- # TODO: Add more routes as needed to expose more functionality
92
-
93
- # start the server if ruby file executed directly
94
- run! if app_file == $0
95
- end
96
- end
97
-
98
-
99
-
100
- ###################################################
101
- #!/usr/bin/env ruby
102
- # experiments/sinatra_examples/svg_viewer.rb
103
- # builds on md_viewer.rb
104
-
105
- require 'sinatra'
106
- require 'kramdown'
107
- require 'nenv'
108
-
109
- # class MdViewer < Sinatra::Application
110
-
111
- # Set the directory location
112
- set :markdown_directory, Nenv.home + '/Downloads'
113
-
114
- # Sinatra route to show a markdown file as html
115
- get '/md/:filename' do
116
- # Get the file name from the URL parameter
117
- filename = params[:filename]
118
-
119
- # Check if the file exists in the specified directory
120
- if File.file?(File.join(settings.markdown_directory, filename))
121
- # Read the markdown file
122
- markdown_content = File.read(File.join(settings.markdown_directory, filename))
123
-
124
- # Convert the markdown to HTML using kramdown
125
- converted_html = Kramdown::Document.new(markdown_content).to_html
126
-
127
- # Display the generated HTML
128
- content_type :html
129
- converted_html
130
- else
131
- # File not found error
132
- status 404
133
- "File not found: #{filename} in #{markdown_directory}"
134
- end
135
- end
136
-
137
-
138
- # Sinatra route to show a markdown file as html
139
- get '/svg/:filename' do
140
- # Get the file name from the URL parameter
141
- filename = params[:filename]
142
-
143
- # Check if the file exists in the specified directory
144
- if File.file?(File.join(settings.markdown_directory, filename))
145
- # Read the svg file
146
- svg_content = File.read(File.join(settings.markdown_directory, filename))
147
-
148
- # Convert the svg to HTML
149
- converted_html = <<~HTML
150
- <!DOCTYPE html>
151
- <html>
152
- <head>
153
- <meta charset="utf-8">
154
- <meta name="viewport" content="width=device-width, initial-scale=1">
155
- <title>#{filename}</title>
156
- </head>
157
- <body>
158
- #{svg_content}
159
- </body>
160
- </html>
161
- HTML
162
-
163
- # Display the generated HTML
164
- content_type :html
165
- converted_html
166
- else
167
- # File not found error
168
- status 404
169
- "File not found: #{filename} in #{markdown_directory}"
170
- end
171
- end
172
-
173
-
174
-
175
-
176
- # end
177
-
178
- # Start the Sinatra app
179
- # run MdViewer
180
-
181
-
182
- __END__
183
-
184
- ruby myapp.rb [-h] [-x] [-q] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
185
-
186
- Options are:
187
-
188
- -h # help
189
- -p # set the port (default is 4567)
190
- -o # set the host (default is 0.0.0.0)
191
- -e # set the environment (default is development)
192
- -s # specify rack server/handler (default is puma)
193
- -q # turn on quiet mode for server (default is off)
194
- -x # turn on the mutex lock (default is off)
195
-
196
-
197
-
198
-
199
-