ollama-client 0.2.5 → 0.2.7
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/CHANGELOG.md +22 -0
- data/README.md +336 -91
- data/RELEASE_NOTES_v0.2.6.md +41 -0
- data/docs/AREAS_FOR_CONSIDERATION.md +325 -0
- data/docs/EXAMPLE_REORGANIZATION.md +412 -0
- data/docs/FEATURES_ADDED.md +12 -1
- data/docs/GETTING_STARTED.md +361 -0
- data/docs/INTEGRATION_TESTING.md +170 -0
- data/docs/NEXT_STEPS_SUMMARY.md +114 -0
- data/docs/PERSONAS.md +383 -0
- data/docs/QUICK_START.md +195 -0
- data/docs/TESTING.md +392 -170
- data/docs/TEST_CHECKLIST.md +450 -0
- data/examples/README.md +62 -63
- data/examples/basic_chat.rb +33 -0
- data/examples/basic_generate.rb +29 -0
- data/examples/mcp_executor.rb +39 -0
- data/examples/mcp_http_executor.rb +45 -0
- data/examples/tool_calling_parsing.rb +59 -0
- data/examples/tool_dto_example.rb +0 -0
- data/exe/ollama-client +128 -1
- data/lib/ollama/agent/planner.rb +7 -2
- data/lib/ollama/chat_session.rb +101 -0
- data/lib/ollama/client.rb +41 -35
- data/lib/ollama/config.rb +9 -4
- data/lib/ollama/document_loader.rb +1 -1
- data/lib/ollama/embeddings.rb +61 -28
- data/lib/ollama/errors.rb +1 -0
- data/lib/ollama/mcp/http_client.rb +149 -0
- data/lib/ollama/mcp/stdio_client.rb +146 -0
- data/lib/ollama/mcp/tools_bridge.rb +72 -0
- data/lib/ollama/mcp.rb +31 -0
- data/lib/ollama/options.rb +3 -1
- data/lib/ollama/personas.rb +287 -0
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama_client.rb +17 -5
- metadata +22 -48
- data/examples/advanced_complex_schemas.rb +0 -366
- data/examples/advanced_edge_cases.rb +0 -241
- data/examples/advanced_error_handling.rb +0 -200
- data/examples/advanced_multi_step_agent.rb +0 -341
- data/examples/advanced_performance_testing.rb +0 -186
- data/examples/chat_console.rb +0 -143
- data/examples/complete_workflow.rb +0 -245
- data/examples/dhan_console.rb +0 -843
- data/examples/dhanhq/README.md +0 -236
- data/examples/dhanhq/agents/base_agent.rb +0 -74
- data/examples/dhanhq/agents/data_agent.rb +0 -66
- data/examples/dhanhq/agents/orchestrator_agent.rb +0 -120
- data/examples/dhanhq/agents/technical_analysis_agent.rb +0 -252
- data/examples/dhanhq/agents/trading_agent.rb +0 -81
- data/examples/dhanhq/analysis/market_structure.rb +0 -138
- data/examples/dhanhq/analysis/pattern_recognizer.rb +0 -192
- data/examples/dhanhq/analysis/trend_analyzer.rb +0 -88
- data/examples/dhanhq/builders/market_context_builder.rb +0 -67
- data/examples/dhanhq/dhanhq_agent.rb +0 -829
- data/examples/dhanhq/indicators/technical_indicators.rb +0 -158
- data/examples/dhanhq/scanners/intraday_options_scanner.rb +0 -492
- data/examples/dhanhq/scanners/swing_scanner.rb +0 -247
- data/examples/dhanhq/schemas/agent_schemas.rb +0 -61
- data/examples/dhanhq/services/base_service.rb +0 -46
- data/examples/dhanhq/services/data_service.rb +0 -118
- data/examples/dhanhq/services/trading_service.rb +0 -59
- data/examples/dhanhq/technical_analysis_agentic_runner.rb +0 -411
- data/examples/dhanhq/technical_analysis_runner.rb +0 -420
- data/examples/dhanhq/test_tool_calling.rb +0 -538
- data/examples/dhanhq/test_tool_calling_verbose.rb +0 -251
- data/examples/dhanhq/utils/instrument_helper.rb +0 -32
- data/examples/dhanhq/utils/parameter_cleaner.rb +0 -28
- data/examples/dhanhq/utils/parameter_normalizer.rb +0 -45
- data/examples/dhanhq/utils/rate_limiter.rb +0 -23
- data/examples/dhanhq/utils/trading_parameter_normalizer.rb +0 -72
- data/examples/dhanhq_agent.rb +0 -964
- data/examples/dhanhq_tools.rb +0 -1663
- data/examples/multi_step_agent_with_external_data.rb +0 -368
- data/examples/structured_outputs_chat.rb +0 -72
- data/examples/structured_tools.rb +0 -89
- data/examples/test_dhanhq_tool_calling.rb +0 -375
- data/examples/test_tool_calling.rb +0 -160
- data/examples/tool_calling_direct.rb +0 -124
- data/examples/tool_calling_pattern.rb +0 -269
- data/exe/dhan_console +0 -4
|
@@ -1,829 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# DhanHQ Agent - Complete trading agent with data retrieval and trading operations
|
|
5
|
-
# Refactored with proper OOP structure, SOLID principles, and clean architecture
|
|
6
|
-
|
|
7
|
-
require "json"
|
|
8
|
-
require "date"
|
|
9
|
-
require "dhan_hq"
|
|
10
|
-
require_relative "../../lib/ollama_client"
|
|
11
|
-
require_relative "../dhanhq_tools"
|
|
12
|
-
|
|
13
|
-
# Load all modules in dependency order
|
|
14
|
-
require_relative "utils/instrument_helper"
|
|
15
|
-
require_relative "utils/rate_limiter"
|
|
16
|
-
require_relative "utils/parameter_normalizer"
|
|
17
|
-
require_relative "utils/parameter_cleaner"
|
|
18
|
-
require_relative "utils/trading_parameter_normalizer"
|
|
19
|
-
require_relative "builders/market_context_builder"
|
|
20
|
-
require_relative "schemas/agent_schemas"
|
|
21
|
-
require_relative "services/base_service"
|
|
22
|
-
require_relative "services/data_service"
|
|
23
|
-
require_relative "services/trading_service"
|
|
24
|
-
require_relative "indicators/technical_indicators"
|
|
25
|
-
require_relative "analysis/market_structure"
|
|
26
|
-
require_relative "analysis/pattern_recognizer"
|
|
27
|
-
require_relative "analysis/trend_analyzer"
|
|
28
|
-
require_relative "agents/base_agent"
|
|
29
|
-
require_relative "agents/data_agent"
|
|
30
|
-
require_relative "agents/trading_agent"
|
|
31
|
-
require_relative "agents/technical_analysis_agent"
|
|
32
|
-
require_relative "agents/orchestrator_agent"
|
|
33
|
-
require_relative "scanners/swing_scanner"
|
|
34
|
-
require_relative "scanners/intraday_options_scanner"
|
|
35
|
-
|
|
36
|
-
module DhanHQ
|
|
37
|
-
# Main agent orchestrator
|
|
38
|
-
class Agent
|
|
39
|
-
def initialize(ollama_client: nil, trading_ollama_client: nil)
|
|
40
|
-
@ollama_client = ollama_client || Ollama::Client.new
|
|
41
|
-
@trading_ollama_client = trading_ollama_client || create_trading_client
|
|
42
|
-
@data_agent = Agents::DataAgent.new(ollama_client: @ollama_client)
|
|
43
|
-
@trading_agent = Agents::TradingAgent.new(ollama_client: @trading_ollama_client)
|
|
44
|
-
@analysis_agent = Agents::TechnicalAnalysisAgent.new(ollama_client: @ollama_client)
|
|
45
|
-
@orchestrator_agent = Agents::OrchestratorAgent.new(ollama_client: @ollama_client)
|
|
46
|
-
@swing_scanner = Scanners::SwingScanner.new
|
|
47
|
-
@options_scanner = Scanners::IntradayOptionsScanner.new
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
attr_reader :data_agent, :trading_agent, :analysis_agent, :orchestrator_agent, :swing_scanner, :options_scanner
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
def create_trading_client
|
|
55
|
-
config = Ollama::Config.new
|
|
56
|
-
config.timeout = 60
|
|
57
|
-
Ollama::Client.new(config: config)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def price_range_stats(price_ranges)
|
|
63
|
-
return nil unless price_ranges.is_a?(Array) && price_ranges.any?
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
min: price_ranges.min.round(2),
|
|
67
|
-
max: price_ranges.max.round(2),
|
|
68
|
-
avg: (price_ranges.sum / price_ranges.length).round(2),
|
|
69
|
-
count: price_ranges.length
|
|
70
|
-
}
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def build_expired_options_summary(stats)
|
|
74
|
-
{
|
|
75
|
-
data_points: stats[:data_points] || 0,
|
|
76
|
-
avg_volume: stats[:avg_volume]&.round(2),
|
|
77
|
-
avg_open_interest: stats[:avg_open_interest]&.round(2),
|
|
78
|
-
avg_implied_volatility: stats[:avg_implied_volatility]&.round(4),
|
|
79
|
-
price_range_stats: price_range_stats(stats[:price_ranges]),
|
|
80
|
-
has_ohlc: stats[:has_ohlc],
|
|
81
|
-
has_volume: stats[:has_volume],
|
|
82
|
-
has_open_interest: stats[:has_open_interest],
|
|
83
|
-
has_implied_volatility: stats[:has_implied_volatility]
|
|
84
|
-
}
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def build_option_chain_summary(chain_result)
|
|
88
|
-
chain = chain_result[:result][:chain]
|
|
89
|
-
underlying_price = chain_result[:result][:underlying_last_price]
|
|
90
|
-
|
|
91
|
-
unless chain.is_a?(Hash)
|
|
92
|
-
return [{ expiry: chain_result[:result][:expiry], chain_type: chain.class },
|
|
93
|
-
underlying_price]
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
strike_prices = chain.keys.sort_by(&:to_f)
|
|
97
|
-
first_strike_data = strike_prices.any? ? chain[strike_prices.first] : nil
|
|
98
|
-
atm_strike = select_atm_strike(strike_prices, underlying_price)
|
|
99
|
-
atm_data = atm_strike ? chain[atm_strike] : nil
|
|
100
|
-
sample_greeks = build_sample_greeks(atm_data, atm_strike)
|
|
101
|
-
|
|
102
|
-
summary = {
|
|
103
|
-
expiry: chain_result[:result][:expiry],
|
|
104
|
-
underlying_last_price: underlying_price,
|
|
105
|
-
strikes_count: strike_prices.length,
|
|
106
|
-
has_call_options: option_type_present?(first_strike_data, "ce"),
|
|
107
|
-
has_put_options: option_type_present?(first_strike_data, "pe"),
|
|
108
|
-
has_greeks: sample_greeks.any?,
|
|
109
|
-
strike_range: strike_range_summary(strike_prices),
|
|
110
|
-
sample_greeks: sample_greeks.any? ? sample_greeks : nil
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
[summary, underlying_price]
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def select_atm_strike(strike_prices, underlying_price)
|
|
117
|
-
return strike_prices.first unless underlying_price && strike_prices.any?
|
|
118
|
-
|
|
119
|
-
strike_prices.min_by { |strike| (strike.to_f - underlying_price).abs }
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def option_type_present?(strike_data, key)
|
|
123
|
-
strike_data.is_a?(Hash) && (strike_data.key?(key) || strike_data.key?(key.to_sym))
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def strike_range_summary(strike_prices)
|
|
127
|
-
return nil if strike_prices.empty?
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
min: strike_prices.first,
|
|
131
|
-
max: strike_prices.last,
|
|
132
|
-
sample_strikes: strike_prices.first(5)
|
|
133
|
-
}
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def build_sample_greeks(atm_data, atm_strike)
|
|
137
|
-
return {} unless atm_data.is_a?(Hash)
|
|
138
|
-
|
|
139
|
-
sample = {}
|
|
140
|
-
call_data = atm_data["ce"] || atm_data[:ce]
|
|
141
|
-
put_data = atm_data["pe"] || atm_data[:pe]
|
|
142
|
-
|
|
143
|
-
call_greeks = extract_greeks(call_data)
|
|
144
|
-
sample[:call] = greeks_summary(call_greeks, call_data, atm_strike) if call_greeks
|
|
145
|
-
|
|
146
|
-
put_greeks = extract_greeks(put_data)
|
|
147
|
-
sample[:put] = greeks_summary(put_greeks, put_data, atm_strike) if put_greeks
|
|
148
|
-
|
|
149
|
-
sample
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def extract_greeks(option_data)
|
|
153
|
-
return nil unless option_data.is_a?(Hash)
|
|
154
|
-
return nil unless option_data.key?("greeks") || option_data.key?(:greeks)
|
|
155
|
-
|
|
156
|
-
option_data["greeks"] || option_data[:greeks]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def greeks_summary(greeks, option_data, atm_strike)
|
|
160
|
-
{
|
|
161
|
-
strike: atm_strike,
|
|
162
|
-
delta: greeks["delta"] || greeks[:delta],
|
|
163
|
-
theta: greeks["theta"] || greeks[:theta],
|
|
164
|
-
gamma: greeks["gamma"] || greeks[:gamma],
|
|
165
|
-
vega: greeks["vega"] || greeks[:vega],
|
|
166
|
-
iv: option_data["implied_volatility"] || option_data[:implied_volatility],
|
|
167
|
-
oi: option_data["oi"] || option_data[:oi],
|
|
168
|
-
last_price: option_data["last_price"] || option_data[:last_price]
|
|
169
|
-
}
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def format_score_breakdown(details)
|
|
173
|
-
"Trend=#{details[:trend]}, RSI=#{details[:rsi]}, MACD=#{details[:macd]}, " \
|
|
174
|
-
"Structure=#{details[:structure]}, Patterns=#{details[:patterns]}"
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def format_option_setup_details(setup)
|
|
178
|
-
iv = setup[:iv]&.round(2) || "N/A"
|
|
179
|
-
oi = setup[:oi] || "N/A"
|
|
180
|
-
volume = setup[:volume] || "N/A"
|
|
181
|
-
"IV: #{iv}% | OI: #{oi} | Volume: #{volume}"
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def handle_option_chain_result(chain_result)
|
|
185
|
-
if chain_result[:result] && chain_result[:result][:chain]
|
|
186
|
-
chain_summary, underlying_price = build_option_chain_summary(chain_result)
|
|
187
|
-
puts " ✅ Option chain retrieved for expiry: #{chain_result[:result][:expiry]}"
|
|
188
|
-
puts " 📊 Underlying LTP: #{underlying_price}" if underlying_price
|
|
189
|
-
puts " 📊 Chain summary: #{JSON.pretty_generate(chain_summary)}"
|
|
190
|
-
elsif chain_result[:error]
|
|
191
|
-
puts " ⚠️ Could not retrieve option chain data: #{chain_result[:error]}"
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Main execution
|
|
196
|
-
if __FILE__ == $PROGRAM_NAME
|
|
197
|
-
# Configure DhanHQ
|
|
198
|
-
begin
|
|
199
|
-
DhanHQ.configure_with_env
|
|
200
|
-
puts "✅ DhanHQ configured"
|
|
201
|
-
rescue StandardError => e
|
|
202
|
-
puts "⚠️ DhanHQ configuration error: #{e.message}"
|
|
203
|
-
puts " Make sure CLIENT_ID and ACCESS_TOKEN are set in ENV"
|
|
204
|
-
puts " Continuing with mock data for demonstration..."
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
puts "=" * 60
|
|
208
|
-
puts "DhanHQ Agent: Ollama (Reasoning) + DhanHQ (Data & Trading)"
|
|
209
|
-
puts "=" * 60
|
|
210
|
-
puts
|
|
211
|
-
|
|
212
|
-
# Initialize agent
|
|
213
|
-
agent = DhanHQ::Agent.new
|
|
214
|
-
|
|
215
|
-
# ============================================================
|
|
216
|
-
# DATA AGENT EXAMPLES
|
|
217
|
-
# ============================================================
|
|
218
|
-
puts "─" * 60
|
|
219
|
-
puts "DATA AGENT: Market Data Retrieval"
|
|
220
|
-
puts "─" * 60
|
|
221
|
-
puts
|
|
222
|
-
|
|
223
|
-
# Example 1: Analyze market and decide data action (using real data)
|
|
224
|
-
puts "Example 1: Market Analysis & Data Decision (Real Data)"
|
|
225
|
-
puts "─" * 60
|
|
226
|
-
|
|
227
|
-
# Fetch real market data first
|
|
228
|
-
puts "📊 Fetching real market data from DhanHQ..."
|
|
229
|
-
|
|
230
|
-
market_data = {}
|
|
231
|
-
begin
|
|
232
|
-
nifty_result = DhanHQDataTools.get_live_ltp(symbol: "NIFTY", exchange_segment: "IDX_I")
|
|
233
|
-
sleep(1.2)
|
|
234
|
-
if nifty_result.is_a?(Hash) && nifty_result[:result] && !nifty_result[:error]
|
|
235
|
-
market_data[:nifty] = nifty_result[:result]
|
|
236
|
-
ltp = nifty_result[:result][:ltp]
|
|
237
|
-
if ltp && ltp != 0
|
|
238
|
-
puts " ✅ NIFTY: LTP=#{ltp}"
|
|
239
|
-
else
|
|
240
|
-
puts " ⚠️ NIFTY: Data retrieved but LTP is null/empty (may be outside market hours)"
|
|
241
|
-
puts " Result: #{JSON.pretty_generate(nifty_result[:result])}"
|
|
242
|
-
end
|
|
243
|
-
elsif nifty_result && nifty_result[:error]
|
|
244
|
-
puts " ⚠️ NIFTY data error: #{nifty_result[:error]}"
|
|
245
|
-
else
|
|
246
|
-
puts " ⚠️ NIFTY: No data returned"
|
|
247
|
-
end
|
|
248
|
-
rescue StandardError => e
|
|
249
|
-
puts " ⚠️ NIFTY data error: #{e.message}"
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
begin
|
|
253
|
-
reliance_result = DhanHQDataTools.get_live_ltp(symbol: "RELIANCE", exchange_segment: "NSE_EQ")
|
|
254
|
-
sleep(1.2)
|
|
255
|
-
if reliance_result.is_a?(Hash) && reliance_result[:result] && !reliance_result[:error]
|
|
256
|
-
market_data[:reliance] = reliance_result[:result]
|
|
257
|
-
ltp = reliance_result[:result][:ltp]
|
|
258
|
-
if ltp && ltp != 0
|
|
259
|
-
puts " ✅ RELIANCE: LTP=#{ltp}"
|
|
260
|
-
else
|
|
261
|
-
puts " ⚠️ RELIANCE: Data retrieved but LTP is null/empty (may be outside market hours)"
|
|
262
|
-
puts " Result: #{JSON.pretty_generate(reliance_result[:result])}"
|
|
263
|
-
end
|
|
264
|
-
elsif reliance_result && reliance_result[:error]
|
|
265
|
-
puts " ⚠️ RELIANCE data error: #{reliance_result[:error]}"
|
|
266
|
-
else
|
|
267
|
-
puts " ⚠️ RELIANCE: No data returned"
|
|
268
|
-
end
|
|
269
|
-
rescue StandardError => e
|
|
270
|
-
puts " ⚠️ RELIANCE data error: #{e.message}"
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
begin
|
|
274
|
-
positions_result = { action: "check_positions", result: { positions: [], count: 0 },
|
|
275
|
-
note: "Positions API not available in Data Tools" }
|
|
276
|
-
if positions_result[:result]
|
|
277
|
-
market_data[:positions] = positions_result[:result][:positions] || []
|
|
278
|
-
puts " ✅ Positions: #{positions_result[:result][:count] || 0} active"
|
|
279
|
-
else
|
|
280
|
-
puts " ✅ Positions: 0 active (Positions API not in Data Tools)"
|
|
281
|
-
market_data[:positions] = []
|
|
282
|
-
end
|
|
283
|
-
rescue StandardError => e
|
|
284
|
-
puts " ⚠️ Positions error: #{e.message}"
|
|
285
|
-
market_data[:positions] = []
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
puts
|
|
289
|
-
|
|
290
|
-
# Build market context from real data
|
|
291
|
-
market_context = DhanHQ::Builders::MarketContextBuilder.build(market_data)
|
|
292
|
-
|
|
293
|
-
puts "Market Context (from real data):"
|
|
294
|
-
puts market_context
|
|
295
|
-
puts
|
|
296
|
-
|
|
297
|
-
begin
|
|
298
|
-
puts "🤔 Analyzing market with Ollama..."
|
|
299
|
-
decision = agent.data_agent.analyze_and_decide(market_context: market_context)
|
|
300
|
-
|
|
301
|
-
puts "\n📋 Decision:"
|
|
302
|
-
if decision.is_a?(Hash)
|
|
303
|
-
puts " Action: #{decision['action'] || 'N/A'}"
|
|
304
|
-
puts " Reasoning: #{decision['reasoning'] || 'N/A'}"
|
|
305
|
-
if decision["confidence"]
|
|
306
|
-
puts " Confidence: #{(decision['confidence'] * 100).round}%"
|
|
307
|
-
else
|
|
308
|
-
puts " Confidence: N/A"
|
|
309
|
-
end
|
|
310
|
-
puts " Parameters: #{JSON.pretty_generate(decision['parameters'] || {})}"
|
|
311
|
-
else
|
|
312
|
-
puts " ⚠️ Invalid decision returned: #{decision.inspect}"
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
if decision["action"] != "no_action"
|
|
316
|
-
puts "\n⚡ Executing data retrieval..."
|
|
317
|
-
result = agent.data_agent.execute_decision(decision)
|
|
318
|
-
puts " Result: #{JSON.pretty_generate(result)}"
|
|
319
|
-
end
|
|
320
|
-
rescue Ollama::Error => e
|
|
321
|
-
puts "❌ Error: #{e.message}"
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
puts
|
|
325
|
-
puts "─" * 60
|
|
326
|
-
puts "Example 2: All Data APIs Demonstration"
|
|
327
|
-
puts "─" * 60
|
|
328
|
-
puts "Demonstrating all available DhanHQ Data APIs:"
|
|
329
|
-
puts
|
|
330
|
-
|
|
331
|
-
test_symbol = "RELIANCE"
|
|
332
|
-
test_exchange = "NSE_EQ"
|
|
333
|
-
|
|
334
|
-
# 1. Market Quote
|
|
335
|
-
puts "1️⃣ Market Quote API"
|
|
336
|
-
begin
|
|
337
|
-
result = DhanHQDataTools.get_market_quote(symbol: test_symbol, exchange_segment: test_exchange)
|
|
338
|
-
if result[:result]
|
|
339
|
-
puts " ✅ Market Quote retrieved"
|
|
340
|
-
puts " 📊 Quote data: #{JSON.pretty_generate(result[:result][:quote])}"
|
|
341
|
-
else
|
|
342
|
-
puts " ⚠️ #{result[:error]}"
|
|
343
|
-
end
|
|
344
|
-
rescue StandardError => e
|
|
345
|
-
puts " ❌ Error: #{e.message}"
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
puts
|
|
349
|
-
sleep(1.2)
|
|
350
|
-
|
|
351
|
-
# 2. Live Market Feed (LTP)
|
|
352
|
-
puts "2️⃣ Live Market Feed API (LTP)"
|
|
353
|
-
begin
|
|
354
|
-
result = DhanHQDataTools.get_live_ltp(symbol: test_symbol, exchange_segment: test_exchange)
|
|
355
|
-
if result[:result]
|
|
356
|
-
puts " ✅ LTP retrieved"
|
|
357
|
-
puts " 📊 LTP: #{result[:result][:ltp].inspect}"
|
|
358
|
-
else
|
|
359
|
-
puts " ⚠️ #{result[:error]}"
|
|
360
|
-
end
|
|
361
|
-
rescue StandardError => e
|
|
362
|
-
puts " ❌ Error: #{e.message}"
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
puts
|
|
366
|
-
sleep(1.2)
|
|
367
|
-
|
|
368
|
-
# 3. Full Market Depth
|
|
369
|
-
puts "3️⃣ Full Market Depth API"
|
|
370
|
-
begin
|
|
371
|
-
result = DhanHQDataTools.get_market_depth(symbol: test_symbol, exchange_segment: test_exchange)
|
|
372
|
-
if result[:result]
|
|
373
|
-
puts " ✅ Market Depth retrieved"
|
|
374
|
-
puts " 📊 Buy depth: #{result[:result][:buy_depth]&.length || 0} levels"
|
|
375
|
-
puts " 📊 Sell depth: #{result[:result][:sell_depth]&.length || 0} levels"
|
|
376
|
-
puts " 📊 LTP: #{result[:result][:ltp]}"
|
|
377
|
-
puts " 📊 Volume: #{result[:result][:volume]}"
|
|
378
|
-
else
|
|
379
|
-
puts " ⚠️ #{result[:error]}"
|
|
380
|
-
end
|
|
381
|
-
rescue StandardError => e
|
|
382
|
-
puts " ❌ Error: #{e.message}"
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
puts
|
|
386
|
-
sleep(1.2)
|
|
387
|
-
|
|
388
|
-
# 4. Historical Data
|
|
389
|
-
puts "4️⃣ Historical Data API"
|
|
390
|
-
begin
|
|
391
|
-
to_date = Date.today.strftime("%Y-%m-%d")
|
|
392
|
-
from_date = (Date.today - 30).strftime("%Y-%m-%d")
|
|
393
|
-
result = DhanHQDataTools.get_historical_data(
|
|
394
|
-
symbol: test_symbol,
|
|
395
|
-
exchange_segment: test_exchange,
|
|
396
|
-
from_date: from_date,
|
|
397
|
-
to_date: to_date
|
|
398
|
-
)
|
|
399
|
-
if result[:result]
|
|
400
|
-
puts " ✅ Historical data retrieved"
|
|
401
|
-
puts " 📊 Type: #{result[:type]}"
|
|
402
|
-
puts " 📊 Records: #{result[:result][:count]}"
|
|
403
|
-
if result[:result][:count].zero?
|
|
404
|
-
puts " ⚠️ No data found for date range #{from_date} to #{to_date}"
|
|
405
|
-
puts " (This may be normal if market was closed or data unavailable)"
|
|
406
|
-
end
|
|
407
|
-
else
|
|
408
|
-
puts " ⚠️ #{result[:error]}"
|
|
409
|
-
end
|
|
410
|
-
rescue StandardError => e
|
|
411
|
-
puts " ❌ Error: #{e.message}"
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
puts
|
|
415
|
-
sleep(0.5)
|
|
416
|
-
|
|
417
|
-
# 5. Expired Options Data
|
|
418
|
-
puts "5️⃣ Expired Options Data API"
|
|
419
|
-
begin
|
|
420
|
-
result = DhanHQDataTools.get_expired_options_data(
|
|
421
|
-
security_id: "13",
|
|
422
|
-
exchange_segment: "NSE_FNO",
|
|
423
|
-
expiry_date: (Date.today - 7).strftime("%Y-%m-%d"),
|
|
424
|
-
instrument: "OPTIDX",
|
|
425
|
-
expiry_flag: "MONTH",
|
|
426
|
-
expiry_code: 1,
|
|
427
|
-
strike: "ATM",
|
|
428
|
-
drv_option_type: "CALL",
|
|
429
|
-
interval: "1"
|
|
430
|
-
)
|
|
431
|
-
if result[:result]
|
|
432
|
-
puts " ✅ Expired options data retrieved"
|
|
433
|
-
puts " 📊 Expiry: #{result[:result][:expiry_date]}"
|
|
434
|
-
if result[:result][:summary_stats]
|
|
435
|
-
stats = result[:result][:summary_stats]
|
|
436
|
-
concise_summary = build_expired_options_summary(stats)
|
|
437
|
-
puts " 📊 Data summary: #{JSON.pretty_generate(concise_summary)}"
|
|
438
|
-
else
|
|
439
|
-
puts " 📊 Data available but summary stats not found"
|
|
440
|
-
end
|
|
441
|
-
else
|
|
442
|
-
puts " ⚠️ #{result[:error]}"
|
|
443
|
-
puts " (Note: Options may require specific symbol format or may not exist for this instrument)"
|
|
444
|
-
end
|
|
445
|
-
rescue StandardError => e
|
|
446
|
-
puts " ❌ Error: #{e.message}"
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
puts
|
|
450
|
-
sleep(0.5)
|
|
451
|
-
|
|
452
|
-
# 6. Option Chain
|
|
453
|
-
puts "6️⃣ Option Chain API"
|
|
454
|
-
begin
|
|
455
|
-
expiry_list_result = DhanHQDataTools.get_option_chain(
|
|
456
|
-
symbol: "NIFTY",
|
|
457
|
-
exchange_segment: "IDX_I"
|
|
458
|
-
)
|
|
459
|
-
if expiry_list_result[:result] && expiry_list_result[:result][:expiries]
|
|
460
|
-
expiries = expiry_list_result[:result][:expiries]
|
|
461
|
-
puts " ✅ Available expiries: #{expiry_list_result[:result][:count]}"
|
|
462
|
-
puts " 📊 First few expiries: #{expiries.first(3).inspect}" if expiries.is_a?(Array) && !expiries.empty?
|
|
463
|
-
|
|
464
|
-
next_expiry = expiries.is_a?(Array) && !expiries.empty? ? expiries.first : nil
|
|
465
|
-
if next_expiry
|
|
466
|
-
puts " 📊 Fetching option chain for next expiry: #{next_expiry}"
|
|
467
|
-
chain_result = DhanHQDataTools.get_option_chain(
|
|
468
|
-
symbol: "NIFTY",
|
|
469
|
-
exchange_segment: "IDX_I",
|
|
470
|
-
expiry: next_expiry
|
|
471
|
-
)
|
|
472
|
-
handle_option_chain_result(chain_result)
|
|
473
|
-
end
|
|
474
|
-
elsif expiry_list_result[:error]
|
|
475
|
-
puts " ⚠️ #{expiry_list_result[:error]}"
|
|
476
|
-
puts " (Note: Options may require specific symbol format or may not exist for this instrument)"
|
|
477
|
-
end
|
|
478
|
-
rescue StandardError => e
|
|
479
|
-
puts " ❌ Error: #{e.message}"
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
puts
|
|
483
|
-
puts "=" * 60
|
|
484
|
-
puts "TRADING AGENT: Order Parameter Building"
|
|
485
|
-
puts "=" * 60
|
|
486
|
-
puts
|
|
487
|
-
|
|
488
|
-
# ============================================================
|
|
489
|
-
# TRADING AGENT EXAMPLES
|
|
490
|
-
# ============================================================
|
|
491
|
-
puts "Example 1: Simple Buy Order"
|
|
492
|
-
puts "─" * 60
|
|
493
|
-
|
|
494
|
-
market_context = <<~CONTEXT
|
|
495
|
-
RELIANCE is showing strong momentum.
|
|
496
|
-
Current LTP: 2850
|
|
497
|
-
Entry price: 2850
|
|
498
|
-
Quantity: 100 shares
|
|
499
|
-
Use regular order. security_id="1333", exchange_segment="NSE_EQ"
|
|
500
|
-
CONTEXT
|
|
501
|
-
|
|
502
|
-
puts "Market Context:"
|
|
503
|
-
puts market_context
|
|
504
|
-
puts
|
|
505
|
-
|
|
506
|
-
begin
|
|
507
|
-
puts "🤔 Analyzing with Ollama..."
|
|
508
|
-
decision = agent.trading_agent.analyze_and_decide(market_context: market_context)
|
|
509
|
-
|
|
510
|
-
puts "\n📋 Decision:"
|
|
511
|
-
if decision.is_a?(Hash)
|
|
512
|
-
puts " Action: #{decision['action'] || 'N/A'}"
|
|
513
|
-
puts " Reasoning: #{decision['reasoning'] || 'N/A'}"
|
|
514
|
-
puts " Confidence: #{(decision['confidence'] * 100).round}%" if decision["confidence"]
|
|
515
|
-
puts " Parameters: #{JSON.pretty_generate(decision['parameters'] || {})}"
|
|
516
|
-
end
|
|
517
|
-
|
|
518
|
-
if decision["action"] != "no_action"
|
|
519
|
-
puts "\n⚡ Building order parameters (order not placed)..."
|
|
520
|
-
result = agent.trading_agent.execute_decision(decision)
|
|
521
|
-
puts " Result: #{JSON.pretty_generate(result)}"
|
|
522
|
-
if result.is_a?(Hash) && result[:order_params]
|
|
523
|
-
puts "\n 📝 Order Parameters Ready:"
|
|
524
|
-
puts " #{JSON.pretty_generate(result[:order_params])}"
|
|
525
|
-
puts " 💡 To place order: DhanHQ::Models::Order.new(result[:order_params]).save"
|
|
526
|
-
end
|
|
527
|
-
end
|
|
528
|
-
rescue Ollama::TimeoutError => e
|
|
529
|
-
puts "⏱️ Timeout: #{e.message}"
|
|
530
|
-
rescue Ollama::Error => e
|
|
531
|
-
puts "❌ Error: #{e.message}"
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
puts
|
|
535
|
-
puts "=" * 60
|
|
536
|
-
puts "TECHNICAL ANALYSIS EXAMPLES"
|
|
537
|
-
puts "=" * 60
|
|
538
|
-
puts
|
|
539
|
-
|
|
540
|
-
# ============================================================
|
|
541
|
-
# TECHNICAL ANALYSIS EXAMPLES
|
|
542
|
-
# ============================================================
|
|
543
|
-
puts "Example 1: Technical Analysis for RELIANCE"
|
|
544
|
-
puts "─" * 60
|
|
545
|
-
|
|
546
|
-
begin
|
|
547
|
-
analysis_result = agent.analysis_agent.analyze_symbol(
|
|
548
|
-
symbol: "RELIANCE",
|
|
549
|
-
exchange_segment: "NSE_EQ"
|
|
550
|
-
)
|
|
551
|
-
|
|
552
|
-
if analysis_result[:error]
|
|
553
|
-
puts " ⚠️ Error: #{analysis_result[:error]}"
|
|
554
|
-
elsif analysis_result[:analysis].nil? || analysis_result[:analysis].empty?
|
|
555
|
-
puts " ⚠️ Error: Analysis returned empty result"
|
|
556
|
-
else
|
|
557
|
-
analysis = analysis_result[:analysis]
|
|
558
|
-
puts " ✅ Analysis Complete"
|
|
559
|
-
puts " 📊 Trend: #{analysis[:trend]&.dig(:trend) || 'N/A'} (#{analysis[:trend]&.dig(:strength) || 0}% strength)"
|
|
560
|
-
puts " 📊 RSI: #{analysis[:indicators]&.dig(:rsi)&.round(2) || 'N/A'}"
|
|
561
|
-
puts " 📊 MACD: #{analysis[:indicators]&.dig(:macd)&.round(2) || 'N/A'}"
|
|
562
|
-
puts " 📊 Current Price: #{analysis[:current_price] || 'N/A'}"
|
|
563
|
-
puts " 📊 Patterns Detected: #{analysis[:patterns]&.dig(:candlestick)&.length || 0} candlestick patterns"
|
|
564
|
-
puts " 📊 Structure Break: #{analysis[:structure_break]&.dig(:broken) ? 'Yes' : 'No'}"
|
|
565
|
-
|
|
566
|
-
# Generate swing trading recommendation
|
|
567
|
-
begin
|
|
568
|
-
recommendation = agent.analysis_agent.generate_recommendation(
|
|
569
|
-
analysis_result,
|
|
570
|
-
trading_style: :swing
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
if recommendation && !recommendation[:error] && recommendation.is_a?(Hash)
|
|
574
|
-
puts "\n 💡 Swing Trading Recommendation:"
|
|
575
|
-
puts " Action: #{recommendation['recommendation']&.upcase || 'N/A'}"
|
|
576
|
-
puts " Entry: #{recommendation['entry_price'] || 'N/A'}"
|
|
577
|
-
puts " Stop Loss: #{recommendation['stop_loss'] || 'N/A'}"
|
|
578
|
-
puts " Target: #{recommendation['target_price'] || 'N/A'}"
|
|
579
|
-
puts " Risk/Reward: #{recommendation['risk_reward_ratio']&.round(2) || 'N/A'}"
|
|
580
|
-
puts " Confidence: #{(recommendation['confidence'] * 100).round}%" if recommendation["confidence"]
|
|
581
|
-
end
|
|
582
|
-
rescue StandardError => e
|
|
583
|
-
puts " ⚠️ Could not generate recommendation: #{e.message}"
|
|
584
|
-
end
|
|
585
|
-
end
|
|
586
|
-
rescue StandardError => e
|
|
587
|
-
puts " ❌ Error: #{e.message}"
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
puts
|
|
591
|
-
puts "Example 2: Swing Trading Scanner"
|
|
592
|
-
puts "─" * 60
|
|
593
|
-
|
|
594
|
-
begin
|
|
595
|
-
# Scan a few symbols for swing opportunities
|
|
596
|
-
symbols_to_scan = ["RELIANCE", "TCS", "INFY"]
|
|
597
|
-
puts " 🔍 Scanning #{symbols_to_scan.length} symbols for swing opportunities..."
|
|
598
|
-
|
|
599
|
-
candidates = agent.swing_scanner.scan_symbols(
|
|
600
|
-
symbols_to_scan,
|
|
601
|
-
exchange_segment: "NSE_EQ",
|
|
602
|
-
min_score: 40,
|
|
603
|
-
verbose: true
|
|
604
|
-
)
|
|
605
|
-
|
|
606
|
-
if candidates.empty?
|
|
607
|
-
puts " ⚠️ No swing candidates found above minimum score (40/100)"
|
|
608
|
-
puts " Try lowering min_score or check rejected candidates above"
|
|
609
|
-
else
|
|
610
|
-
puts " ✅ Found #{candidates.length} swing candidates:"
|
|
611
|
-
candidates.each do |candidate|
|
|
612
|
-
puts " 📈 #{candidate[:symbol]}: Score #{candidate[:score]}/100"
|
|
613
|
-
if candidate[:score_details]
|
|
614
|
-
details = candidate[:score_details]
|
|
615
|
-
puts " Breakdown: #{format_score_breakdown(details)}"
|
|
616
|
-
end
|
|
617
|
-
trend = candidate[:analysis][:trend]
|
|
618
|
-
puts " Trend: #{trend[:trend]} (#{trend[:strength]}% strength)"
|
|
619
|
-
puts " #{candidate[:interpretation]}"
|
|
620
|
-
end
|
|
621
|
-
end
|
|
622
|
-
rescue StandardError => e
|
|
623
|
-
puts " ❌ Error: #{e.message}"
|
|
624
|
-
puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
puts
|
|
628
|
-
puts "Example 3: Intraday Options Scanner"
|
|
629
|
-
puts "─" * 60
|
|
630
|
-
|
|
631
|
-
begin
|
|
632
|
-
puts " 🔍 Scanning NIFTY for intraday options opportunities..."
|
|
633
|
-
|
|
634
|
-
options_setups = agent.options_scanner.scan_for_options_setups(
|
|
635
|
-
"NIFTY",
|
|
636
|
-
exchange_segment: "IDX_I",
|
|
637
|
-
min_score: 40,
|
|
638
|
-
verbose: true
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
if options_setups[:error]
|
|
642
|
-
puts " ⚠️ #{options_setups[:error]}"
|
|
643
|
-
elsif options_setups[:setups] && !options_setups[:setups].empty?
|
|
644
|
-
puts " ✅ Found #{options_setups[:setups].length} options setups:"
|
|
645
|
-
options_setups[:setups].each do |setup|
|
|
646
|
-
puts " 📊 #{setup[:type].to_s.upcase} @ #{setup[:strike]}"
|
|
647
|
-
puts " #{format_option_setup_details(setup)}"
|
|
648
|
-
puts " Score: #{setup[:score]}/100 | Recommendation: #{setup[:recommendation]}"
|
|
649
|
-
end
|
|
650
|
-
else
|
|
651
|
-
puts " ⚠️ No options setups found above minimum score (40/100)"
|
|
652
|
-
puts " Check rejected setups above or try lowering min_score"
|
|
653
|
-
end
|
|
654
|
-
rescue StandardError => e
|
|
655
|
-
puts " ❌ Error: #{e.message}"
|
|
656
|
-
puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
|
|
657
|
-
end
|
|
658
|
-
|
|
659
|
-
puts
|
|
660
|
-
puts "=" * 60
|
|
661
|
-
puts "TOOL CALLING EXAMPLE (Using Executor + Structured Tool Classes)"
|
|
662
|
-
puts "=" * 60
|
|
663
|
-
puts
|
|
664
|
-
|
|
665
|
-
# ============================================================
|
|
666
|
-
# TOOL CALLING WITH EXECUTOR
|
|
667
|
-
# ============================================================
|
|
668
|
-
puts "Example: Agentic Tool Calling with DhanHQ Tools"
|
|
669
|
-
puts "─" * 60
|
|
670
|
-
puts "This demonstrates the new tool calling pattern using:"
|
|
671
|
-
puts " - Structured Tool classes (type-safe schemas)"
|
|
672
|
-
puts " - Executor (automatic tool execution loop)"
|
|
673
|
-
puts " - chat_raw() internally (via Executor)"
|
|
674
|
-
puts
|
|
675
|
-
|
|
676
|
-
# Define DhanHQ tools using structured Tool classes
|
|
677
|
-
market_quote_tool = Ollama::Tool.new(
|
|
678
|
-
type: "function",
|
|
679
|
-
function: Ollama::Tool::Function.new(
|
|
680
|
-
name: "get_market_quote",
|
|
681
|
-
description: "Get market quote for a symbol. Returns OHLC, depth, volume, and other market data. " \
|
|
682
|
-
"Finds instrument automatically using exchange_segment and symbol.",
|
|
683
|
-
parameters: Ollama::Tool::Function::Parameters.new(
|
|
684
|
-
type: "object",
|
|
685
|
-
properties: {
|
|
686
|
-
symbol: Ollama::Tool::Function::Parameters::Property.new(
|
|
687
|
-
type: "string",
|
|
688
|
-
description: "Stock or index symbol (e.g., RELIANCE, NIFTY)"
|
|
689
|
-
),
|
|
690
|
-
exchange_segment: Ollama::Tool::Function::Parameters::Property.new(
|
|
691
|
-
type: "string",
|
|
692
|
-
description: "Exchange segment",
|
|
693
|
-
enum: %w[NSE_EQ NSE_FNO BSE_EQ BSE_FNO IDX_I]
|
|
694
|
-
)
|
|
695
|
-
},
|
|
696
|
-
required: %w[symbol exchange_segment]
|
|
697
|
-
)
|
|
698
|
-
)
|
|
699
|
-
)
|
|
700
|
-
|
|
701
|
-
live_ltp_tool = Ollama::Tool.new(
|
|
702
|
-
type: "function",
|
|
703
|
-
function: Ollama::Tool::Function.new(
|
|
704
|
-
name: "get_live_ltp",
|
|
705
|
-
description: "Get live last traded price (LTP) for a symbol. Fast API for current price. " \
|
|
706
|
-
"Finds instrument automatically using exchange_segment and symbol.",
|
|
707
|
-
parameters: Ollama::Tool::Function::Parameters.new(
|
|
708
|
-
type: "object",
|
|
709
|
-
properties: {
|
|
710
|
-
symbol: Ollama::Tool::Function::Parameters::Property.new(
|
|
711
|
-
type: "string",
|
|
712
|
-
description: "Stock or index symbol"
|
|
713
|
-
),
|
|
714
|
-
exchange_segment: Ollama::Tool::Function::Parameters::Property.new(
|
|
715
|
-
type: "string",
|
|
716
|
-
description: "Exchange segment",
|
|
717
|
-
enum: %w[NSE_EQ NSE_FNO BSE_EQ BSE_FNO IDX_I]
|
|
718
|
-
)
|
|
719
|
-
},
|
|
720
|
-
required: %w[symbol exchange_segment]
|
|
721
|
-
)
|
|
722
|
-
)
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
# Define tools with structured Tool classes and callables
|
|
726
|
-
tools = {
|
|
727
|
-
"get_market_quote" => {
|
|
728
|
-
tool: market_quote_tool,
|
|
729
|
-
callable: lambda do |symbol:, exchange_segment:|
|
|
730
|
-
result = DhanHQDataTools.get_market_quote(
|
|
731
|
-
symbol: symbol.to_s,
|
|
732
|
-
exchange_segment: exchange_segment.to_s
|
|
733
|
-
)
|
|
734
|
-
|
|
735
|
-
if result[:error]
|
|
736
|
-
{ error: result[:error] }
|
|
737
|
-
else
|
|
738
|
-
quote = result[:result][:quote]
|
|
739
|
-
{
|
|
740
|
-
symbol: symbol,
|
|
741
|
-
exchange_segment: exchange_segment,
|
|
742
|
-
last_price: quote[:last_price],
|
|
743
|
-
volume: quote[:volume],
|
|
744
|
-
ohlc: quote[:ohlc],
|
|
745
|
-
change_percent: quote[:net_change]
|
|
746
|
-
}
|
|
747
|
-
end
|
|
748
|
-
rescue StandardError => e
|
|
749
|
-
{ error: e.message }
|
|
750
|
-
end
|
|
751
|
-
},
|
|
752
|
-
|
|
753
|
-
"get_live_ltp" => {
|
|
754
|
-
tool: live_ltp_tool,
|
|
755
|
-
callable: lambda do |symbol:, exchange_segment:|
|
|
756
|
-
result = DhanHQDataTools.get_live_ltp(
|
|
757
|
-
symbol: symbol.to_s,
|
|
758
|
-
exchange_segment: exchange_segment.to_s
|
|
759
|
-
)
|
|
760
|
-
|
|
761
|
-
if result[:error]
|
|
762
|
-
{ error: result[:error] }
|
|
763
|
-
else
|
|
764
|
-
{
|
|
765
|
-
symbol: symbol,
|
|
766
|
-
exchange_segment: exchange_segment,
|
|
767
|
-
ltp: result[:result][:ltp],
|
|
768
|
-
timestamp: result[:result][:timestamp]
|
|
769
|
-
}
|
|
770
|
-
end
|
|
771
|
-
rescue StandardError => e
|
|
772
|
-
{ error: e.message }
|
|
773
|
-
end
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
# Create executor with tools
|
|
778
|
-
# Create client with same configuration as other examples
|
|
779
|
-
executor_config = Ollama::Config.new
|
|
780
|
-
executor_config.model = ENV.fetch("OLLAMA_MODEL", "llama3.1:8b")
|
|
781
|
-
executor_config.temperature = 0.2
|
|
782
|
-
executor_config.timeout = 60
|
|
783
|
-
executor_client = Ollama::Client.new(config: executor_config)
|
|
784
|
-
|
|
785
|
-
executor = Ollama::Agent::Executor.new(
|
|
786
|
-
executor_client,
|
|
787
|
-
tools: tools,
|
|
788
|
-
max_steps: 10
|
|
789
|
-
)
|
|
790
|
-
|
|
791
|
-
begin
|
|
792
|
-
puts "🔄 Starting agentic tool-calling loop..."
|
|
793
|
-
puts "User query: Get market quote for RELIANCE and also check NIFTY's current price"
|
|
794
|
-
puts
|
|
795
|
-
|
|
796
|
-
result = executor.run(
|
|
797
|
-
system: "You are a market data assistant. Use the available tools to get market data. " \
|
|
798
|
-
"You can call multiple tools in sequence. When you have the data, summarize it for the user.",
|
|
799
|
-
user: "Get market quote for RELIANCE stock and also check NIFTY's current price"
|
|
800
|
-
)
|
|
801
|
-
|
|
802
|
-
puts
|
|
803
|
-
puts "=" * 60
|
|
804
|
-
puts "Tool Calling Result:"
|
|
805
|
-
puts "=" * 60
|
|
806
|
-
puts result
|
|
807
|
-
puts
|
|
808
|
-
rescue Ollama::Error => e
|
|
809
|
-
puts "❌ Error: #{e.message}"
|
|
810
|
-
puts e.backtrace.first(5).join("\n") if e.backtrace
|
|
811
|
-
rescue StandardError => e
|
|
812
|
-
puts "❌ Unexpected error: #{e.message}"
|
|
813
|
-
puts e.backtrace.first(3).join("\n") if e.backtrace
|
|
814
|
-
end
|
|
815
|
-
|
|
816
|
-
puts
|
|
817
|
-
puts "=" * 60
|
|
818
|
-
puts "DhanHQ Agent Summary:"
|
|
819
|
-
puts " ✅ Ollama: Reasoning & Decision Making"
|
|
820
|
-
puts " ✅ DhanHQ: Data Retrieval & Order Building"
|
|
821
|
-
puts " ✅ Data APIs: Market Quote, Live Market Feed, Full Market Depth, " \
|
|
822
|
-
"Historical Data, Expired Options Data, Option Chain"
|
|
823
|
-
puts " ✅ Trading Tools: Order parameters, Super order parameters, Cancel parameters"
|
|
824
|
-
puts " ✅ Technical Analysis: Trend analysis, SMC concepts, Pattern recognition, Indicators (RSI, MACD, MA, etc.)"
|
|
825
|
-
puts " ✅ Scanners: Swing trading scanner, Intraday options scanner"
|
|
826
|
-
puts " ✅ Analysis Agents: Technical analysis agent with LLM interpretation"
|
|
827
|
-
puts " ✅ Tool Calling: Executor with structured Tool classes (NEW!)"
|
|
828
|
-
puts "=" * 60
|
|
829
|
-
end
|