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.
@@ -0,0 +1,404 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BudaApi
4
+ module AI
5
+ # AI-powered trading assistant that provides market analysis and trading insights
6
+ class TradingAssistant
7
+ def initialize(client, llm_provider: :openai, model: nil)
8
+ @client = client
9
+ @llm = RubyLLM.new(
10
+ provider: llm_provider,
11
+ model: model || default_model_for_provider(llm_provider)
12
+ )
13
+
14
+ BudaApi::Logger.info("Trading Assistant initialized with #{llm_provider}")
15
+ end
16
+
17
+ # Analyze market conditions and provide trading insights
18
+ #
19
+ # @param market_id [String] market identifier (e.g., "BTC-CLP")
20
+ # @return [Hash] analysis results with sentiment, signals, and recommendations
21
+ # @example
22
+ # assistant = BudaApi.trading_assistant(client)
23
+ # analysis = assistant.analyze_market("BTC-CLP")
24
+ # puts analysis[:sentiment] # => "bullish", "bearish", or "neutral"
25
+ def analyze_market(market_id)
26
+ BudaApi::Logger.info("Analyzing market #{market_id}")
27
+
28
+ # Gather comprehensive market data
29
+ ticker = @client.ticker(market_id)
30
+ order_book = @client.order_book(market_id)
31
+ trades = @client.trades(market_id, limit: 50)
32
+
33
+ # Get historical data for context
34
+ start_time = Time.now - 86400 # 24 hours
35
+ candlesticks = @client.candlestick_report(market_id, start_at: start_time)
36
+
37
+ prompt = build_market_analysis_prompt(market_id, ticker, order_book, trades, candlesticks)
38
+
39
+ begin
40
+ analysis = @llm.complete(prompt, max_tokens: 1000)
41
+ parsed_analysis = parse_market_analysis(analysis)
42
+
43
+ BudaApi::Logger.info("Market analysis completed for #{market_id}")
44
+ parsed_analysis
45
+
46
+ rescue => e
47
+ BudaApi::Logger.error("Failed to analyze market: #{e.message}")
48
+ {
49
+ error: "Analysis failed: #{e.message}",
50
+ market_id: market_id,
51
+ timestamp: Time.now
52
+ }
53
+ end
54
+ end
55
+
56
+ # Suggest trading strategy based on market conditions and user preferences
57
+ #
58
+ # @param market_id [String] market identifier
59
+ # @param balance [Models::Balance] current balance information
60
+ # @param risk_tolerance [String] risk level ("low", "medium", "high")
61
+ # @param investment_horizon [String] time horizon ("short", "medium", "long")
62
+ # @return [Hash] strategy recommendations
63
+ def suggest_trading_strategy(market_id, balance, risk_tolerance: 'medium', investment_horizon: 'medium')
64
+ BudaApi::Logger.info("Generating trading strategy for #{market_id}")
65
+
66
+ # Get market analysis first
67
+ market_analysis = analyze_market(market_id)
68
+
69
+ # Get portfolio context
70
+ portfolio_data = gather_portfolio_data(balance)
71
+
72
+ prompt = build_strategy_prompt(
73
+ market_id, market_analysis, portfolio_data,
74
+ risk_tolerance, investment_horizon
75
+ )
76
+
77
+ begin
78
+ strategy = @llm.complete(prompt, max_tokens: 1200)
79
+ parsed_strategy = parse_strategy_recommendations(strategy)
80
+
81
+ BudaApi::Logger.info("Trading strategy generated for #{market_id}")
82
+ parsed_strategy
83
+
84
+ rescue => e
85
+ BudaApi::Logger.error("Failed to generate strategy: #{e.message}")
86
+ {
87
+ error: "Strategy generation failed: #{e.message}",
88
+ market_id: market_id,
89
+ timestamp: Time.now
90
+ }
91
+ end
92
+ end
93
+
94
+ # Get AI-powered insights on optimal entry/exit points
95
+ #
96
+ # @param market_id [String] market identifier
97
+ # @param action [String] intended action ("buy" or "sell")
98
+ # @param amount [Float] intended trade amount
99
+ # @return [Hash] entry/exit recommendations
100
+ def get_entry_exit_signals(market_id, action, amount)
101
+ BudaApi::Logger.info("Getting #{action} signals for #{amount} #{market_id}")
102
+
103
+ # Get current market state
104
+ ticker = @client.ticker(market_id)
105
+ order_book = @client.order_book(market_id)
106
+
107
+ # Get quotation for the intended trade
108
+ quotation_type = action == "buy" ? "bid_given_size" : "ask_given_size"
109
+ quotation = @client.quotation(market_id, quotation_type, amount)
110
+
111
+ prompt = build_entry_exit_prompt(market_id, action, amount, ticker, order_book, quotation)
112
+
113
+ begin
114
+ signals = @llm.complete(prompt, max_tokens: 800)
115
+ parsed_signals = parse_entry_exit_signals(signals)
116
+
117
+ BudaApi::Logger.info("Entry/exit signals generated for #{market_id}")
118
+ parsed_signals
119
+
120
+ rescue => e
121
+ BudaApi::Logger.error("Failed to generate signals: #{e.message}")
122
+ {
123
+ error: "Signal generation failed: #{e.message}",
124
+ market_id: market_id,
125
+ action: action,
126
+ timestamp: Time.now
127
+ }
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ def default_model_for_provider(provider)
134
+ case provider
135
+ when :openai then "gpt-4"
136
+ when :anthropic then "claude-3-sonnet-20240229"
137
+ when :google then "gemini-pro"
138
+ else nil
139
+ end
140
+ end
141
+
142
+ def build_market_analysis_prompt(market_id, ticker, order_book, trades, candlesticks)
143
+ recent_trades_summary = summarize_recent_trades(trades)
144
+ price_action = analyze_price_action(candlesticks) if candlesticks.any?
145
+
146
+ """
147
+ Analyze the following cryptocurrency market data for #{market_id} and provide comprehensive insights:
148
+
149
+ CURRENT MARKET STATE:
150
+ - Current Price: #{ticker.last_price}
151
+ - 24h Change: #{ticker.price_variation_24h}%
152
+ - 7d Change: #{ticker.price_variation_7d}%
153
+ - Volume: #{ticker.volume}
154
+ - Min Ask: #{ticker.min_ask}
155
+ - Max Bid: #{ticker.max_bid}
156
+
157
+ ORDER BOOK:
158
+ - Best Ask: #{order_book.best_ask.price} (#{order_book.best_ask.amount})
159
+ - Best Bid: #{order_book.best_bid.price} (#{order_book.best_bid.amount})
160
+ - Spread: #{order_book.spread_percentage}%
161
+ - Order Book Depth: #{order_book.asks.length} asks, #{order_book.bids.length} bids
162
+
163
+ RECENT TRADING ACTIVITY:
164
+ #{recent_trades_summary}
165
+
166
+ #{price_action ? "PRICE ACTION (24H):\n#{price_action}" : ""}
167
+
168
+ Please provide a comprehensive analysis including:
169
+ 1. Overall market sentiment (bullish/bearish/neutral)
170
+ 2. Key support and resistance levels
171
+ 3. Market momentum indicators
172
+ 4. Volume analysis
173
+ 5. Short-term price prediction (next 1-4 hours)
174
+ 6. Risk factors to consider
175
+ 7. Trading opportunities
176
+ 8. Confidence level (1-10) for your analysis
177
+
178
+ Format your response as structured analysis with clear sections.
179
+ """
180
+ end
181
+
182
+ def build_strategy_prompt(market_id, market_analysis, portfolio_data, risk_tolerance, investment_horizon)
183
+ """
184
+ Based on the following information, suggest a comprehensive trading strategy:
185
+
186
+ MARKET: #{market_id}
187
+ MARKET ANALYSIS:
188
+ #{format_analysis_for_prompt(market_analysis)}
189
+
190
+ PORTFOLIO CONTEXT:
191
+ #{format_portfolio_for_prompt(portfolio_data)}
192
+
193
+ USER PREFERENCES:
194
+ - Risk Tolerance: #{risk_tolerance}
195
+ - Investment Horizon: #{investment_horizon}
196
+
197
+ Please provide:
198
+ 1. Recommended strategy type (DCA, swing trade, scalping, etc.)
199
+ 2. Position sizing recommendations
200
+ 3. Entry price targets
201
+ 4. Exit strategies (profit targets and stop losses)
202
+ 5. Risk management rules
203
+ 6. Timeline for the strategy
204
+ 7. Specific action steps
205
+ 8. Alternative scenarios to consider
206
+
207
+ Tailor recommendations to the user's risk tolerance and investment horizon.
208
+ """
209
+ end
210
+
211
+ def build_entry_exit_prompt(market_id, action, amount, ticker, order_book, quotation)
212
+ """
213
+ Provide optimal entry/exit timing for the following trade:
214
+
215
+ INTENDED TRADE:
216
+ - Market: #{market_id}
217
+ - Action: #{action.upcase}
218
+ - Amount: #{amount}
219
+ - Estimated Cost: #{quotation.quote_balance_change}
220
+ - Fee: #{quotation.fee}
221
+
222
+ CURRENT MARKET CONDITIONS:
223
+ - Current Price: #{ticker.last_price}
224
+ - Best Ask: #{order_book.best_ask.price}
225
+ - Best Bid: #{order_book.best_bid.price}
226
+ - Spread: #{order_book.spread_percentage}%
227
+
228
+ Please recommend:
229
+ 1. Optimal timing for entry (immediate, wait for dip, etc.)
230
+ 2. Suggested order type (market vs limit)
231
+ 3. If limit order, suggested price levels
232
+ 4. Risk factors for this specific trade
233
+ 5. Alternative amounts to consider
234
+ 6. Market conditions to monitor before executing
235
+
236
+ Be specific with price levels and timing recommendations.
237
+ """
238
+ end
239
+
240
+ def summarize_recent_trades(trades)
241
+ return "No recent trades available" if trades.count == 0
242
+
243
+ total_volume = trades.trades.sum { |t| t.amount.amount }
244
+ buy_volume = trades.trades.select { |t| t.direction == "up" }.sum { |t| t.amount.amount }
245
+ sell_volume = total_volume - buy_volume
246
+
247
+ "#{trades.count} recent trades, #{total_volume.round(4)} total volume (#{(buy_volume/total_volume*100).round(1)}% buys)"
248
+ end
249
+
250
+ def analyze_price_action(candlesticks)
251
+ return nil if candlesticks.length < 2
252
+
253
+ first_candle = candlesticks.first
254
+ last_candle = candlesticks.last
255
+
256
+ price_change = ((last_candle.close - first_candle.open) / first_candle.open * 100).round(2)
257
+ high = candlesticks.max_by(&:high).high
258
+ low = candlesticks.min_by(&:low).low
259
+
260
+ "Price moved #{price_change}% (High: #{high}, Low: #{low})"
261
+ end
262
+
263
+ def gather_portfolio_data(balance)
264
+ {
265
+ currency: balance.currency,
266
+ total_amount: balance.amount,
267
+ available: balance.available_amount,
268
+ frozen: balance.frozen_amount,
269
+ pending_withdrawals: balance.pending_withdraw_amount
270
+ }
271
+ end
272
+
273
+ def parse_market_analysis(analysis_text)
274
+ # Extract structured data from AI response
275
+ sentiment = extract_sentiment(analysis_text)
276
+ confidence = extract_confidence(analysis_text)
277
+
278
+ {
279
+ raw_analysis: analysis_text,
280
+ sentiment: sentiment,
281
+ confidence: confidence,
282
+ timestamp: Time.now,
283
+ summary: extract_summary(analysis_text)
284
+ }
285
+ end
286
+
287
+ def parse_strategy_recommendations(strategy_text)
288
+ {
289
+ raw_strategy: strategy_text,
290
+ strategy_type: extract_strategy_type(strategy_text),
291
+ action_steps: extract_action_steps(strategy_text),
292
+ risk_level: extract_risk_level(strategy_text),
293
+ timestamp: Time.now
294
+ }
295
+ end
296
+
297
+ def parse_entry_exit_signals(signals_text)
298
+ {
299
+ raw_signals: signals_text,
300
+ timing_recommendation: extract_timing(signals_text),
301
+ suggested_price: extract_price_target(signals_text),
302
+ risk_factors: extract_risk_factors(signals_text),
303
+ timestamp: Time.now
304
+ }
305
+ end
306
+
307
+ # Simple text extraction methods (could be enhanced with more sophisticated parsing)
308
+ def extract_sentiment(text)
309
+ text_lower = text.downcase
310
+ if text_lower.include?('bullish') || text_lower.include?('positive')
311
+ 'bullish'
312
+ elsif text_lower.include?('bearish') || text_lower.include?('negative')
313
+ 'bearish'
314
+ else
315
+ 'neutral'
316
+ end
317
+ end
318
+
319
+ def extract_confidence(text)
320
+ # Look for confidence patterns like "confidence: 7/10" or "7 out of 10"
321
+ match = text.match(/confidence[:\s]*(\d+)(?:\/10|\/\d+|\s*out\s*of\s*10)/i)
322
+ match ? match[1].to_i : 5 # Default to 5 if not found
323
+ end
324
+
325
+ def extract_summary(text)
326
+ # Extract first few sentences as summary
327
+ sentences = text.split(/[.!?]+/)
328
+ sentences.first(2).join('. ').strip
329
+ end
330
+
331
+ def extract_strategy_type(text)
332
+ text_lower = text.downcase
333
+ if text_lower.include?('dca') || text_lower.include?('dollar cost')
334
+ 'DCA'
335
+ elsif text_lower.include?('swing')
336
+ 'swing_trading'
337
+ elsif text_lower.include?('scalp')
338
+ 'scalping'
339
+ elsif text_lower.include?('hold') || text_lower.include?('hodl')
340
+ 'hold'
341
+ else
342
+ 'mixed'
343
+ end
344
+ end
345
+
346
+ def extract_action_steps(text)
347
+ # Look for numbered lists or bullet points
348
+ steps = text.scan(/(?:^\d+\.|\*|\-)\s*(.+)$/m).flatten
349
+ steps.empty? ? ["Review the full strategy analysis"] : steps
350
+ end
351
+
352
+ def extract_risk_level(text)
353
+ text_lower = text.downcase
354
+ if text_lower.include?('high risk') || text_lower.include?('very risky')
355
+ 'high'
356
+ elsif text_lower.include?('low risk') || text_lower.include?('conservative')
357
+ 'low'
358
+ else
359
+ 'medium'
360
+ end
361
+ end
362
+
363
+ def extract_timing(text)
364
+ text_lower = text.downcase
365
+ if text_lower.include?('immediate') || text_lower.include?('now')
366
+ 'immediate'
367
+ elsif text_lower.include?('wait') || text_lower.include?('delay')
368
+ 'wait'
369
+ elsif text_lower.include?('monitor') || text_lower.include?('watch')
370
+ 'monitor'
371
+ else
372
+ 'evaluate'
373
+ end
374
+ end
375
+
376
+ def extract_price_target(text)
377
+ # Look for price patterns (simple regex for CLP prices)
378
+ match = text.match(/(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*CLP/i)
379
+ match ? match[1].gsub(',', '').to_f : nil
380
+ end
381
+
382
+ def extract_risk_factors(text)
383
+ # Look for risk-related bullet points or sentences
384
+ risks = text.scan(/(?:risk|danger|warning|caution)[:\s]*([^.!?]+)/i).flatten
385
+ risks.empty? ? ["Market volatility"] : risks.map(&:strip)
386
+ end
387
+
388
+ def format_analysis_for_prompt(analysis)
389
+ return "Analysis not available" unless analysis.is_a?(Hash)
390
+
391
+ "Sentiment: #{analysis[:sentiment] || 'unknown'}\n" +
392
+ "Confidence: #{analysis[:confidence] || 'unknown'}\n" +
393
+ "Summary: #{analysis[:summary] || analysis[:raw_analysis]&.slice(0, 200)}"
394
+ end
395
+
396
+ def format_portfolio_for_prompt(portfolio)
397
+ "Currency: #{portfolio[:currency]}\n" +
398
+ "Available: #{portfolio[:available]}\n" +
399
+ "Total: #{portfolio[:total_amount]}\n" +
400
+ "Frozen: #{portfolio[:frozen]}"
401
+ end
402
+ end
403
+ end
404
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BudaApi
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
data/lib/buda_api.rb CHANGED
@@ -9,6 +9,18 @@ require_relative "buda_api/constants"
9
9
  require_relative "buda_api/errors"
10
10
  require_relative "buda_api/logger"
11
11
 
12
+ # AI-enhanced features (optional)
13
+ begin
14
+ require "ruby_llm"
15
+ require_relative "buda_api/ai/trading_assistant"
16
+ require_relative "buda_api/ai/natural_language_trader"
17
+ require_relative "buda_api/ai/risk_manager"
18
+ require_relative "buda_api/ai/report_generator"
19
+ require_relative "buda_api/ai/anomaly_detector"
20
+ rescue LoadError
21
+ # RubyLLM not available, AI features disabled
22
+ end
23
+
12
24
  # BudaApi is the main module for the Buda.com API Ruby SDK
13
25
  #
14
26
  # This SDK provides comprehensive access to the Buda.com cryptocurrency exchange API
@@ -78,4 +90,29 @@ module BudaApi
78
90
  def self.authenticated_client(api_key:, api_secret:, **options)
79
91
  AuthenticatedClient.new(api_key: api_key, api_secret: api_secret, **options)
80
92
  end
93
+
94
+ # Check if AI features are available
95
+ # @return [Boolean] true if RubyLLM is available
96
+ def self.ai_available?
97
+ defined?(RubyLLM) && defined?(BudaApi::AI)
98
+ end
99
+
100
+ # Convenience method to create an AI-enhanced trading assistant
101
+ # @param client [AuthenticatedClient] authenticated client
102
+ # @param llm_provider [Symbol] LLM provider (:openai, :anthropic, etc.)
103
+ # @return [AI::TradingAssistant] new trading assistant instance
104
+ def self.trading_assistant(client, llm_provider: :openai)
105
+ raise Error, "AI features not available. Install ruby_llm gem." unless ai_available?
106
+
107
+ AI::TradingAssistant.new(client, llm_provider: llm_provider)
108
+ end
109
+
110
+ # Convenience method to create a natural language trader
111
+ # @param client [AuthenticatedClient] authenticated client
112
+ # @return [AI::NaturalLanguageTrader] new natural language trader instance
113
+ def self.natural_language_trader(client)
114
+ raise Error, "AI features not available. Install ruby_llm gem." unless ai_available?
115
+
116
+ AI::NaturalLanguageTrader.new(client)
117
+ end
81
118
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: buda_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Buda API Ruby SDK
@@ -65,6 +65,26 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '2.6'
68
+ - !ruby/object:Gem::Dependency
69
+ name: ruby_llm
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.5'
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 0.5.0
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '0.5'
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 0.5.0
68
88
  - !ruby/object:Gem::Dependency
69
89
  name: rspec
70
90
  requirement: !ruby/object:Gem::Requirement
@@ -153,11 +173,22 @@ files:
153
173
  - Rakefile
154
174
  - buda_api.gemspec
155
175
  - examples/.env.example
176
+ - examples/ai/README.md
177
+ - examples/ai/anomaly_detection_example.rb
178
+ - examples/ai/natural_language_trading.rb
179
+ - examples/ai/report_generation_example.rb
180
+ - examples/ai/risk_management_example.rb
181
+ - examples/ai/trading_assistant_example.rb
156
182
  - examples/authenticated_api_example.rb
157
183
  - examples/error_handling_example.rb
158
184
  - examples/public_api_example.rb
159
185
  - examples/trading_bot_example.rb
160
186
  - lib/buda_api.rb
187
+ - lib/buda_api/ai/anomaly_detector.rb
188
+ - lib/buda_api/ai/natural_language_trader.rb
189
+ - lib/buda_api/ai/report_generator.rb
190
+ - lib/buda_api/ai/risk_manager.rb
191
+ - lib/buda_api/ai/trading_assistant.rb
161
192
  - lib/buda_api/authenticated_client.rb
162
193
  - lib/buda_api/client.rb
163
194
  - lib/buda_api/constants.rb