kbs 0.0.1

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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/COMMITS.md +196 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +481 -0
  7. data/Rakefile +8 -0
  8. data/examples/README.md +531 -0
  9. data/examples/advanced_example.rb +270 -0
  10. data/examples/ai_enhanced_kbs.rb +523 -0
  11. data/examples/blackboard_demo.rb +50 -0
  12. data/examples/car_diagnostic.rb +64 -0
  13. data/examples/concurrent_inference_demo.rb +363 -0
  14. data/examples/csv_trading_system.rb +559 -0
  15. data/examples/iot_demo_using_dsl.rb +83 -0
  16. data/examples/portfolio_rebalancing_system.rb +651 -0
  17. data/examples/redis_trading_demo.rb +177 -0
  18. data/examples/sample_stock_data.csv +46 -0
  19. data/examples/stock_trading_advanced.rb +469 -0
  20. data/examples/stock_trading_system.rb.bak +563 -0
  21. data/examples/timestamped_trading.rb +286 -0
  22. data/examples/trading_demo.rb +334 -0
  23. data/examples/working_demo.rb +176 -0
  24. data/lib/kbs/alpha_memory.rb +37 -0
  25. data/lib/kbs/beta_memory.rb +57 -0
  26. data/lib/kbs/blackboard/audit_log.rb +115 -0
  27. data/lib/kbs/blackboard/engine.rb +83 -0
  28. data/lib/kbs/blackboard/fact.rb +65 -0
  29. data/lib/kbs/blackboard/memory.rb +191 -0
  30. data/lib/kbs/blackboard/message_queue.rb +96 -0
  31. data/lib/kbs/blackboard/persistence/hybrid_store.rb +118 -0
  32. data/lib/kbs/blackboard/persistence/redis_store.rb +218 -0
  33. data/lib/kbs/blackboard/persistence/sqlite_store.rb +242 -0
  34. data/lib/kbs/blackboard/persistence/store.rb +55 -0
  35. data/lib/kbs/blackboard/redis_audit_log.rb +107 -0
  36. data/lib/kbs/blackboard/redis_message_queue.rb +111 -0
  37. data/lib/kbs/blackboard.rb +23 -0
  38. data/lib/kbs/condition.rb +26 -0
  39. data/lib/kbs/dsl/condition_helpers.rb +57 -0
  40. data/lib/kbs/dsl/knowledge_base.rb +86 -0
  41. data/lib/kbs/dsl/pattern_evaluator.rb +69 -0
  42. data/lib/kbs/dsl/rule_builder.rb +115 -0
  43. data/lib/kbs/dsl/variable.rb +35 -0
  44. data/lib/kbs/dsl.rb +18 -0
  45. data/lib/kbs/fact.rb +43 -0
  46. data/lib/kbs/join_node.rb +117 -0
  47. data/lib/kbs/negation_node.rb +88 -0
  48. data/lib/kbs/production_node.rb +28 -0
  49. data/lib/kbs/rete_engine.rb +108 -0
  50. data/lib/kbs/rule.rb +46 -0
  51. data/lib/kbs/token.rb +37 -0
  52. data/lib/kbs/version.rb +5 -0
  53. data/lib/kbs/working_memory.rb +32 -0
  54. data/lib/kbs.rb +20 -0
  55. metadata +164 -0
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/kbs'
4
+ require 'time'
5
+
6
+ class TimestampedTradingSystem
7
+ def initialize
8
+ @engine = KBS::ReteEngine.new
9
+ @market_open = Time.parse("09:30:00")
10
+ @market_close = Time.parse("16:00:00")
11
+ setup_time_aware_rules
12
+ end
13
+
14
+ def setup_time_aware_rules
15
+ # Rule 1: Fresh data only
16
+ fresh_momentum_rule = KBS::Rule.new(
17
+ "fresh_momentum",
18
+ conditions: [
19
+ KBS::Condition.new(:stock, {
20
+ price_change: ->(p) { p && p > 2 },
21
+ timestamp: ->(t) { t && (Time.now - t) < 60 }, # Within 1 minute
22
+ market_session: "regular"
23
+ })
24
+ ],
25
+ action: lambda do |facts, bindings|
26
+ stock = facts.find { |f| f.type == :stock }
27
+ age = Time.now - stock[:timestamp]
28
+ puts "🚀 FRESH MOMENTUM: #{stock[:symbol]}"
29
+ puts " Price Change: +#{stock[:price_change]}%"
30
+ puts " Data Age: #{age.round(1)} seconds"
31
+ puts " Market Time: #{stock[:market_time]}"
32
+ puts " Recommendation: BUY (fresh signal)"
33
+ end,
34
+ priority: 15
35
+ )
36
+
37
+ # Rule 2: Stale data warning
38
+ stale_data_rule = KBS::Rule.new(
39
+ "stale_data_warning",
40
+ conditions: [
41
+ KBS::Condition.new(:stock, {
42
+ timestamp: ->(t) { t && (Time.now - t) > 300 } # Older than 5 minutes
43
+ })
44
+ ],
45
+ action: lambda do |facts, bindings|
46
+ stock = facts.find { |f| f.type == :stock }
47
+ age = Time.now - stock[:timestamp]
48
+ puts "⚠️ STALE DATA: #{stock[:symbol]}"
49
+ puts " Data Age: #{(age / 60).round(1)} minutes"
50
+ puts " Last Update: #{stock[:timestamp]}"
51
+ puts " ACTION: IGNORE - Data too old"
52
+ end,
53
+ priority: 20
54
+ )
55
+
56
+ # Rule 3: Market hours validation
57
+ market_hours_rule = KBS::Rule.new(
58
+ "market_hours_check",
59
+ conditions: [
60
+ KBS::Condition.new(:stock, {
61
+ market_session: "after_hours",
62
+ volume: ->(v) { v && v > 100_000 }
63
+ })
64
+ ],
65
+ action: lambda do |facts, bindings|
66
+ stock = facts.find { |f| f.type == :stock }
67
+ puts "🌙 AFTER HOURS ACTIVITY: #{stock[:symbol]}"
68
+ puts " Volume: #{format_volume(stock[:volume])}"
69
+ puts " Time: #{stock[:market_time]}"
70
+ puts " ACTION: Monitor for gap potential"
71
+ end
72
+ )
73
+
74
+ # Rule 4: Rapid price movement detection
75
+ rapid_movement_rule = KBS::Rule.new(
76
+ "rapid_movement",
77
+ conditions: [
78
+ KBS::Condition.new(:price_tick, {
79
+ time_delta: ->(d) { d && d < 5 }, # Within 5 seconds
80
+ price_delta: ->(d) { d && d.abs > 0.50 } # More than $0.50 move
81
+ })
82
+ ],
83
+ action: lambda do |facts, bindings|
84
+ tick = facts.find { |f| f.type == :price_tick }
85
+ direction = tick[:price_delta] > 0 ? "📈 UP" : "📉 DOWN"
86
+ puts "⚡ RAPID MOVEMENT: #{tick[:symbol]} #{direction}"
87
+ puts " Price Change: #{tick[:price_delta] > 0 ? '+' : ''}$#{tick[:price_delta]}"
88
+ puts " Time Frame: #{tick[:time_delta]} seconds"
89
+ puts " ACTION: Check for news catalyst"
90
+ end
91
+ )
92
+
93
+ # Rule 5: Time-based strategy
94
+ opening_gap_rule = KBS::Rule.new(
95
+ "opening_gap",
96
+ conditions: [
97
+ KBS::Condition.new(:stock, {
98
+ market_time: ->(t) {
99
+ t && (t.hour == 9 && t.min >= 30 && t.min <= 35) # First 5 minutes
100
+ },
101
+ gap_percentage: ->(g) { g && g.abs > 1 }
102
+ })
103
+ ],
104
+ action: lambda do |facts, bindings|
105
+ stock = facts.find { |f| f.type == :stock }
106
+ direction = stock[:gap_percentage] > 0 ? "GAP UP" : "GAP DOWN"
107
+ puts "🔔 OPENING #{direction}: #{stock[:symbol]}"
108
+ puts " Gap: #{stock[:gap_percentage] > 0 ? '+' : ''}#{stock[:gap_percentage]}%"
109
+ puts " Time: #{stock[:market_time]}"
110
+ puts " Volume: #{format_volume(stock[:volume])}"
111
+ puts " ACTION: Monitor for gap fill or continuation"
112
+ end
113
+ )
114
+
115
+ # Rule 6: End of day positioning
116
+ eod_rule = KBS::Rule.new(
117
+ "end_of_day",
118
+ conditions: [
119
+ KBS::Condition.new(:stock, {
120
+ market_time: ->(t) {
121
+ t && (t.hour == 15 && t.min >= 45) # Last 15 minutes
122
+ }
123
+ }),
124
+ KBS::Condition.new(:position, { status: "open" })
125
+ ],
126
+ action: lambda do |facts, bindings|
127
+ stock = facts.find { |f| f.type == :stock }
128
+ position = facts.find { |f| f.type == :position }
129
+ puts "🕐 END OF DAY: #{position[:symbol]}"
130
+ puts " Current Time: #{stock[:market_time]}"
131
+ puts " Position P&L: #{position[:unrealized_pnl]}"
132
+ puts " ACTION: Consider closing before market close"
133
+ end
134
+ )
135
+
136
+ @engine.add_rule(fresh_momentum_rule)
137
+ @engine.add_rule(stale_data_rule)
138
+ @engine.add_rule(market_hours_rule)
139
+ @engine.add_rule(rapid_movement_rule)
140
+ @engine.add_rule(opening_gap_rule)
141
+ @engine.add_rule(eod_rule)
142
+ end
143
+
144
+ def format_volume(volume)
145
+ if volume >= 1_000_000
146
+ "#{(volume / 1_000_000.0).round(1)}M"
147
+ elsif volume >= 1_000
148
+ "#{(volume / 1_000.0).round(1)}K"
149
+ else
150
+ volume.to_s
151
+ end
152
+ end
153
+
154
+ def determine_market_session(time)
155
+ hour_min = time.hour * 100 + time.min
156
+
157
+ case hour_min
158
+ when 400..929 then "pre_market"
159
+ when 930..1559 then "regular"
160
+ when 1600..2000 then "after_hours"
161
+ else "closed"
162
+ end
163
+ end
164
+
165
+ def add_timestamped_stock_fact(symbol, price, volume, options = {})
166
+ timestamp = options[:timestamp] || Time.now
167
+ market_time = options[:market_time] || timestamp
168
+
169
+ @engine.add_fact(:stock, {
170
+ symbol: symbol,
171
+ price: price,
172
+ volume: volume,
173
+ timestamp: timestamp,
174
+ market_time: market_time,
175
+ market_session: determine_market_session(market_time),
176
+ price_change: options[:price_change] || 0,
177
+ gap_percentage: options[:gap_percentage] || 0
178
+ })
179
+ end
180
+
181
+ def add_price_tick(symbol, old_price, new_price, old_time, new_time)
182
+ price_delta = new_price - old_price
183
+ time_delta = new_time - old_time
184
+
185
+ @engine.add_fact(:price_tick, {
186
+ symbol: symbol,
187
+ old_price: old_price,
188
+ new_price: new_price,
189
+ price_delta: price_delta,
190
+ time_delta: time_delta,
191
+ timestamp: new_time
192
+ })
193
+ end
194
+
195
+ def simulate_trading_day
196
+ puts "🏦 TIMESTAMPED STOCK TRADING SYSTEM"
197
+ puts "=" * 60
198
+
199
+ base_time = Time.parse("2024-08-15 09:30:00")
200
+
201
+ # Scenario 1: Market Open with Gap
202
+ puts "\n📊 SCENARIO 1: Market Open Gap Up"
203
+ puts "-" * 40
204
+ @engine.working_memory.facts.clear
205
+
206
+ add_timestamped_stock_fact("AAPL", 185.50, 2_500_000, {
207
+ market_time: base_time + 2*60, # 9:32 AM
208
+ price_change: 2.1,
209
+ gap_percentage: 2.3
210
+ })
211
+ @engine.run
212
+
213
+ # Scenario 2: Fresh Momentum Signal
214
+ puts "\n📊 SCENARIO 2: Fresh Momentum (Recent Data)"
215
+ puts "-" * 40
216
+ @engine.working_memory.facts.clear
217
+
218
+ add_timestamped_stock_fact("GOOGL", 142.80, 1_200_000, {
219
+ timestamp: Time.now - 30, # 30 seconds ago
220
+ market_time: Time.now - 30,
221
+ price_change: 3.2
222
+ })
223
+ @engine.run
224
+
225
+ # Scenario 3: Stale Data
226
+ puts "\n📊 SCENARIO 3: Stale Data Warning"
227
+ puts "-" * 40
228
+ @engine.working_memory.facts.clear
229
+
230
+ add_timestamped_stock_fact("TSLA", 195.40, 800_000, {
231
+ timestamp: Time.now - 600, # 10 minutes ago
232
+ market_time: Time.now - 600,
233
+ price_change: 1.5
234
+ })
235
+ @engine.run
236
+
237
+ # Scenario 4: After Hours Activity
238
+ puts "\n📊 SCENARIO 4: After Hours Trading"
239
+ puts "-" * 40
240
+ @engine.working_memory.facts.clear
241
+
242
+ after_hours_time = Time.parse("2024-08-15 17:30:00")
243
+ add_timestamped_stock_fact("NVDA", 425.80, 150_000, {
244
+ market_time: after_hours_time,
245
+ timestamp: after_hours_time
246
+ })
247
+ @engine.run
248
+
249
+ # Scenario 5: Rapid Price Movement
250
+ puts "\n📊 SCENARIO 5: Rapid Price Tick"
251
+ puts "-" * 40
252
+ @engine.working_memory.facts.clear
253
+
254
+ tick_time = Time.now
255
+ add_price_tick("META", 298.50, 299.25, tick_time - 3, tick_time)
256
+ @engine.run
257
+
258
+ # Scenario 6: End of Day
259
+ puts "\n📊 SCENARIO 6: End of Trading Day"
260
+ puts "-" * 40
261
+ @engine.working_memory.facts.clear
262
+
263
+ eod_time = Time.parse("2024-08-15 15:50:00")
264
+ add_timestamped_stock_fact("AMZN", 142.30, 900_000, {
265
+ market_time: eod_time,
266
+ timestamp: eod_time
267
+ })
268
+
269
+ @engine.add_fact(:position, {
270
+ symbol: "AMZN",
271
+ status: "open",
272
+ shares: 100,
273
+ entry_price: 140.00,
274
+ unrealized_pnl: 230.00
275
+ })
276
+ @engine.run
277
+
278
+ puts "\n" + "=" * 60
279
+ puts "TIMESTAMPED TRADING SIMULATION COMPLETE"
280
+ end
281
+ end
282
+
283
+ if __FILE__ == $0
284
+ system = TimestampedTradingSystem.new
285
+ system.simulate_trading_day
286
+ end
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/kbs'
4
+
5
+ class TradingDemo
6
+ def initialize
7
+ @engine = KBS::ReteEngine.new
8
+ setup_trading_rules
9
+ puts "🏦 STOCK TRADING EXPERT SYSTEM LOADED"
10
+ puts "📊 #{@engine.rules.size} trading strategies active"
11
+ end
12
+
13
+ def setup_trading_rules
14
+ golden_cross = KBS::Rule.new(
15
+ "golden_cross_buy",
16
+ conditions: [
17
+ KBS::Condition.new(:ma_signal, { type: "golden_cross" }),
18
+ KBS::Condition.new(:stock, { volume: ->(v) { v && v > 500000 } })
19
+ ],
20
+ action: lambda do |facts, bindings|
21
+ stock = facts.find { |f| f.type == :stock }
22
+ signal = facts.find { |f| f.type == :ma_signal }
23
+ puts "📈 GOLDEN CROSS SIGNAL: #{stock[:symbol]}"
24
+ puts " 50-MA crossed above 200-MA"
25
+ puts " Volume: #{format_volume(stock[:volume])}"
26
+ puts " Price: $#{stock[:price]}"
27
+ puts " Recommendation: STRONG BUY"
28
+ end
29
+ )
30
+
31
+ momentum_buy = KBS::Rule.new(
32
+ "momentum_breakout",
33
+ conditions: [
34
+ KBS::Condition.new(:stock, {
35
+ price_change: ->(p) { p > 2 },
36
+ rsi: ->(r) { r.between?(50, 75) }
37
+ })
38
+ ],
39
+ action: lambda do |facts, bindings|
40
+ stock = facts.find { |f| f.type == :stock }
41
+ puts "🚀 MOMENTUM BREAKOUT: #{stock[:symbol]}"
42
+ puts " Price Change: +#{stock[:price_change].round(1)}%"
43
+ puts " RSI: #{stock[:rsi].round(1)} (strong but not overbought)"
44
+ puts " Recommendation: BUY"
45
+ end
46
+ )
47
+
48
+ oversold_buy = KBS::Rule.new(
49
+ "oversold_reversal",
50
+ conditions: [
51
+ KBS::Condition.new(:stock, { rsi: ->(r) { r < 35 } }),
52
+ KBS::Condition.new(:market, { trend: "bullish" })
53
+ ],
54
+ action: lambda do |facts, bindings|
55
+ stock = facts.find { |f| f.type == :stock }
56
+ puts "🔄 OVERSOLD REVERSAL: #{stock[:symbol]}"
57
+ puts " RSI: #{stock[:rsi].round(1)} (oversold)"
58
+ puts " Market: Bullish environment"
59
+ puts " Recommendation: CONTRARIAN BUY"
60
+ end
61
+ )
62
+
63
+ earnings_volatility = KBS::Rule.new(
64
+ "earnings_play",
65
+ conditions: [
66
+ KBS::Condition.new(:earnings, { days_until: ->(d) { d <= 3 } }),
67
+ KBS::Condition.new(:options, { iv: ->(v) { v > 40 } })
68
+ ],
69
+ action: lambda do |facts, bindings|
70
+ earnings = facts.find { |f| f.type == :earnings }
71
+ options = facts.find { |f| f.type == :options }
72
+ puts "💰 EARNINGS PLAY: #{earnings[:symbol]}"
73
+ puts " Earnings in #{earnings[:days_until]} day(s)"
74
+ puts " Implied Volatility: #{options[:iv].round(1)}%"
75
+ puts " Recommendation: VOLATILITY STRATEGY"
76
+ end
77
+ )
78
+
79
+ stop_loss = KBS::Rule.new(
80
+ "stop_loss_alert",
81
+ conditions: [
82
+ KBS::Condition.new(:position, {
83
+ status: "open",
84
+ loss_pct: ->(l) { l > 8 }
85
+ })
86
+ ],
87
+ action: lambda do |facts, bindings|
88
+ position = facts.find { |f| f.type == :position }
89
+ puts "🛑 STOP LOSS TRIGGERED: #{position[:symbol]}"
90
+ puts " Loss: #{position[:loss_pct].round(1)}%"
91
+ puts " Entry: $#{position[:entry_price]}"
92
+ puts " Current: $#{position[:current_price]}"
93
+ puts " Recommendation: SELL IMMEDIATELY"
94
+ end
95
+ )
96
+
97
+ risk_warning = KBS::Rule.new(
98
+ "concentration_risk",
99
+ conditions: [
100
+ KBS::Condition.new(:portfolio, {
101
+ concentration: ->(c) { c > 25 }
102
+ })
103
+ ],
104
+ action: lambda do |facts, bindings|
105
+ portfolio = facts.find { |f| f.type == :portfolio }
106
+ puts "⚠️ CONCENTRATION RISK: #{portfolio[:top_holding]}"
107
+ puts " Position Size: #{portfolio[:concentration].round(1)}% of portfolio"
108
+ puts " Recommendation: DIVERSIFY HOLDINGS"
109
+ end
110
+ )
111
+
112
+ news_catalyst = KBS::Rule.new(
113
+ "news_sentiment",
114
+ conditions: [
115
+ KBS::Condition.new(:news, {
116
+ sentiment: ->(s) { s.abs > 0.6 },
117
+ impact: "high"
118
+ })
119
+ ],
120
+ action: lambda do |facts, bindings|
121
+ news = facts.find { |f| f.type == :news }
122
+ sentiment = news[:sentiment] > 0 ? "POSITIVE" : "NEGATIVE"
123
+ action = news[:sentiment] > 0 ? "BUY" : "SELL"
124
+ puts "📰 NEWS CATALYST: #{news[:symbol]}"
125
+ puts " Sentiment: #{sentiment} (#{news[:sentiment].round(2)})"
126
+ puts " Impact: HIGH"
127
+ puts " Recommendation: #{action} ON NEWS"
128
+ end
129
+ )
130
+
131
+ sector_rotation = KBS::Rule.new(
132
+ "sector_strength",
133
+ conditions: [
134
+ KBS::Condition.new(:sector, {
135
+ performance: ->(p) { p > 5 },
136
+ trend: "accelerating"
137
+ })
138
+ ],
139
+ action: lambda do |facts, bindings|
140
+ sector = facts.find { |f| f.type == :sector }
141
+ puts "🔄 SECTOR ROTATION: #{sector[:name]}"
142
+ puts " Performance: +#{sector[:performance].round(1)}%"
143
+ puts " Trend: Accelerating"
144
+ puts " Recommendation: INCREASE ALLOCATION"
145
+ end
146
+ )
147
+
148
+ @engine.add_rule(golden_cross)
149
+ @engine.add_rule(momentum_buy)
150
+ @engine.add_rule(oversold_buy)
151
+ @engine.add_rule(earnings_volatility)
152
+ @engine.add_rule(stop_loss)
153
+ @engine.add_rule(risk_warning)
154
+ @engine.add_rule(news_catalyst)
155
+ @engine.add_rule(sector_rotation)
156
+ end
157
+
158
+ def format_volume(volume)
159
+ if volume >= 1_000_000
160
+ "#{(volume / 1_000_000.0).round(1)}M"
161
+ elsif volume >= 1_000
162
+ "#{(volume / 1_000.0).round(1)}K"
163
+ else
164
+ volume.to_s
165
+ end
166
+ end
167
+
168
+ def generate_scenario(name, &block)
169
+ puts "\n" + "="*60
170
+ puts "SCENARIO: #{name}"
171
+ puts "="*60
172
+
173
+ @engine.working_memory.facts.clear
174
+
175
+ yield
176
+
177
+ puts "\nFacts in working memory:"
178
+ @engine.working_memory.facts.each do |fact|
179
+ puts " #{fact}"
180
+ end
181
+ puts ""
182
+
183
+ @engine.run
184
+
185
+ puts "-"*60
186
+ end
187
+
188
+ def demo_scenarios
189
+ generate_scenario("Bull Market with Golden Cross") do
190
+ @engine.add_fact(:stock, {
191
+ symbol: "AAPL",
192
+ price: 185.50,
193
+ volume: 1_250_000,
194
+ price_change: 1.2,
195
+ rsi: 65
196
+ })
197
+
198
+ @engine.add_fact(:ma_signal, {
199
+ symbol: "AAPL",
200
+ type: "golden_cross"
201
+ })
202
+
203
+ @engine.add_fact(:market, { trend: "bullish" })
204
+ end
205
+
206
+ generate_scenario("Momentum Breakout") do
207
+ @engine.add_fact(:stock, {
208
+ symbol: "NVDA",
209
+ price: 425.80,
210
+ volume: 980_000,
211
+ price_change: 4.7,
212
+ rsi: 68
213
+ })
214
+
215
+ @engine.add_fact(:market, { trend: "bullish" })
216
+ end
217
+
218
+ generate_scenario("Oversold Bounce Opportunity") do
219
+ @engine.add_fact(:stock, {
220
+ symbol: "TSLA",
221
+ price: 178.90,
222
+ volume: 750_000,
223
+ price_change: -2.1,
224
+ rsi: 28
225
+ })
226
+
227
+ @engine.add_fact(:market, { trend: "bullish" })
228
+ end
229
+
230
+ generate_scenario("Earnings Volatility Play") do
231
+ @engine.add_fact(:earnings, {
232
+ symbol: "GOOGL",
233
+ days_until: 2,
234
+ expected_move: 8.5
235
+ })
236
+
237
+ @engine.add_fact(:options, {
238
+ symbol: "GOOGL",
239
+ iv: 45.2,
240
+ iv_rank: 75
241
+ })
242
+ end
243
+
244
+ generate_scenario("Stop Loss Alert") do
245
+ @engine.add_fact(:position, {
246
+ symbol: "META",
247
+ status: "open",
248
+ entry_price: 320.00,
249
+ current_price: 285.40,
250
+ loss_pct: 10.8,
251
+ shares: 100
252
+ })
253
+ end
254
+
255
+ generate_scenario("Portfolio Risk Warning") do
256
+ @engine.add_fact(:portfolio, {
257
+ total_value: 250_000,
258
+ top_holding: "AAPL",
259
+ concentration: 32.5,
260
+ cash_pct: 5
261
+ })
262
+ end
263
+
264
+ generate_scenario("News-Driven Trade") do
265
+ @engine.add_fact(:news, {
266
+ symbol: "MSFT",
267
+ sentiment: -0.75,
268
+ impact: "high",
269
+ headlines: "Major cloud outage affects services"
270
+ })
271
+
272
+ @engine.add_fact(:stock, {
273
+ symbol: "MSFT",
274
+ price: 395.20,
275
+ price_change: -1.2,
276
+ volume: 890_000
277
+ })
278
+ end
279
+
280
+ generate_scenario("Sector Rotation Signal") do
281
+ @engine.add_fact(:sector, {
282
+ name: "Technology",
283
+ performance: 7.3,
284
+ trend: "accelerating",
285
+ leaders: ["AAPL", "MSFT", "GOOGL"]
286
+ })
287
+ end
288
+
289
+ generate_scenario("Complex Multi-Signal Day") do
290
+ @engine.add_fact(:stock, {
291
+ symbol: "AMZN",
292
+ price: 142.50,
293
+ volume: 1_800_000,
294
+ price_change: 3.8,
295
+ rsi: 72
296
+ })
297
+
298
+ @engine.add_fact(:ma_signal, {
299
+ symbol: "AMZN",
300
+ type: "golden_cross"
301
+ })
302
+
303
+ @engine.add_fact(:news, {
304
+ symbol: "AMZN",
305
+ sentiment: 0.8,
306
+ impact: "high",
307
+ headlines: "AWS wins major government contract"
308
+ })
309
+
310
+ @engine.add_fact(:earnings, {
311
+ symbol: "AMZN",
312
+ days_until: 1,
313
+ expected_move: 6.2
314
+ })
315
+
316
+ @engine.add_fact(:options, {
317
+ symbol: "AMZN",
318
+ iv: 52.1,
319
+ iv_rank: 85
320
+ })
321
+
322
+ @engine.add_fact(:market, { trend: "bullish" })
323
+ end
324
+ end
325
+ end
326
+
327
+ if __FILE__ == $0
328
+ demo = TradingDemo.new
329
+ demo.demo_scenarios
330
+
331
+ puts "\n" + "="*60
332
+ puts "TRADING SYSTEM DEMONSTRATION COMPLETE"
333
+ puts "="*60
334
+ end