buda_api 1.0.0 ā 1.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.
- checksums.yaml +4 -4
- data/README.md +101 -4
- data/buda_api.gemspec +4 -1
- data/examples/ai/README.md +314 -0
- data/examples/ai/anomaly_detection_example.rb +412 -0
- data/examples/ai/natural_language_trading.rb +369 -0
- data/examples/ai/report_generation_example.rb +605 -0
- data/examples/ai/risk_management_example.rb +300 -0
- data/examples/ai/trading_assistant_example.rb +295 -0
- data/lib/buda_api/ai/anomaly_detector.rb +787 -0
- data/lib/buda_api/ai/natural_language_trader.rb +541 -0
- data/lib/buda_api/ai/report_generator.rb +1054 -0
- data/lib/buda_api/ai/risk_manager.rb +789 -0
- data/lib/buda_api/ai/trading_assistant.rb +404 -0
- data/lib/buda_api/version.rb +1 -1
- data/lib/buda_api.rb +37 -0
- metadata +32 -1
@@ -0,0 +1,412 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Example: Market Anomaly Detection System
|
5
|
+
# This example demonstrates AI-powered anomaly detection for cryptocurrency markets
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
require_relative '../../lib/buda_api'
|
9
|
+
|
10
|
+
# Configuration
|
11
|
+
API_KEY = ENV['BUDA_API_KEY'] || 'your_api_key_here'
|
12
|
+
API_SECRET = ENV['BUDA_API_SECRET'] || 'your_api_secret_here'
|
13
|
+
|
14
|
+
class AnomalyMonitor
|
15
|
+
def initialize(client, llm_provider = :openai)
|
16
|
+
@client = client
|
17
|
+
@detector = BudaApi::AI::AnomalyDetector.new(client, llm_provider: llm_provider)
|
18
|
+
@monitoring_active = false
|
19
|
+
@alert_count = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_monitoring(markets = nil, options = {})
|
23
|
+
markets ||= BudaApi::Constants::Market::MAJOR
|
24
|
+
@monitoring_active = true
|
25
|
+
@alert_count = 0
|
26
|
+
|
27
|
+
puts "š Starting Anomaly Detection System"
|
28
|
+
puts "Markets: #{markets.join(', ')}"
|
29
|
+
puts "=" * 50
|
30
|
+
|
31
|
+
# Initial scan
|
32
|
+
puts "\nš Initial Market Scan"
|
33
|
+
puts "-" * 30
|
34
|
+
|
35
|
+
initial_results = @detector.detect_market_anomalies(
|
36
|
+
markets: markets,
|
37
|
+
include_ai_analysis: true
|
38
|
+
)
|
39
|
+
|
40
|
+
display_detection_results(initial_results)
|
41
|
+
|
42
|
+
# Set up continuous monitoring
|
43
|
+
if options[:continuous]
|
44
|
+
start_continuous_monitoring(markets, options[:interval] || 300)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set up alerts
|
48
|
+
if options[:alerts]
|
49
|
+
setup_alert_system(options[:alert_config] || {})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_continuous_monitoring(markets, interval_seconds)
|
54
|
+
puts "\nš Starting Continuous Monitoring"
|
55
|
+
puts "Scan interval: #{interval_seconds} seconds"
|
56
|
+
puts "Press Ctrl+C to stop monitoring"
|
57
|
+
puts "-" * 30
|
58
|
+
|
59
|
+
begin
|
60
|
+
while @monitoring_active
|
61
|
+
sleep(interval_seconds)
|
62
|
+
|
63
|
+
puts "\nā° #{Time.now.strftime('%H:%M:%S')} - Scanning markets..."
|
64
|
+
|
65
|
+
results = @detector.detect_market_anomalies(
|
66
|
+
markets: markets,
|
67
|
+
include_ai_analysis: false # Skip AI for frequent scans
|
68
|
+
)
|
69
|
+
|
70
|
+
if results[:anomalies_detected] > 0
|
71
|
+
@alert_count += 1
|
72
|
+
puts "šØ ANOMALIES DETECTED (##{@alert_count})"
|
73
|
+
display_detection_results(results, compact: true)
|
74
|
+
else
|
75
|
+
puts "ā
No anomalies detected"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue Interrupt
|
79
|
+
puts "\nā¹ļø Monitoring stopped by user"
|
80
|
+
@monitoring_active = false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def setup_alert_system(config)
|
85
|
+
puts "\nšØ Setting up Alert System"
|
86
|
+
puts "-" * 30
|
87
|
+
|
88
|
+
alert_system = @detector.setup_anomaly_alerts(config)
|
89
|
+
puts "Alert system configured: #{alert_system[:status]}"
|
90
|
+
puts "Configuration: #{alert_system[:config]}"
|
91
|
+
|
92
|
+
alert_system
|
93
|
+
end
|
94
|
+
|
95
|
+
def analyze_single_market(market_id)
|
96
|
+
puts "\nš Deep Market Analysis: #{market_id}"
|
97
|
+
puts "-" * 30
|
98
|
+
|
99
|
+
# Real-time analysis
|
100
|
+
current_anomalies = @detector.detect_market_anomalies(
|
101
|
+
markets: [market_id],
|
102
|
+
include_ai_analysis: true
|
103
|
+
)
|
104
|
+
|
105
|
+
display_detection_results(current_anomalies)
|
106
|
+
|
107
|
+
# Historical analysis
|
108
|
+
puts "\nš Historical Anomaly Analysis (24h)"
|
109
|
+
puts "-" * 30
|
110
|
+
|
111
|
+
historical_results = @detector.analyze_historical_anomalies(market_id, 24)
|
112
|
+
|
113
|
+
if historical_results[:type] == :historical_analysis
|
114
|
+
puts "Data Points Analyzed: #{historical_results[:data_points]}"
|
115
|
+
puts "Historical Anomalies: #{historical_results[:anomalies_found]}"
|
116
|
+
|
117
|
+
if historical_results[:anomalies_found] > 0
|
118
|
+
puts "\nTop Historical Anomalies:"
|
119
|
+
historical_results[:anomalies].first(5).each_with_index do |anomaly, i|
|
120
|
+
puts "#{i + 1}. #{anomaly[:type]}: #{anomaly[:description]} (#{anomaly[:severity]})"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
else
|
124
|
+
puts "Historical analysis: #{historical_results[:message]}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def display_detection_results(results, compact: false)
|
129
|
+
if results[:type] == :anomaly_detection_error
|
130
|
+
puts "ā Detection Error: #{results[:error]}"
|
131
|
+
return
|
132
|
+
end
|
133
|
+
|
134
|
+
puts "Markets Analyzed: #{results[:markets_analyzed]}"
|
135
|
+
puts "Anomalies Detected: #{results[:anomalies_detected]}"
|
136
|
+
|
137
|
+
if results[:anomalies_detected] == 0
|
138
|
+
puts "ā
All markets operating normally"
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
# Display severity summary
|
143
|
+
severity = results[:severity_summary]
|
144
|
+
if severity[:critical] > 0 || severity[:high] > 0
|
145
|
+
puts "šØ Alert Levels:"
|
146
|
+
puts " Critical: #{severity[:critical]}" if severity[:critical] > 0
|
147
|
+
puts " High: #{severity[:high]}" if severity[:high] > 0
|
148
|
+
puts " Medium: #{severity[:medium]}" if severity[:medium] > 0
|
149
|
+
puts " Low: #{severity[:low]}" if severity[:low] > 0
|
150
|
+
end
|
151
|
+
|
152
|
+
# Display top anomalies
|
153
|
+
display_count = compact ? 3 : 10
|
154
|
+
puts "\nš Detected Anomalies:"
|
155
|
+
|
156
|
+
results[:anomalies].first(display_count).each_with_index do |anomaly, i|
|
157
|
+
severity_emoji = get_severity_emoji(anomaly[:severity])
|
158
|
+
|
159
|
+
puts "#{i + 1}. #{severity_emoji} #{anomaly[:market_id]} - #{anomaly[:type].to_s.upcase}"
|
160
|
+
puts " #{anomaly[:description]}"
|
161
|
+
puts " Severity: #{anomaly[:severity]} (#{anomaly[:severity_score].round(1)})"
|
162
|
+
|
163
|
+
unless compact
|
164
|
+
if anomaly[:recommendation]
|
165
|
+
puts " š” #{anomaly[:recommendation]}"
|
166
|
+
end
|
167
|
+
|
168
|
+
if anomaly[:details]
|
169
|
+
display_anomaly_details(anomaly[:details])
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
puts
|
174
|
+
end
|
175
|
+
|
176
|
+
# Display recommendations
|
177
|
+
if results[:recommendations].any? && !compact
|
178
|
+
puts "š” General Recommendations:"
|
179
|
+
results[:recommendations].each do |rec|
|
180
|
+
puts " ⢠#{rec}"
|
181
|
+
end
|
182
|
+
puts
|
183
|
+
end
|
184
|
+
|
185
|
+
# Display AI analysis if available
|
186
|
+
if results[:ai_analysis] && !compact
|
187
|
+
puts "š§ AI Analysis:"
|
188
|
+
puts results[:ai_analysis][:analysis]
|
189
|
+
puts
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def get_severity_emoji(severity)
|
194
|
+
case severity
|
195
|
+
when :critical then "š«"
|
196
|
+
when :high then "š“"
|
197
|
+
when :medium then "š”"
|
198
|
+
when :low then "š¢"
|
199
|
+
else "ā"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def display_anomaly_details(details)
|
204
|
+
case
|
205
|
+
when details[:current_price]
|
206
|
+
puts " Price: #{details[:current_price]} CLP"
|
207
|
+
puts " Change: #{details[:change_24h]}%" if details[:change_24h]
|
208
|
+
when details[:current_volume]
|
209
|
+
puts " Volume: #{details[:current_volume]}"
|
210
|
+
puts " Ratio: #{details[:volume_ratio]}x normal" if details[:volume_ratio]
|
211
|
+
when details[:current_spread]
|
212
|
+
puts " Spread: #{details[:current_spread]}%"
|
213
|
+
puts " Normal: #{details[:normal_spread]}%" if details[:normal_spread]
|
214
|
+
when details[:large_orders]
|
215
|
+
puts " Large Orders: #{details[:large_orders].length}"
|
216
|
+
puts " Total Value: #{details[:total_value].round(2)} CLP" if details[:total_value]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def generate_anomaly_report
|
221
|
+
puts "\nš Anomaly Detection Report"
|
222
|
+
puts "=" * 50
|
223
|
+
|
224
|
+
# Generate comprehensive report
|
225
|
+
markets = BudaApi::Constants::Market::MAJOR
|
226
|
+
|
227
|
+
full_results = @detector.detect_market_anomalies(
|
228
|
+
markets: markets,
|
229
|
+
include_ai_analysis: true
|
230
|
+
)
|
231
|
+
|
232
|
+
display_detection_results(full_results)
|
233
|
+
|
234
|
+
# Save report to file
|
235
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
236
|
+
filename = "anomaly_report_#{timestamp}.json"
|
237
|
+
|
238
|
+
File.write(filename, JSON.pretty_generate(full_results))
|
239
|
+
puts "š Full report saved to: #{filename}"
|
240
|
+
|
241
|
+
full_results
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def main
|
246
|
+
puts "š Market Anomaly Detection System"
|
247
|
+
puts "=" * 50
|
248
|
+
|
249
|
+
unless BudaApi.ai_available?
|
250
|
+
puts "ā AI features not available. Install ruby_llm gem first."
|
251
|
+
return
|
252
|
+
end
|
253
|
+
|
254
|
+
# Initialize client and monitor
|
255
|
+
client = BudaApi::AuthenticatedClient.new(
|
256
|
+
api_key: API_KEY,
|
257
|
+
api_secret: API_SECRET,
|
258
|
+
sandbox: true
|
259
|
+
)
|
260
|
+
|
261
|
+
monitor = AnomalyMonitor.new(client, :openai)
|
262
|
+
|
263
|
+
puts "Select monitoring mode:"
|
264
|
+
puts "1. One-time scan (default)"
|
265
|
+
puts "2. Continuous monitoring"
|
266
|
+
puts "3. Single market analysis"
|
267
|
+
puts "4. Generate full report"
|
268
|
+
print "Choice (1-4): "
|
269
|
+
|
270
|
+
choice = gets.chomp
|
271
|
+
|
272
|
+
case choice
|
273
|
+
when '2'
|
274
|
+
# Continuous monitoring
|
275
|
+
puts "\nContinuous monitoring setup:"
|
276
|
+
print "Scan interval (seconds, default 300): "
|
277
|
+
interval = gets.chomp.to_i
|
278
|
+
interval = 300 if interval <= 0
|
279
|
+
|
280
|
+
monitor.start_monitoring(
|
281
|
+
nil, # Use default markets
|
282
|
+
continuous: true,
|
283
|
+
interval: interval,
|
284
|
+
alerts: true
|
285
|
+
)
|
286
|
+
|
287
|
+
when '3'
|
288
|
+
# Single market analysis
|
289
|
+
puts "\nAvailable markets: #{BudaApi::Constants::Market::MAJOR.join(', ')}"
|
290
|
+
print "Enter market ID (e.g., BTC-CLP): "
|
291
|
+
market_id = gets.chomp.upcase
|
292
|
+
|
293
|
+
if BudaApi::Constants::Market::MAJOR.include?(market_id)
|
294
|
+
monitor.analyze_single_market(market_id)
|
295
|
+
else
|
296
|
+
puts "ā Invalid market ID"
|
297
|
+
end
|
298
|
+
|
299
|
+
when '4'
|
300
|
+
# Generate full report
|
301
|
+
monitor.generate_anomaly_report
|
302
|
+
|
303
|
+
else
|
304
|
+
# One-time scan (default)
|
305
|
+
monitor.start_monitoring(
|
306
|
+
BudaApi::Constants::Market::MAJOR,
|
307
|
+
continuous: false
|
308
|
+
)
|
309
|
+
end
|
310
|
+
|
311
|
+
rescue BudaApi::ApiError => e
|
312
|
+
puts "ā API Error: #{e.message}"
|
313
|
+
rescue Interrupt
|
314
|
+
puts "\nā¹ļø Monitoring stopped"
|
315
|
+
rescue => e
|
316
|
+
puts "ā Error: #{e.message}"
|
317
|
+
puts e.backtrace.first(3).join("\n") if ENV['DEBUG']
|
318
|
+
end
|
319
|
+
|
320
|
+
def demo_mode
|
321
|
+
puts "\nš Demo Mode - Simulated Anomaly Detection"
|
322
|
+
puts "=" * 50
|
323
|
+
|
324
|
+
# Simulate some anomalies for demonstration
|
325
|
+
simulated_anomalies = [
|
326
|
+
{
|
327
|
+
type: :price_spike,
|
328
|
+
market_id: "BTC-CLP",
|
329
|
+
severity: :high,
|
330
|
+
severity_score: 8.2,
|
331
|
+
description: "Price spike detected: +12.5% change",
|
332
|
+
recommendation: "Monitor for potential reversal or continuation",
|
333
|
+
timestamp: Time.now
|
334
|
+
},
|
335
|
+
{
|
336
|
+
type: :volume_anomaly,
|
337
|
+
market_id: "ETH-CLP",
|
338
|
+
severity: :medium,
|
339
|
+
severity_score: 6.1,
|
340
|
+
description: "Volume anomaly: 4.2x normal volume",
|
341
|
+
recommendation: "Watch for breaking news or large transactions",
|
342
|
+
timestamp: Time.now
|
343
|
+
},
|
344
|
+
{
|
345
|
+
type: :whale_activity,
|
346
|
+
market_id: "BTC-CLP",
|
347
|
+
severity: :high,
|
348
|
+
severity_score: 7.8,
|
349
|
+
description: "Large order activity detected: 3 whale orders",
|
350
|
+
recommendation: "Expect potential price impact from large orders",
|
351
|
+
timestamp: Time.now
|
352
|
+
}
|
353
|
+
]
|
354
|
+
|
355
|
+
simulated_results = {
|
356
|
+
type: :anomaly_detection,
|
357
|
+
timestamp: Time.now,
|
358
|
+
markets_analyzed: 3,
|
359
|
+
anomalies_detected: 3,
|
360
|
+
anomalies: simulated_anomalies,
|
361
|
+
severity_summary: { critical: 0, high: 2, medium: 1, low: 0 },
|
362
|
+
recommendations: [
|
363
|
+
"šØ Multiple high-severity anomalies detected",
|
364
|
+
"š Large order activity - monitor for price impact",
|
365
|
+
"š Increased market volatility - use caution with orders"
|
366
|
+
]
|
367
|
+
}
|
368
|
+
|
369
|
+
puts "š Simulated Detection Results:"
|
370
|
+
|
371
|
+
monitor = Object.new
|
372
|
+
monitor.define_singleton_method(:display_detection_results) do |results, compact: false|
|
373
|
+
# Use the same display logic as the real monitor
|
374
|
+
puts "Markets Analyzed: #{results[:markets_analyzed]}"
|
375
|
+
puts "Anomalies Detected: #{results[:anomalies_detected]}"
|
376
|
+
|
377
|
+
puts "\nšØ Alert Levels:"
|
378
|
+
severity = results[:severity_summary]
|
379
|
+
puts " High: #{severity[:high]}" if severity[:high] > 0
|
380
|
+
puts " Medium: #{severity[:medium]}" if severity[:medium] > 0
|
381
|
+
|
382
|
+
puts "\nš Detected Anomalies:"
|
383
|
+
results[:anomalies].each_with_index do |anomaly, i|
|
384
|
+
severity_emoji = case anomaly[:severity]
|
385
|
+
when :high then "š“"
|
386
|
+
when :medium then "š”"
|
387
|
+
else "š¢"
|
388
|
+
end
|
389
|
+
|
390
|
+
puts "#{i + 1}. #{severity_emoji} #{anomaly[:market_id]} - #{anomaly[:type].to_s.upcase}"
|
391
|
+
puts " #{anomaly[:description]}"
|
392
|
+
puts " š” #{anomaly[:recommendation]}"
|
393
|
+
puts
|
394
|
+
end
|
395
|
+
|
396
|
+
puts "š” Recommendations:"
|
397
|
+
results[:recommendations].each { |rec| puts " ⢠#{rec}" }
|
398
|
+
end
|
399
|
+
|
400
|
+
monitor.display_detection_results(simulated_results)
|
401
|
+
|
402
|
+
puts "\nšÆ This is a demonstration of the anomaly detection system."
|
403
|
+
puts "In real mode, it would analyze actual market data from the Buda API."
|
404
|
+
end
|
405
|
+
|
406
|
+
if __FILE__ == $0
|
407
|
+
if ARGV.include?('--demo')
|
408
|
+
demo_mode
|
409
|
+
else
|
410
|
+
main
|
411
|
+
end
|
412
|
+
end
|