sqa 0.0.24 → 0.0.32

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 (203) 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 +95 -0
  6. data/CLAUDE.md +674 -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 +839 -265
  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 +1114 -0
  31. data/docs/api/index.md +126 -0
  32. data/docs/assets/css/custom.css +88 -0
  33. data/docs/assets/images/sqa.jpg +0 -0
  34. data/docs/assets/js/mathjax.js +18 -0
  35. data/docs/concepts/index.md +60 -0
  36. data/docs/contributing/index.md +60 -0
  37. data/docs/data-sources/index.md +66 -0
  38. data/docs/data_frame.md +316 -97
  39. data/docs/factors_that_impact_price.md +26 -0
  40. data/docs/finviz.md +11 -0
  41. data/docs/fx_pro_bit.md +25 -0
  42. data/docs/genetic_programming.md +104 -0
  43. data/docs/getting-started/index.md +107 -0
  44. data/docs/getting-started/installation.md +229 -0
  45. data/docs/getting-started/quick-start.md +244 -0
  46. data/docs/i_gotta_an_idea.md +22 -0
  47. data/docs/index.md +161 -0
  48. data/docs/indicators/index.md +97 -0
  49. data/docs/indicators.md +110 -24
  50. data/docs/options.md +8 -0
  51. data/docs/strategies/bollinger-bands.md +146 -0
  52. data/docs/strategies/consensus.md +64 -0
  53. data/docs/strategies/custom.md +310 -0
  54. data/docs/strategies/ema.md +53 -0
  55. data/docs/strategies/index.md +92 -0
  56. data/docs/strategies/kbs.md +164 -0
  57. data/docs/strategies/macd.md +96 -0
  58. data/docs/strategies/market-profile.md +54 -0
  59. data/docs/strategies/mean-reversion.md +58 -0
  60. data/docs/strategies/rsi.md +95 -0
  61. data/docs/strategies/sma.md +55 -0
  62. data/docs/strategies/stochastic.md +63 -0
  63. data/docs/strategies/volume-breakout.md +54 -0
  64. data/docs/tags.md +7 -0
  65. data/examples/README.md +354 -0
  66. data/examples/advanced_features_example.rb +350 -0
  67. data/examples/fpop_analysis_example.rb +191 -0
  68. data/examples/genetic_programming_example.rb +148 -0
  69. data/examples/kbs_strategy_example.rb +208 -0
  70. data/examples/pattern_context_example.rb +300 -0
  71. data/examples/rails_app/Gemfile +34 -0
  72. data/examples/rails_app/README.md +416 -0
  73. data/examples/rails_app/app/assets/javascripts/application.js +107 -0
  74. data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
  75. data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
  76. data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
  77. data/examples/rails_app/app/controllers/application_controller.rb +22 -0
  78. data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
  79. data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
  80. data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
  81. data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
  82. data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
  83. data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
  84. data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
  85. data/examples/rails_app/app/views/errors/show.html.erb +17 -0
  86. data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
  87. data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
  88. data/examples/rails_app/bin/rails +6 -0
  89. data/examples/rails_app/config/application.rb +45 -0
  90. data/examples/rails_app/config/boot.rb +5 -0
  91. data/examples/rails_app/config/database.yml +18 -0
  92. data/examples/rails_app/config/environment.rb +11 -0
  93. data/examples/rails_app/config/routes.rb +26 -0
  94. data/examples/rails_app/config.ru +8 -0
  95. data/examples/realtime_stream_example.rb +274 -0
  96. data/examples/sinatra_app/Gemfile +42 -0
  97. data/examples/sinatra_app/Gemfile.lock +268 -0
  98. data/examples/sinatra_app/QUICKSTART.md +169 -0
  99. data/examples/sinatra_app/README.md +471 -0
  100. data/examples/sinatra_app/RUNNING_WITHOUT_TALIB.md +90 -0
  101. data/examples/sinatra_app/TROUBLESHOOTING.md +95 -0
  102. data/examples/sinatra_app/app.rb +404 -0
  103. data/examples/sinatra_app/config.ru +5 -0
  104. data/examples/sinatra_app/public/css/style.css +723 -0
  105. data/examples/sinatra_app/public/debug_macd.html +82 -0
  106. data/examples/sinatra_app/public/js/app.js +107 -0
  107. data/examples/sinatra_app/start.sh +53 -0
  108. data/examples/sinatra_app/views/analyze.erb +306 -0
  109. data/examples/sinatra_app/views/backtest.erb +325 -0
  110. data/examples/sinatra_app/views/dashboard.erb +831 -0
  111. data/examples/sinatra_app/views/error.erb +58 -0
  112. data/examples/sinatra_app/views/index.erb +118 -0
  113. data/examples/sinatra_app/views/layout.erb +61 -0
  114. data/examples/sinatra_app/views/portfolio.erb +43 -0
  115. data/examples/strategy_generator_example.rb +346 -0
  116. data/hsa_portfolio.csv +11 -0
  117. data/justfile +0 -0
  118. data/lib/api/alpha_vantage_api.rb +462 -0
  119. data/lib/sqa/backtest.rb +329 -0
  120. data/lib/sqa/data_frame/alpha_vantage.rb +51 -63
  121. data/lib/sqa/data_frame/data.rb +92 -0
  122. data/lib/sqa/data_frame/yahoo_finance.rb +35 -43
  123. data/lib/sqa/data_frame.rb +154 -243
  124. data/lib/sqa/ensemble.rb +359 -0
  125. data/lib/sqa/fpop.rb +199 -0
  126. data/lib/sqa/gp.rb +259 -0
  127. data/lib/sqa/indicator.rb +16 -6
  128. data/lib/sqa/init.rb +15 -8
  129. data/lib/sqa/market_regime.rb +240 -0
  130. data/lib/sqa/multi_timeframe.rb +379 -0
  131. data/lib/sqa/pattern_matcher.rb +497 -0
  132. data/lib/sqa/portfolio.rb +260 -6
  133. data/lib/sqa/portfolio_optimizer.rb +377 -0
  134. data/lib/sqa/risk_manager.rb +442 -0
  135. data/lib/sqa/seasonal_analyzer.rb +209 -0
  136. data/lib/sqa/sector_analyzer.rb +300 -0
  137. data/lib/sqa/stock.rb +131 -127
  138. data/lib/sqa/strategy/bollinger_bands.rb +42 -0
  139. data/lib/sqa/strategy/consensus.rb +5 -2
  140. data/lib/sqa/strategy/kbs_strategy.rb +470 -0
  141. data/lib/sqa/strategy/macd.rb +46 -0
  142. data/lib/sqa/strategy/mp.rb +1 -1
  143. data/lib/sqa/strategy/stochastic.rb +60 -0
  144. data/lib/sqa/strategy/volume_breakout.rb +57 -0
  145. data/lib/sqa/strategy.rb +5 -0
  146. data/lib/sqa/strategy_generator.rb +947 -0
  147. data/lib/sqa/stream.rb +361 -0
  148. data/lib/sqa/ticker.rb +9 -2
  149. data/lib/sqa/version.rb +1 -7
  150. data/lib/sqa.rb +35 -20
  151. data/main.just +81 -0
  152. data/mkdocs.yml +252 -0
  153. data/trace.log +0 -0
  154. metadata +265 -69
  155. data/bin/sqa +0 -6
  156. data/docs/alpha_vantage_technical_indicators.md +0 -62
  157. data/docs/average_true_range.md +0 -9
  158. data/docs/bollinger_bands.md +0 -15
  159. data/docs/candlestick_pattern_recognizer.md +0 -4
  160. data/docs/donchian_channel.md +0 -5
  161. data/docs/double_top_bottom_pattern.md +0 -3
  162. data/docs/exponential_moving_average.md +0 -19
  163. data/docs/fibonacci_retracement.md +0 -30
  164. data/docs/head_and_shoulders_pattern.md +0 -3
  165. data/docs/market_profile.md +0 -4
  166. data/docs/momentum.md +0 -19
  167. data/docs/moving_average_convergence_divergence.md +0 -23
  168. data/docs/peaks_and_valleys.md +0 -11
  169. data/docs/relative_strength_index.md +0 -6
  170. data/docs/simple_moving_average.md +0 -8
  171. data/docs/stochastic_oscillator.md +0 -4
  172. data/docs/ta_lib.md +0 -160
  173. data/docs/true_range.md +0 -12
  174. data/lib/patches/dry-cli.rb +0 -228
  175. data/lib/sqa/activity.rb +0 -10
  176. data/lib/sqa/cli.rb +0 -62
  177. data/lib/sqa/commands/analysis.rb +0 -309
  178. data/lib/sqa/commands/base.rb +0 -139
  179. data/lib/sqa/commands/web.rb +0 -199
  180. data/lib/sqa/commands.rb +0 -22
  181. data/lib/sqa/constants.rb +0 -23
  182. data/lib/sqa/indicator/average_true_range.rb +0 -33
  183. data/lib/sqa/indicator/bollinger_bands.rb +0 -28
  184. data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
  185. data/lib/sqa/indicator/donchian_channel.rb +0 -29
  186. data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
  187. data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
  188. data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
  189. data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
  190. data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
  191. data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
  192. data/lib/sqa/indicator/market_profile.rb +0 -32
  193. data/lib/sqa/indicator/mean_reversion.rb +0 -37
  194. data/lib/sqa/indicator/momentum.rb +0 -28
  195. data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
  196. data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
  197. data/lib/sqa/indicator/predict_next_value.rb +0 -202
  198. data/lib/sqa/indicator/relative_strength_index.rb +0 -47
  199. data/lib/sqa/indicator/simple_moving_average.rb +0 -24
  200. data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
  201. data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
  202. data/lib/sqa/indicator/true_range.rb +0 -39
  203. data/lib/sqa/trade.rb +0 -26
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'boot'
4
+
5
+ require 'rails'
6
+ require 'active_model/railtie'
7
+ require 'active_job/railtie'
8
+ require 'active_record/railtie'
9
+ require 'action_controller/railtie'
10
+ require 'action_view/railtie'
11
+ require 'action_cable/railtie'
12
+ require 'rails/test_unit/railtie'
13
+
14
+ # Require the gems listed in Gemfile
15
+ Bundler.require(*Rails.groups)
16
+
17
+ module SqaRailsApp
18
+ class Application < Rails::Application
19
+ # Initialize configuration defaults for Rails 7.1
20
+ config.load_defaults 7.1
21
+
22
+ # Configuration for the application, engines, and railties
23
+ config.autoload_lib(ignore: %w(assets tasks))
24
+
25
+ # Add SQA library to load path
26
+ lib_path = File.expand_path('../../../lib', __dir__)
27
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
28
+
29
+ # API-only mode for API namespace
30
+ config.api_only = false
31
+
32
+ # Enable sessions
33
+ config.session_store :cookie_store, key: '_sqa_rails_session'
34
+
35
+ # Assets
36
+ config.assets.paths << Rails.root.join('app', 'assets', 'stylesheets')
37
+ config.assets.paths << Rails.root.join('app', 'assets', 'javascripts')
38
+
39
+ # Time zone
40
+ config.time_zone = 'UTC'
41
+
42
+ # Eager load in production
43
+ config.eager_load = Rails.env.production?
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
4
+
5
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
@@ -0,0 +1,18 @@
1
+ # SQLite3 database configuration
2
+
3
+ default: &default
4
+ adapter: sqlite3
5
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
6
+ timeout: 5000
7
+
8
+ development:
9
+ <<: *default
10
+ database: db/development.sqlite3
11
+
12
+ test:
13
+ <<: *default
14
+ database: db/test.sqlite3
15
+
16
+ production:
17
+ <<: *default
18
+ database: db/production.sqlite3
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load the Rails application
4
+ require_relative 'application'
5
+
6
+ # Initialize SQA
7
+ require 'sqa'
8
+ SQA.init
9
+
10
+ # Initialize the Rails application
11
+ Rails.application.initialize!
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ # Root route
5
+ root 'dashboard#index'
6
+
7
+ # Dashboard routes
8
+ get 'dashboard/:ticker', to: 'dashboard#show', as: :stock_dashboard
9
+ get 'analyze/:ticker', to: 'analysis#show', as: :stock_analysis
10
+ get 'backtest/:ticker', to: 'backtest#show', as: :stock_backtest
11
+ get 'portfolio', to: 'portfolio#index', as: :portfolio
12
+
13
+ # API routes
14
+ namespace :api do
15
+ namespace :v1 do
16
+ get 'stock/:ticker', to: 'stocks#show'
17
+ get 'indicators/:ticker', to: 'stocks#indicators'
18
+ post 'backtest/:ticker', to: 'stocks#backtest'
19
+ get 'analyze/:ticker', to: 'stocks#analyze'
20
+ post 'compare/:ticker', to: 'stocks#compare'
21
+ end
22
+ end
23
+
24
+ # Health check
25
+ get 'up', to: 'rails/health#show', as: :rails_health_check
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is used by Rack-based servers to start the application.
4
+
5
+ require_relative 'config/environment'
6
+
7
+ run Rails.application
8
+ Rails.application.load_server
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Real-Time Stock Price Streaming
5
+ #
6
+ # This example shows how to use SQA::Stream to process live stock
7
+ # price updates and generate trading signals in real-time.
8
+
9
+ require 'sqa'
10
+
11
+ SQA.init
12
+
13
+ puts "=" * 60
14
+ puts "Real-Time Stock Price Streaming Example"
15
+ puts "=" * 60
16
+ puts
17
+
18
+ # Example 1: Basic Streaming with Single Strategy
19
+ puts "\n" + "=" * 60
20
+ puts "Example 1: Basic Streaming Setup"
21
+ puts "=" * 60
22
+ puts
23
+
24
+ stream = SQA::Stream.new(
25
+ ticker: 'AAPL',
26
+ window_size: 100,
27
+ strategies: [SQA::Strategy::RSI]
28
+ )
29
+
30
+ puts "Stream initialized:"
31
+ puts " Ticker: #{stream.ticker}"
32
+ puts " Window Size: #{stream.window_size}"
33
+ puts " Strategies: #{stream.strategies.size}"
34
+ puts
35
+
36
+ # Add signal callback
37
+ stream.on_signal do |signal, data|
38
+ puts "🔔 SIGNAL: #{signal.upcase}"
39
+ puts " Price: $#{data[:price].round(2)}"
40
+ puts " Time: #{data[:timestamp]}"
41
+ puts " Strategy Votes: #{data[:strategies_vote]}"
42
+ puts
43
+ end
44
+
45
+ # Add update callback (optional - for monitoring)
46
+ update_count = 0
47
+ stream.on_update do |data|
48
+ update_count += 1
49
+ if update_count % 10 == 0
50
+ puts "📊 Update ##{update_count}: Price=$#{data[:price].round(2)}"
51
+ end
52
+ end
53
+
54
+ puts "Callbacks registered. Simulating price updates..."
55
+ puts "-" * 60
56
+
57
+ # Simulate real-time price updates
58
+ # In production, these would come from a WebSocket or API
59
+ stock = SQA::Stock.new(ticker: 'AAPL')
60
+ prices = stock.df["adj_close_price"].to_a
61
+ volumes = stock.df["volume"].to_a
62
+ highs = stock.df["high_price"].to_a
63
+ lows = stock.df["low_price"].to_a
64
+
65
+ # Stream the last 150 data points as if they were real-time
66
+ prices.last(150).each_with_index do |price, i|
67
+ stream.update(
68
+ price: price,
69
+ volume: volumes.last(150)[i],
70
+ high: highs.last(150)[i],
71
+ low: lows.last(150)[i],
72
+ timestamp: Time.now - (150 - i) * 60 # 1-minute intervals
73
+ )
74
+
75
+ # Simulate delay (in production, this happens naturally)
76
+ # sleep(0.01) # Uncomment for slower demo
77
+ end
78
+
79
+ puts "-" * 60
80
+ puts "Streaming complete. Final statistics:"
81
+ stats = stream.stats
82
+ puts " Total Updates: #{stats[:updates]}"
83
+ puts " Current Price: $#{stats[:current_price].round(2)}"
84
+ puts " Price Range: $#{stats[:price_range][:min].round(2)} - $#{stats[:price_range][:max].round(2)}"
85
+ puts " Last Signal: #{stats[:last_signal].upcase}"
86
+ puts
87
+
88
+ # Example 2: Multi-Strategy Streaming
89
+ puts "\n" + "=" * 60
90
+ puts "Example 2: Multi-Strategy Consensus"
91
+ puts "=" * 60
92
+ puts
93
+
94
+ multi_stream = SQA::Stream.new(
95
+ ticker: 'MSFT',
96
+ window_size: 100,
97
+ strategies: [
98
+ SQA::Strategy::RSI,
99
+ SQA::Strategy::MACD,
100
+ SQA::Strategy::BollingerBands,
101
+ SQA::Strategy::Stochastic
102
+ ]
103
+ )
104
+
105
+ puts "Multi-strategy stream configured with:"
106
+ multi_stream.strategies.each_with_index do |strategy, i|
107
+ puts " #{i + 1}. #{strategy}"
108
+ end
109
+ puts
110
+
111
+ signal_log = []
112
+
113
+ multi_stream.on_signal do |signal, data|
114
+ signal_log << { signal: signal, price: data[:price], votes: data[:strategies_vote] }
115
+ puts "🎯 CONSENSUS SIGNAL: #{signal.upcase}"
116
+ puts " Price: $#{data[:price].round(2)}"
117
+ puts " Strategy Votes: #{data[:strategies_vote]}"
118
+ puts " Time: #{data[:timestamp].strftime('%Y-%m-%d %H:%M:%S')}"
119
+ puts
120
+ end
121
+
122
+ puts "Simulating market data stream..."
123
+ puts "-" * 60
124
+
125
+ # Simulate streaming
126
+ stock2 = SQA::Stock.new(ticker: 'MSFT')
127
+ prices2 = stock2.df["adj_close_price"].to_a
128
+ volumes2 = stock2.df["volume"].to_a
129
+
130
+ prices2.last(100).each_with_index do |price, i|
131
+ multi_stream.update(
132
+ price: price,
133
+ volume: volumes2.last(100)[i],
134
+ timestamp: Time.now - (100 - i) * 60
135
+ )
136
+ end
137
+
138
+ puts "-" * 60
139
+ puts "Multi-strategy streaming complete."
140
+ puts "Signal summary:"
141
+ signal_log.each_with_index do |log, i|
142
+ puts " #{i + 1}. #{log[:signal].to_s.upcase} at $#{log[:price].round(2)} (votes: #{log[:votes]})"
143
+ end
144
+ puts
145
+
146
+ # Example 3: Custom Indicator Access
147
+ puts "\n" + "=" * 60
148
+ puts "Example 3: Accessing Real-Time Indicators"
149
+ puts "=" * 60
150
+ puts
151
+
152
+ indicator_stream = SQA::Stream.new(
153
+ ticker: 'GOOGL',
154
+ window_size: 50
155
+ )
156
+
157
+ # Stream some data first
158
+ stock3 = SQA::Stock.new(ticker: 'GOOGL')
159
+ stock3.df["adj_close_price"].to_a.last(50).each do |price|
160
+ indicator_stream.update(price: price)
161
+ end
162
+
163
+ puts "Current market data:"
164
+ puts " Current Price: $#{indicator_stream.current_price.round(2)}"
165
+ puts " Recent Prices (last 5): #{indicator_stream.recent_prices(5).map { |p| p.round(2) }}"
166
+ puts
167
+
168
+ puts "Real-time indicators:"
169
+ rsi = indicator_stream.indicator(:rsi, period: 14)
170
+ sma_20 = indicator_stream.indicator(:sma, period: 20)
171
+ ema_12 = indicator_stream.indicator(:ema, period: 12)
172
+ macd_data = indicator_stream.indicator(:macd)
173
+
174
+ puts " RSI(14): #{rsi.last.round(2)}"
175
+ puts " SMA(20): $#{sma_20.last.round(2)}"
176
+ puts " EMA(12): $#{ema_12.last.round(2)}"
177
+ puts " MACD: #{macd_data[0].last.round(4)}"
178
+ puts " Signal: #{macd_data[1].last.round(4)}"
179
+ puts
180
+
181
+ # Example 4: Production-Style WebSocket Simulation
182
+ puts "\n" + "=" * 60
183
+ puts "Example 4: Production WebSocket Pattern"
184
+ puts "=" * 60
185
+ puts
186
+
187
+ class MockWebSocket
188
+ def initialize(ticker)
189
+ @ticker = ticker
190
+ @stock = SQA::Stock.new(ticker: ticker)
191
+ @prices = @stock.df["adj_close_price"].to_a
192
+ @index = @prices.size - 100
193
+ end
194
+
195
+ def on_message(&block)
196
+ @message_handler = block
197
+ end
198
+
199
+ def start
200
+ puts "📡 WebSocket connected to #{@ticker} market data feed"
201
+ puts " Streaming live prices..."
202
+ puts
203
+
204
+ # Simulate receiving messages
205
+ (@index...@prices.size).each do |i|
206
+ # Simulate WebSocket message
207
+ message = {
208
+ symbol: @ticker,
209
+ price: @prices[i],
210
+ volume: rand(1_000_000..10_000_000),
211
+ timestamp: Time.now
212
+ }
213
+
214
+ @message_handler.call(message) if @message_handler
215
+
216
+ # sleep(0.1) # Uncomment for slower demo
217
+ end
218
+
219
+ puts " WebSocket closed"
220
+ end
221
+ end
222
+
223
+ # Initialize stream for production pattern
224
+ production_stream = SQA::Stream.new(
225
+ ticker: 'TSLA',
226
+ window_size: 100,
227
+ strategies: [
228
+ SQA::Strategy::RSI,
229
+ SQA::Strategy::MACD,
230
+ SQA::Strategy::BollingerBands
231
+ ]
232
+ )
233
+
234
+ # Set up signal handling
235
+ production_stream.on_signal do |signal, data|
236
+ # In production, this would trigger:
237
+ # - Alert notifications
238
+ # - Automated trading execution
239
+ # - Logging to database
240
+ # - Risk management checks
241
+
242
+ puts "⚡ TRADING SIGNAL: #{signal.upcase}"
243
+ puts " Execute #{signal} order for #{data[:price]}"
244
+ puts " Consensus: #{data[:strategies_vote]}"
245
+ puts
246
+ end
247
+
248
+ # Set up WebSocket (mock)
249
+ ws = MockWebSocket.new('TSLA')
250
+
251
+ ws.on_message do |message|
252
+ # Feed WebSocket data into stream processor
253
+ production_stream.update(
254
+ price: message[:price],
255
+ volume: message[:volume],
256
+ timestamp: message[:timestamp]
257
+ )
258
+ end
259
+
260
+ # Start streaming
261
+ ws.start
262
+
263
+ puts "-" * 60
264
+ puts "Production simulation complete."
265
+ puts
266
+
267
+ puts "All streaming examples complete!"
268
+ puts
269
+ puts "In production, connect to real WebSocket feeds from:"
270
+ puts " - Alpha Vantage WebSocket API"
271
+ puts " - Yahoo Finance Streaming"
272
+ puts " - Interactive Brokers TWS API"
273
+ puts " - Polygon.io WebSocket"
274
+ puts " - Alpaca Markets WebSocket"
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ ruby '>= 3.2.0'
6
+
7
+ # Web framework
8
+ gem 'rackup'
9
+ gem 'sinatra', '~> 4.0'
10
+ gem 'sinatra-contrib', '~> 4.0'
11
+ gem 'puma', '~> 6.0'
12
+
13
+ # JSON handling
14
+ gem 'json', '~> 2.7'
15
+
16
+ # SQA library dependencies (required when loading SQA from local path)
17
+ gem 'alphavantage'
18
+ gem 'csv'
19
+ gem 'faraday'
20
+ gem 'hashie'
21
+ gem 'kbs'
22
+ gem 'lite-statistics'
23
+ gem 'nenv'
24
+ gem 'redis'
25
+ gem 'ruby_llm'
26
+ gem 'ruby_llm-mcp'
27
+ gem 'shared_tools'
28
+ gem 'sqa-tai'
29
+ gem 'tty-table'
30
+ gem 'eps'
31
+ gem 'polars-df'
32
+ gem 'toml-rb'
33
+ gem 'regent'
34
+
35
+ # SQA library (assumes it's in parent directory)
36
+ # In production, this would be:
37
+ # gem 'sqa'
38
+ # For development, we'll load from local path via require in app.rb
39
+
40
+ group :development do
41
+ gem 'rerun' # Auto-reload on file changes
42
+ end
@@ -0,0 +1,268 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.8.7)
5
+ public_suffix (>= 2.0.2, < 7.0)
6
+ alphavantage (1.2.0)
7
+ faraday (~> 1.4)
8
+ hashie (~> 4.1)
9
+ base64 (0.3.0)
10
+ bigdecimal (3.3.1)
11
+ citrus (3.0.2)
12
+ concurrent-ruby (1.3.5)
13
+ connection_pool (2.5.4)
14
+ csv (3.3.5)
15
+ dentaku (3.5.6)
16
+ bigdecimal
17
+ concurrent-ruby
18
+ eps (0.6.0)
19
+ lightgbm (>= 0.1.7)
20
+ matrix
21
+ nokogiri
22
+ event_stream_parser (1.0.0)
23
+ faraday (1.10.4)
24
+ faraday-em_http (~> 1.0)
25
+ faraday-em_synchrony (~> 1.0)
26
+ faraday-excon (~> 1.1)
27
+ faraday-httpclient (~> 1.0)
28
+ faraday-multipart (~> 1.0)
29
+ faraday-net_http (~> 1.0)
30
+ faraday-net_http_persistent (~> 1.0)
31
+ faraday-patron (~> 1.0)
32
+ faraday-rack (~> 1.0)
33
+ faraday-retry (~> 1.0)
34
+ ruby2_keywords (>= 0.0.4)
35
+ faraday-em_http (1.0.0)
36
+ faraday-em_synchrony (1.0.1)
37
+ faraday-excon (1.1.0)
38
+ faraday-httpclient (1.0.1)
39
+ faraday-multipart (1.1.1)
40
+ multipart-post (~> 2.0)
41
+ faraday-net_http (1.0.2)
42
+ faraday-net_http_persistent (1.2.0)
43
+ faraday-patron (1.0.0)
44
+ faraday-rack (1.0.0)
45
+ faraday-retry (1.0.3)
46
+ ffi (1.17.2)
47
+ ffi (1.17.2-aarch64-linux-gnu)
48
+ ffi (1.17.2-aarch64-linux-musl)
49
+ ffi (1.17.2-arm-linux-gnu)
50
+ ffi (1.17.2-arm-linux-musl)
51
+ ffi (1.17.2-arm64-darwin)
52
+ ffi (1.17.2-x86-linux-gnu)
53
+ ffi (1.17.2-x86-linux-musl)
54
+ ffi (1.17.2-x86_64-darwin)
55
+ ffi (1.17.2-x86_64-linux-gnu)
56
+ ffi (1.17.2-x86_64-linux-musl)
57
+ fiddle (1.1.8)
58
+ hashie (4.1.0)
59
+ http-2 (1.1.1)
60
+ httpx (1.6.3)
61
+ http-2 (>= 1.0.0)
62
+ json (2.16.0)
63
+ json-schema (5.2.2)
64
+ addressable (~> 2.8)
65
+ bigdecimal (~> 3.1)
66
+ kbs (0.1.0)
67
+ sqlite3 (~> 1.6)
68
+ lightgbm (0.4.3)
69
+ ffi
70
+ listen (3.9.0)
71
+ rb-fsevent (~> 0.10, >= 0.10.3)
72
+ rb-inotify (~> 0.9, >= 0.9.10)
73
+ lite-memoize (1.1.1)
74
+ lite-statistics (2.0.0)
75
+ lite-memoize
76
+ logger (1.7.0)
77
+ marcel (1.1.0)
78
+ matrix (0.4.3)
79
+ mini_portile2 (2.8.9)
80
+ multi_json (1.17.0)
81
+ multipart-post (2.4.1)
82
+ mustermann (3.0.4)
83
+ ruby2_keywords (~> 0.0.1)
84
+ nenv (0.3.0)
85
+ nio4r (2.7.5)
86
+ nokogiri (1.18.10)
87
+ mini_portile2 (~> 2.8.2)
88
+ racc (~> 1.4)
89
+ nokogiri (1.18.10-aarch64-linux-gnu)
90
+ racc (~> 1.4)
91
+ nokogiri (1.18.10-aarch64-linux-musl)
92
+ racc (~> 1.4)
93
+ nokogiri (1.18.10-arm-linux-gnu)
94
+ racc (~> 1.4)
95
+ nokogiri (1.18.10-arm-linux-musl)
96
+ racc (~> 1.4)
97
+ nokogiri (1.18.10-arm64-darwin)
98
+ racc (~> 1.4)
99
+ nokogiri (1.18.10-x86_64-darwin)
100
+ racc (~> 1.4)
101
+ nokogiri (1.18.10-x86_64-linux-gnu)
102
+ racc (~> 1.4)
103
+ nokogiri (1.18.10-x86_64-linux-musl)
104
+ racc (~> 1.4)
105
+ openweathermap (0.2.3)
106
+ pastel (0.8.0)
107
+ tty-color (~> 0.5)
108
+ polars-df (0.23.0)
109
+ bigdecimal
110
+ rb_sys
111
+ polars-df (0.23.0-aarch64-linux)
112
+ bigdecimal
113
+ polars-df (0.23.0-aarch64-linux-musl)
114
+ bigdecimal
115
+ polars-df (0.23.0-arm64-darwin)
116
+ bigdecimal
117
+ polars-df (0.23.0-x86_64-darwin)
118
+ bigdecimal
119
+ polars-df (0.23.0-x86_64-linux)
120
+ bigdecimal
121
+ polars-df (0.23.0-x86_64-linux-musl)
122
+ bigdecimal
123
+ public_suffix (6.0.2)
124
+ puma (6.6.1)
125
+ nio4r (~> 2.0)
126
+ racc (1.8.1)
127
+ rack (3.2.4)
128
+ rack-protection (4.2.1)
129
+ base64 (>= 0.1.0)
130
+ logger (>= 1.6.0)
131
+ rack (>= 3.0.0, < 4)
132
+ rack-session (2.1.1)
133
+ base64 (>= 0.1.0)
134
+ rack (>= 3.0.0)
135
+ rackup (2.2.1)
136
+ rack (>= 3)
137
+ rake-compiler-dock (1.9.1)
138
+ rb-fsevent (0.11.2)
139
+ rb-inotify (0.11.1)
140
+ ffi (~> 1.0)
141
+ rb_sys (0.9.117)
142
+ rake-compiler-dock (= 1.9.1)
143
+ redis (5.4.1)
144
+ redis-client (>= 0.22.0)
145
+ redis-client (0.26.1)
146
+ connection_pool
147
+ regent (0.3.3)
148
+ pastel (~> 0.8.0)
149
+ tty-spinner (~> 0.9.3)
150
+ zeitwerk (~> 2.7)
151
+ rerun (0.14.0)
152
+ listen (~> 3.0)
153
+ ruby2_keywords (0.0.5)
154
+ ruby_llm (1.9.1)
155
+ base64
156
+ event_stream_parser (~> 1)
157
+ faraday (>= 1.10.0)
158
+ faraday-multipart (>= 1)
159
+ faraday-net_http (>= 1)
160
+ faraday-retry (>= 1)
161
+ marcel (~> 1.0)
162
+ ruby_llm-schema (~> 0.2.1)
163
+ zeitwerk (~> 2)
164
+ ruby_llm-mcp (0.8.0)
165
+ httpx (~> 1.4)
166
+ json-schema (~> 5.0)
167
+ ruby_llm (~> 1.9)
168
+ zeitwerk (~> 2)
169
+ ruby_llm-schema (0.2.4)
170
+ sequel (5.98.0)
171
+ bigdecimal
172
+ shared_tools (0.3.0)
173
+ dentaku
174
+ nokogiri
175
+ openweathermap
176
+ ruby_llm
177
+ ruby_llm-mcp
178
+ sequel
179
+ zeitwerk
180
+ sinatra (4.2.1)
181
+ logger (>= 1.6.0)
182
+ mustermann (~> 3.0)
183
+ rack (>= 3.0.0, < 4)
184
+ rack-protection (= 4.2.1)
185
+ rack-session (>= 2.0.0, < 3)
186
+ tilt (~> 2.0)
187
+ sinatra-contrib (4.2.1)
188
+ multi_json (>= 0.0.2)
189
+ mustermann (~> 3.0)
190
+ rack-protection (= 4.2.1)
191
+ sinatra (= 4.2.1)
192
+ tilt (~> 2.0)
193
+ sqa-tai (0.1.0)
194
+ ta_lib_ffi (~> 0.3)
195
+ sqlite3 (1.7.3)
196
+ mini_portile2 (~> 2.8.0)
197
+ sqlite3 (1.7.3-aarch64-linux)
198
+ sqlite3 (1.7.3-arm-linux)
199
+ sqlite3 (1.7.3-arm64-darwin)
200
+ sqlite3 (1.7.3-x86-linux)
201
+ sqlite3 (1.7.3-x86_64-darwin)
202
+ sqlite3 (1.7.3-x86_64-linux)
203
+ strings (0.2.1)
204
+ strings-ansi (~> 0.2)
205
+ unicode-display_width (>= 1.5, < 3.0)
206
+ unicode_utils (~> 1.4)
207
+ strings-ansi (0.2.0)
208
+ ta_lib_ffi (0.3.0)
209
+ fiddle (~> 1.1)
210
+ tilt (2.6.1)
211
+ toml-rb (4.1.0)
212
+ citrus (~> 3.0, > 3.0)
213
+ racc (~> 1.7)
214
+ tty-color (0.6.0)
215
+ tty-cursor (0.7.1)
216
+ tty-screen (0.8.2)
217
+ tty-spinner (0.9.3)
218
+ tty-cursor (~> 0.7)
219
+ tty-table (0.12.0)
220
+ pastel (~> 0.8)
221
+ strings (~> 0.2.0)
222
+ tty-screen (~> 0.8)
223
+ unicode-display_width (2.6.0)
224
+ unicode_utils (1.4.0)
225
+ zeitwerk (2.7.3)
226
+
227
+ PLATFORMS
228
+ aarch64-linux-gnu
229
+ aarch64-linux-musl
230
+ arm-linux-gnu
231
+ arm-linux-musl
232
+ arm64-darwin
233
+ x86-linux-gnu
234
+ x86-linux-musl
235
+ x86_64-darwin
236
+ x86_64-linux-gnu
237
+ x86_64-linux-musl
238
+
239
+ DEPENDENCIES
240
+ alphavantage
241
+ csv
242
+ eps
243
+ faraday
244
+ hashie
245
+ json (~> 2.7)
246
+ kbs
247
+ lite-statistics
248
+ nenv
249
+ polars-df
250
+ puma (~> 6.0)
251
+ rackup
252
+ redis
253
+ regent
254
+ rerun
255
+ ruby_llm
256
+ ruby_llm-mcp
257
+ shared_tools
258
+ sinatra (~> 4.0)
259
+ sinatra-contrib (~> 4.0)
260
+ sqa-tai
261
+ toml-rb
262
+ tty-table
263
+
264
+ RUBY VERSION
265
+ ruby 3.3.6p108
266
+
267
+ BUNDLED WITH
268
+ 2.7.2