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
|
@@ -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,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,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,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,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
ruby '>= 3.2.0'
|
|
6
|
+
|
|
7
|
+
# Web framework
|
|
8
|
+
gem 'sinatra', '~> 4.0'
|
|
9
|
+
gem 'sinatra-contrib', '~> 4.0'
|
|
10
|
+
gem 'puma', '~> 6.0'
|
|
11
|
+
|
|
12
|
+
# JSON handling
|
|
13
|
+
gem 'json', '~> 2.7'
|
|
14
|
+
|
|
15
|
+
# SQA library (assumes it's in parent directory)
|
|
16
|
+
# In production, this would be:
|
|
17
|
+
# gem 'sqa'
|
|
18
|
+
# For development, we'll load from local path via require in app.rb
|
|
19
|
+
|
|
20
|
+
group :development do
|
|
21
|
+
gem 'rerun' # Auto-reload on file changes
|
|
22
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# SQA Web App - Quick Start Guide
|
|
2
|
+
|
|
3
|
+
Get the SQA web application running in 5 minutes.
|
|
4
|
+
|
|
5
|
+
## 1. Prerequisites
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install TA-Lib (macOS)
|
|
9
|
+
brew install ta-lib
|
|
10
|
+
|
|
11
|
+
# Start Redis
|
|
12
|
+
redis-server
|
|
13
|
+
|
|
14
|
+
# Set API key (optional, uses Yahoo Finance as fallback)
|
|
15
|
+
export AV_API_KEY="your_key_here"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 2. Install Dependencies
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd examples/sinatra_app
|
|
22
|
+
bundle install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 3. Start the Application
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ruby app.rb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The app will start on http://localhost:4567
|
|
32
|
+
|
|
33
|
+
## 4. Quick Tour
|
|
34
|
+
|
|
35
|
+
### Home Page
|
|
36
|
+
- Click on any quick link ticker (AAPL, MSFT, etc.)
|
|
37
|
+
- Or enter a custom ticker in the search box
|
|
38
|
+
|
|
39
|
+
### Dashboard
|
|
40
|
+
- View interactive price charts (candlestick or line)
|
|
41
|
+
- See volume chart
|
|
42
|
+
- Check RSI and MACD indicators
|
|
43
|
+
- View key metrics (52-week high/low, current RSI, market regime)
|
|
44
|
+
- Compare all strategies at once
|
|
45
|
+
|
|
46
|
+
### Analysis Page
|
|
47
|
+
- Market regime detection (bull/bear/sideways)
|
|
48
|
+
- Seasonal patterns (best months and quarters)
|
|
49
|
+
- FPOP (Future Period) analysis
|
|
50
|
+
- Risk metrics (VaR, Sharpe ratio, max drawdown)
|
|
51
|
+
|
|
52
|
+
### Backtest Page
|
|
53
|
+
- Select a strategy (RSI, MACD, SMA, EMA, Bollinger Bands, KBS)
|
|
54
|
+
- View detailed backtest results
|
|
55
|
+
- Compare all strategies side-by-side
|
|
56
|
+
|
|
57
|
+
## 5. Keyboard Shortcuts
|
|
58
|
+
|
|
59
|
+
- `Ctrl/Cmd + K` - Open ticker search
|
|
60
|
+
- `Escape` - Close modal
|
|
61
|
+
|
|
62
|
+
## 6. API Examples
|
|
63
|
+
|
|
64
|
+
### Get Stock Data
|
|
65
|
+
```bash
|
|
66
|
+
curl http://localhost:4567/api/stock/AAPL
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Get Technical Indicators
|
|
70
|
+
```bash
|
|
71
|
+
curl http://localhost:4567/api/indicators/AAPL
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Run Backtest
|
|
75
|
+
```bash
|
|
76
|
+
curl -X POST http://localhost:4567/api/backtest/AAPL \
|
|
77
|
+
-d "strategy=RSI"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Get Market Analysis
|
|
81
|
+
```bash
|
|
82
|
+
curl http://localhost:4567/api/analyze/AAPL
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Compare Strategies
|
|
86
|
+
```bash
|
|
87
|
+
curl -X POST http://localhost:4567/api/compare/AAPL
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 7. Development Mode
|
|
91
|
+
|
|
92
|
+
Auto-reload on file changes:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
gem install rerun
|
|
96
|
+
rerun 'ruby app.rb'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 8. Troubleshooting
|
|
100
|
+
|
|
101
|
+
**Port already in use:**
|
|
102
|
+
```bash
|
|
103
|
+
# Kill process on port 4567
|
|
104
|
+
lsof -ti:4567 | xargs kill -9
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**TA-Lib not found:**
|
|
108
|
+
```bash
|
|
109
|
+
# Reinstall TA-Lib
|
|
110
|
+
brew reinstall ta-lib
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Redis connection error:**
|
|
114
|
+
```bash
|
|
115
|
+
# Start Redis
|
|
116
|
+
redis-server &
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Stock data not loading:**
|
|
120
|
+
- Check internet connection
|
|
121
|
+
- Verify ticker symbol is valid
|
|
122
|
+
- Try a different ticker (e.g., AAPL, MSFT, GOOGL)
|
|
123
|
+
|
|
124
|
+
## 9. Popular Tickers
|
|
125
|
+
|
|
126
|
+
Try these popular stocks:
|
|
127
|
+
|
|
128
|
+
**Tech:**
|
|
129
|
+
- AAPL (Apple)
|
|
130
|
+
- MSFT (Microsoft)
|
|
131
|
+
- GOOGL (Google)
|
|
132
|
+
- AMZN (Amazon)
|
|
133
|
+
- NVDA (NVIDIA)
|
|
134
|
+
- TSLA (Tesla)
|
|
135
|
+
|
|
136
|
+
**Finance:**
|
|
137
|
+
- JPM (JP Morgan)
|
|
138
|
+
- BAC (Bank of America)
|
|
139
|
+
- GS (Goldman Sachs)
|
|
140
|
+
|
|
141
|
+
**ETFs:**
|
|
142
|
+
- SPY (S&P 500)
|
|
143
|
+
- QQQ (NASDAQ 100)
|
|
144
|
+
- DIA (Dow Jones)
|
|
145
|
+
|
|
146
|
+
## 10. Next Steps
|
|
147
|
+
|
|
148
|
+
- Explore different strategies on the Backtest page
|
|
149
|
+
- Compare seasonal patterns across multiple stocks
|
|
150
|
+
- Check market regime before making trade decisions
|
|
151
|
+
- Use FPOP analysis to identify high-probability setups
|
|
152
|
+
|
|
153
|
+
## Support
|
|
154
|
+
|
|
155
|
+
See README.md for full documentation and API reference.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
Happy analyzing! 📊
|