DhanHQ 3.1.0 → 3.2.0
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/ARCHITECTURE.md +47 -1
- data/CHANGELOG.md +47 -0
- data/README.md +241 -3
- data/docs/AUTHENTICATION.md +3 -5
- data/docs/CONFIGURATION.md +3 -2
- data/docs/CONSTANTS_REFERENCE.md +5 -4
- data/docs/ENDPOINTS_AND_SANDBOX.md +1 -0
- data/docs/LIVE_ORDER_UPDATES.md +5 -10
- data/docs/RAILS_INTEGRATION.md +1 -1
- data/docs/RELEASE_GUIDE.md +13 -2
- data/docs/STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md +2 -2
- data/docs/WEBSOCKET_INTEGRATION.md +13 -20
- data/docs/WEBSOCKET_PROTOCOL.md +7 -3
- data/lib/DhanHQ/agent/key_coercion.rb +36 -0
- data/lib/DhanHQ/agent/tool.rb +37 -0
- data/lib/DhanHQ/agent/tool_catalogue.rb +180 -0
- data/lib/DhanHQ/agent/tool_handlers.rb +116 -0
- data/lib/DhanHQ/agent/tool_registry.rb +17 -261
- data/lib/DhanHQ/agent/tool_schemas.rb +160 -0
- data/lib/DhanHQ/client.rb +58 -2
- data/lib/DhanHQ/concerns/order_audit.rb +35 -4
- data/lib/DhanHQ/configuration.rb +70 -1
- data/lib/DhanHQ/constants.rb +101 -0
- data/lib/DhanHQ/contracts/expired_options_data_contract.rb +1 -9
- data/lib/DhanHQ/contracts/global_stocks_estimator_contract.rb +28 -0
- data/lib/DhanHQ/contracts/global_stocks_modify_order_contract.rb +26 -0
- data/lib/DhanHQ/contracts/global_stocks_order_contract.rb +33 -0
- data/lib/DhanHQ/contracts/global_stocks_place_order_contract.rb +75 -0
- data/lib/DhanHQ/contracts/historical_data_contract.rb +1 -21
- data/lib/DhanHQ/contracts/multi_order_contract.rb +74 -0
- data/lib/DhanHQ/contracts/trade_history_contract.rb +1 -8
- data/lib/DhanHQ/dry_run/ledger.rb +71 -0
- data/lib/DhanHQ/dry_run/simulator.rb +140 -0
- data/lib/DhanHQ/helpers/attribute_helper.rb +23 -0
- data/lib/DhanHQ/models/alert_order.rb +5 -2
- data/lib/DhanHQ/models/global_stocks/funds.rb +56 -0
- data/lib/DhanHQ/models/global_stocks/holding.rb +101 -0
- data/lib/DhanHQ/models/global_stocks/margin.rb +54 -0
- data/lib/DhanHQ/models/global_stocks/market_status.rb +63 -0
- data/lib/DhanHQ/models/global_stocks/order.rb +189 -0
- data/lib/DhanHQ/models/global_stocks/order_estimate.rb +61 -0
- data/lib/DhanHQ/models/global_stocks/trade.rb +74 -0
- data/lib/DhanHQ/models/margin.rb +5 -1
- data/lib/DhanHQ/models/multi_order.rb +130 -0
- data/lib/DhanHQ/resources/global_stocks/funds.rb +25 -0
- data/lib/DhanHQ/resources/global_stocks/holdings.rb +22 -0
- data/lib/DhanHQ/resources/global_stocks/margin_calculator.rb +70 -0
- data/lib/DhanHQ/resources/global_stocks/market_status.rb +22 -0
- data/lib/DhanHQ/resources/global_stocks/orders.rb +112 -0
- data/lib/DhanHQ/resources/global_stocks/trades.rb +31 -0
- data/lib/DhanHQ/resources/multi_orders.rb +58 -0
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/write_paths.rb +57 -0
- data/lib/DhanHQ/ws/client.rb +117 -2
- data/lib/DhanHQ/ws/connection.rb +40 -11
- data/lib/DhanHQ/ws/sub_state.rb +14 -0
- data/lib/dhanhq/analysis/options_buying_advisor.rb +11 -10
- metadata +30 -40
- data/.rspec +0 -3
- data/.rubocop.yml +0 -50
- data/.rubocop_todo.yml +0 -217
- data/AGENTS.md +0 -23
- data/CODE_OF_CONDUCT.md +0 -132
- data/Rakefile +0 -14
- data/TAGS +0 -10
- data/core +0 -0
- data/diagram.html +0 -184
- data/skills/dhanhq-ruby/SKILL.md +0 -207
- data/skills/dhanhq-ruby/examples/fetch_option_chain.rb +0 -54
- data/skills/dhanhq-ruby/examples/gtt_forever_order.rb +0 -65
- data/skills/dhanhq-ruby/examples/historical_data_analysis.rb +0 -89
- data/skills/dhanhq-ruby/examples/iron_condor.rb +0 -137
- data/skills/dhanhq-ruby/examples/live_feed_setup.rb +0 -43
- data/skills/dhanhq-ruby/examples/margin_check.rb +0 -42
- data/skills/dhanhq-ruby/examples/order_management.rb +0 -105
- data/skills/dhanhq-ruby/examples/place_equity_order.rb +0 -36
- data/skills/dhanhq-ruby/examples/place_fno_order.rb +0 -76
- data/skills/dhanhq-ruby/examples/portfolio_summary.rb +0 -74
- data/skills/dhanhq-ruby/examples/super_order_with_sl.rb +0 -57
- data/skills/dhanhq-ruby/references/backtesting-with-dhan.md +0 -65
- data/skills/dhanhq-ruby/references/common-workflows.md +0 -76
- data/skills/dhanhq-ruby/references/error-codes.md +0 -50
- data/skills/dhanhq-ruby/references/funds.md +0 -67
- data/skills/dhanhq-ruby/references/instruments.md +0 -85
- data/skills/dhanhq-ruby/references/live-feed.md +0 -83
- data/skills/dhanhq-ruby/references/market-data.md +0 -119
- data/skills/dhanhq-ruby/references/option-chain.md +0 -71
- data/skills/dhanhq-ruby/references/options-analysis-patterns.md +0 -76
- data/skills/dhanhq-ruby/references/orders.md +0 -201
- data/skills/dhanhq-ruby/references/portfolio.md +0 -93
- data/skills/dhanhq-ruby/references/scanx-data.md +0 -62
- data/skills/dhanhq-ruby/scripts/dhan_helpers.rb +0 -323
- data/skills/dhanhq-ruby/scripts/resolve_security.rb +0 -168
- data/skills/dhanhq-ruby/scripts/trade_logger.rb +0 -131
- data/skills/dhanhq-ruby/scripts/validate_order.rb +0 -169
- data/watchlist.csv +0 -3
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
-
require "dhan_hq"
|
|
5
|
-
require_relative "../scripts/dhan_helpers"
|
|
6
|
-
|
|
7
|
-
# Initialize credentials
|
|
8
|
-
get_client
|
|
9
|
-
|
|
10
|
-
security_id = "2885"
|
|
11
|
-
price = 2000.0
|
|
12
|
-
quantity = 1
|
|
13
|
-
|
|
14
|
-
puts preview_order(
|
|
15
|
-
security_id: security_id,
|
|
16
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
|
|
17
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
18
|
-
quantity: quantity,
|
|
19
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
20
|
-
product_type: DhanHQ::Constants::ProductType::CNC,
|
|
21
|
-
price: price,
|
|
22
|
-
trading_symbol: "RELIANCE"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
if ENV["RUN_LIVE_EXAMPLE"] != "1"
|
|
26
|
-
puts "Set RUN_LIVE_EXAMPLE=1 to place, modify, and cancel a live demo order."
|
|
27
|
-
exit 0
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Step 1: Place a limit order (well below market for demo — won't fill)
|
|
31
|
-
puts "Step 1: Placing limit buy order for RELIANCE..."
|
|
32
|
-
order = DhanHQ::Models::Order.place(
|
|
33
|
-
security_id: security_id,
|
|
34
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
|
|
35
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
36
|
-
quantity: quantity,
|
|
37
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
38
|
-
product_type: DhanHQ::Constants::ProductType::CNC,
|
|
39
|
-
price: price, # Below market — will stay pending
|
|
40
|
-
validity: DhanHQ::Constants::Validity::DAY
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
if order.nil? || order.order_id.to_s.empty?
|
|
44
|
-
puts "Order failed to place."
|
|
45
|
-
exit 1
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
order_id = order.order_id
|
|
49
|
-
puts "Order placed: #{order_id}"
|
|
50
|
-
|
|
51
|
-
# Step 2: Check order status
|
|
52
|
-
puts "\nStep 2: Checking order status..."
|
|
53
|
-
sleep(1)
|
|
54
|
-
order = DhanHQ::Models::Order.find(order_id)
|
|
55
|
-
status = order.order_status
|
|
56
|
-
puts "Status: #{status}"
|
|
57
|
-
puts " Security: #{order.trading_symbol}"
|
|
58
|
-
puts " Qty: #{order.quantity}"
|
|
59
|
-
puts " Price: ₹#{order.price}"
|
|
60
|
-
puts " Filled: #{order.filled_qty || 0}"
|
|
61
|
-
|
|
62
|
-
# Step 3: Modify the order (change price)
|
|
63
|
-
if status == DhanHQ::Constants::OrderStatus::PENDING
|
|
64
|
-
puts "\nStep 3: Modifying order price to ₹2050..."
|
|
65
|
-
modified_order = order.modify(
|
|
66
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
67
|
-
quantity: quantity,
|
|
68
|
-
price: 2050.00,
|
|
69
|
-
validity: DhanHQ::Constants::Validity::DAY
|
|
70
|
-
)
|
|
71
|
-
puts "Modify result: #{modified_order ? "Success" : "Failure"}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Step 4: Cancel the order
|
|
75
|
-
puts "\nStep 4: Cancelling order..."
|
|
76
|
-
cancel_success = order.cancel
|
|
77
|
-
puts "Cancel result: #{cancel_success ? "Success" : "Failure"}"
|
|
78
|
-
|
|
79
|
-
# Step 5: View order book
|
|
80
|
-
puts "\nStep 5: Today's order book:"
|
|
81
|
-
orders = begin
|
|
82
|
-
DhanHQ::Models::Order.all
|
|
83
|
-
rescue StandardError
|
|
84
|
-
[]
|
|
85
|
-
end
|
|
86
|
-
if orders.any?
|
|
87
|
-
orders.last(5).each do |o| # Last 5 orders
|
|
88
|
-
printf(" %-12s | %-12s | %-4s | %-12s | ₹%-8.2f\n", o.order_id.to_s[0...12], o.trading_symbol, o.transaction_type, o.order_status, o.price.to_f)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Step 6: View trade book
|
|
93
|
-
puts "\nStep 6: Today's trade book:"
|
|
94
|
-
trades = begin
|
|
95
|
-
DhanHQ::Models::Trade.today
|
|
96
|
-
rescue StandardError
|
|
97
|
-
[]
|
|
98
|
-
end
|
|
99
|
-
if trades.any?
|
|
100
|
-
trades.last(5).each do |t|
|
|
101
|
-
printf(" %-12s | %-4s | Qty: %-5d | ₹%-8.2f\n", t.trading_symbol, t.transaction_type, t.traded_quantity, t.traded_price.to_f)
|
|
102
|
-
end
|
|
103
|
-
else
|
|
104
|
-
puts " No trades today"
|
|
105
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
-
require "dhan_hq"
|
|
5
|
-
require_relative "../scripts/dhan_helpers"
|
|
6
|
-
|
|
7
|
-
# Initialize credentials
|
|
8
|
-
get_client
|
|
9
|
-
|
|
10
|
-
security_id = "2885" # RELIANCE
|
|
11
|
-
price = 2450.0
|
|
12
|
-
quantity = 1
|
|
13
|
-
|
|
14
|
-
puts preview_order(
|
|
15
|
-
security_id: security_id,
|
|
16
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
|
|
17
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
18
|
-
quantity: quantity,
|
|
19
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
20
|
-
product_type: DhanHQ::Constants::ProductType::CNC,
|
|
21
|
-
price: price,
|
|
22
|
-
trading_symbol: "RELIANCE"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
# Uncomment after confirmation:
|
|
26
|
-
# order = DhanHQ::Models::Order.place(
|
|
27
|
-
# security_id: security_id,
|
|
28
|
-
# exchange_segment: "NSE_EQ",
|
|
29
|
-
# transaction_type: "BUY",
|
|
30
|
-
# quantity: quantity,
|
|
31
|
-
# order_type: "LIMIT",
|
|
32
|
-
# product_type: "CNC",
|
|
33
|
-
# price: price,
|
|
34
|
-
# validity: "DAY"
|
|
35
|
-
# )
|
|
36
|
-
# puts "Placed order ID: #{order.order_id}"
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
-
require "dhan_hq"
|
|
5
|
-
require_relative "../scripts/dhan_helpers"
|
|
6
|
-
|
|
7
|
-
# Initialize credentials
|
|
8
|
-
get_client
|
|
9
|
-
|
|
10
|
-
expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(underlying_scrip: 13, underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I)
|
|
11
|
-
nearest_expiry = expiries.first
|
|
12
|
-
|
|
13
|
-
if nearest_expiry.nil?
|
|
14
|
-
puts "Failed to fetch expiries."
|
|
15
|
-
exit 1
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
puts "Nearest expiry: #{nearest_expiry}"
|
|
19
|
-
|
|
20
|
-
chain_df, spot = fetch_chain_df(under_security_id: 13, expiry: nearest_expiry)
|
|
21
|
-
atm = find_atm_row(chain_df, spot)
|
|
22
|
-
|
|
23
|
-
if atm.nil?
|
|
24
|
-
puts "Failed to find ATM row."
|
|
25
|
-
exit 1
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
ce_security_id = atm["ce_security_id"]
|
|
29
|
-
ce_ltp = atm["ce_ltp"].to_f
|
|
30
|
-
lot_size = get_lot_size(underlying: "NIFTY") || 75
|
|
31
|
-
quantity = lot_size
|
|
32
|
-
|
|
33
|
-
puts "Nifty spot: #{spot}"
|
|
34
|
-
puts "ATM strike: #{atm["strike"]}"
|
|
35
|
-
puts "CE security ID: #{ce_security_id}, LTP: Rs. #{"%.2f" % ce_ltp}"
|
|
36
|
-
puts
|
|
37
|
-
|
|
38
|
-
puts preview_order(
|
|
39
|
-
security_id: ce_security_id,
|
|
40
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
|
|
41
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
42
|
-
quantity: quantity,
|
|
43
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
44
|
-
product_type: DhanHQ::Constants::ProductType::INTRADAY,
|
|
45
|
-
price: ce_ltp,
|
|
46
|
-
trading_symbol: "NIFTY #{atm["strike"].to_i} CE"
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
margin = check_margin(
|
|
50
|
-
security_id: ce_security_id,
|
|
51
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
|
|
52
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
53
|
-
quantity: quantity,
|
|
54
|
-
product_type: DhanHQ::Constants::ProductType::INTRADAY,
|
|
55
|
-
price: ce_ltp
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
printf(
|
|
59
|
-
"Margin check: sufficient=%s required=Rs. %s available=Rs. %s\n",
|
|
60
|
-
margin["sufficient"].to_s,
|
|
61
|
-
"%.2f" % margin["total_margin"],
|
|
62
|
-
"%.2f" % margin["available_balance"]
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# Uncomment only after confirmation:
|
|
66
|
-
# order = DhanHQ::Models::Order.place(
|
|
67
|
-
# security_id: ce_security_id,
|
|
68
|
-
# exchange_segment: "NSE_FNO",
|
|
69
|
-
# transaction_type: "BUY",
|
|
70
|
-
# quantity: quantity,
|
|
71
|
-
# order_type: "LIMIT",
|
|
72
|
-
# product_type: "INTRADAY",
|
|
73
|
-
# price: ce_ltp,
|
|
74
|
-
# validity: "DAY"
|
|
75
|
-
# )
|
|
76
|
-
# puts "Placed order ID: #{order.order_id}"
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
-
require "dhan_hq"
|
|
5
|
-
require_relative "../scripts/dhan_helpers"
|
|
6
|
-
|
|
7
|
-
# Initialize credentials
|
|
8
|
-
get_client
|
|
9
|
-
|
|
10
|
-
holdings = begin
|
|
11
|
-
DhanHQ::Models::Holding.all
|
|
12
|
-
rescue StandardError
|
|
13
|
-
[]
|
|
14
|
-
end
|
|
15
|
-
positions = begin
|
|
16
|
-
DhanHQ::Models::Position.all
|
|
17
|
-
rescue StandardError
|
|
18
|
-
[]
|
|
19
|
-
end
|
|
20
|
-
funds = begin
|
|
21
|
-
DhanHQ::Models::Funds.fetch
|
|
22
|
-
rescue StandardError
|
|
23
|
-
nil
|
|
24
|
-
end
|
|
25
|
-
trades = begin
|
|
26
|
-
DhanHQ::Models::Trade.today
|
|
27
|
-
rescue StandardError
|
|
28
|
-
[]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
summary = format_pnl_report(holdings, positions)
|
|
32
|
-
|
|
33
|
-
puts "=" * 50
|
|
34
|
-
puts " PORTFOLIO SUMMARY"
|
|
35
|
-
puts "=" * 50
|
|
36
|
-
puts "\nHoldings count: #{summary["holdings_count"]}"
|
|
37
|
-
puts "Positions count: #{summary["positions_count"]}"
|
|
38
|
-
printf("Current value: Rs. %12.2f\n", summary["current_value"])
|
|
39
|
-
printf("Total P&L: Rs. %12.2f\n", summary["total_pnl"])
|
|
40
|
-
printf("Day P&L: Rs. %12.2f\n", summary["day_pnl"])
|
|
41
|
-
|
|
42
|
-
if funds
|
|
43
|
-
available = funds.availabel_balance || funds.available_balance || 0.0
|
|
44
|
-
utilized = funds.utilized_amount || 0.0
|
|
45
|
-
collateral = funds.collateral_amount || 0.0
|
|
46
|
-
withdrawable = funds.withdrawable_balance || 0.0
|
|
47
|
-
|
|
48
|
-
puts "\nFUNDS"
|
|
49
|
-
printf(" Available: Rs. %12.2f\n", available)
|
|
50
|
-
printf(" Utilized: Rs. %12.2f\n", utilized)
|
|
51
|
-
printf(" Collateral: Rs. %12.2f\n", collateral)
|
|
52
|
-
printf(" Withdrawable: Rs. %12.2f\n", withdrawable)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
if holdings.any?
|
|
56
|
-
puts "\nTOP HOLDINGS"
|
|
57
|
-
# Sort holdings by quantity
|
|
58
|
-
sorted_holdings = holdings.sort_by { |h| -(h.total_qty || 0) }
|
|
59
|
-
sorted_holdings.first(5).each do |holding|
|
|
60
|
-
printf(" %-15s qty=%5d available=%5d\n", holding.trading_symbol, holding.total_qty.to_i, holding.available_qty.to_i)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
open_positions = positions.reject { |p| p.net_qty.to_i.zero? }
|
|
65
|
-
if open_positions.any?
|
|
66
|
-
puts "\nOPEN POSITIONS"
|
|
67
|
-
open_positions.first(5).each do |position|
|
|
68
|
-
pnl = position.realized_profit.to_f + position.unrealized_profit.to_f
|
|
69
|
-
printf(" %-20s netQty=%5d pnl=Rs. %8.0f\n", position.trading_symbol, position.net_qty.to_i, pnl)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
puts "\nTrades today: #{trades.size}"
|
|
74
|
-
puts "=" * 50
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
-
require "dhan_hq"
|
|
5
|
-
require_relative "../scripts/dhan_helpers"
|
|
6
|
-
|
|
7
|
-
# Initialize credentials
|
|
8
|
-
get_client
|
|
9
|
-
|
|
10
|
-
# Fetch LTP for Reliance
|
|
11
|
-
ltp_response = DhanHQ::Models::MarketFeed.ltp("NSE_EQ" => [2885])
|
|
12
|
-
if ltp_response[:status] != "success"
|
|
13
|
-
puts "Failed to fetch LTP: #{ltp_response[:remarks]}"
|
|
14
|
-
exit 1
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
reliance_ltp = ltp_response[:data][DhanHQ::Constants::ExchangeSegment::NSE_EQ]["2885"]["last_price"].to_f
|
|
18
|
-
puts "Reliance LTP: Rs. #{"%.2f" % reliance_ltp}"
|
|
19
|
-
|
|
20
|
-
entry_price = reliance_ltp
|
|
21
|
-
target_price = (entry_price * 1.02).round(2)
|
|
22
|
-
sl_price = (entry_price * 0.99).round(2)
|
|
23
|
-
trailing_jump = 5.0
|
|
24
|
-
|
|
25
|
-
puts "\n--- Super Order Preview ---"
|
|
26
|
-
puts "Action: BUY 1 share of RELIANCE"
|
|
27
|
-
puts "Entry Price: Rs. #{"%.2f" % entry_price}"
|
|
28
|
-
puts "Target: Rs. #{"%.2f" % target_price}"
|
|
29
|
-
puts "Stop Loss: Rs. #{"%.2f" % sl_price}"
|
|
30
|
-
puts "Trailing Jump: Rs. #{"%.2f" % trailing_jump}"
|
|
31
|
-
puts "Product: INTRADAY"
|
|
32
|
-
|
|
33
|
-
# Uncomment after confirmation:
|
|
34
|
-
# order = DhanHQ::Models::SuperOrder.create(
|
|
35
|
-
# security_id: "2885",
|
|
36
|
-
# exchange_segment: "NSE_EQ",
|
|
37
|
-
# transaction_type: "BUY",
|
|
38
|
-
# quantity: 1,
|
|
39
|
-
# order_type: "LIMIT",
|
|
40
|
-
# product_type: "INTRADAY",
|
|
41
|
-
# price: entry_price,
|
|
42
|
-
# target_price: target_price,
|
|
43
|
-
# stop_loss_price: sl_price,
|
|
44
|
-
# trailing_jump: trailing_jump
|
|
45
|
-
# )
|
|
46
|
-
#
|
|
47
|
-
# if order
|
|
48
|
-
# puts "Super order placed: #{order.order_id} - #{order.order_status}"
|
|
49
|
-
#
|
|
50
|
-
# # Connect to Order Update websocket to listen for updates
|
|
51
|
-
# orders_client = DhanHQ::WS::Orders.connect do |update|
|
|
52
|
-
# puts "Order Update -> OrderNo: #{update.order_no}, Status: #{update.status}"
|
|
53
|
-
# end
|
|
54
|
-
#
|
|
55
|
-
# sleep(10)
|
|
56
|
-
# orders_client.stop rescue nil
|
|
57
|
-
# end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Backtesting With Dhan Data (Ruby SDK)
|
|
2
|
-
|
|
3
|
-
## Daily Equity Backtest Skeleton
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
# Fetch daily charts via HistoricalData model
|
|
7
|
-
candles = DhanHQ::Models::HistoricalData.daily(
|
|
8
|
-
security_id: "2885",
|
|
9
|
-
exchange_segment: "NSE_EQ",
|
|
10
|
-
instrument: "EQUITY",
|
|
11
|
-
from_date: "2023-01-01",
|
|
12
|
-
to_date: "2024-12-31"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
# candles is a normalized array of hashes:
|
|
16
|
-
# [{ timestamp: Time, open: Float, high: Float, low: Float, close: Float, volume: Integer }]
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Typical next steps:
|
|
20
|
-
- Create signals based on technical calculations.
|
|
21
|
-
- Shift positions to avoid look-ahead bias.
|
|
22
|
-
- Apply transaction costs.
|
|
23
|
-
- Compute CAGR, maximum drawdown, Sharpe ratio, and win rate.
|
|
24
|
-
|
|
25
|
-
## Minute-Level Backtest Skeleton
|
|
26
|
-
|
|
27
|
-
```ruby
|
|
28
|
-
candles = DhanHQ::Models::HistoricalData.intraday(
|
|
29
|
-
security_id: "2885",
|
|
30
|
-
exchange_segment: "NSE_EQ",
|
|
31
|
-
instrument: "EQUITY",
|
|
32
|
-
from_date: "2024-09-11 09:30:00",
|
|
33
|
-
to_date: "2024-09-15 13:00:00",
|
|
34
|
-
interval: "5", # 5-minute interval
|
|
35
|
-
oi: false
|
|
36
|
-
)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Expired Options Backtest Skeleton
|
|
40
|
-
|
|
41
|
-
```ruby
|
|
42
|
-
response = DhanHQ::Models::ExpiredOptionsData.fetch(
|
|
43
|
-
underlying_scrip: 13,
|
|
44
|
-
exchange_segment: "NSE_FNO",
|
|
45
|
-
expiry_flag: "MONTH",
|
|
46
|
-
expiry_code: 1,
|
|
47
|
-
strike: "ATM",
|
|
48
|
-
option_type: "CALL",
|
|
49
|
-
required_data: ["open", "high", "low", "close", "volume", "oi", "spot"],
|
|
50
|
-
from_date: "2021-08-01",
|
|
51
|
-
to_date: "2021-08-31",
|
|
52
|
-
interval: "1"
|
|
53
|
-
)
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Cost Model Reminders
|
|
57
|
-
|
|
58
|
-
At minimum consider:
|
|
59
|
-
- Brokerage charges
|
|
60
|
-
- Securities Transaction Tax (STT)
|
|
61
|
-
- Exchange transaction charges
|
|
62
|
-
- GST (Service Tax)
|
|
63
|
-
- Stamp duty
|
|
64
|
-
- SEBI turnover charges
|
|
65
|
-
- Slippage
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# Common Workflows — Agent Playbooks (Ruby SDK)
|
|
2
|
-
|
|
3
|
-
## Portfolio Rebalance
|
|
4
|
-
|
|
5
|
-
Recommended sequence:
|
|
6
|
-
1. Fetch holdings and funds.
|
|
7
|
-
2. Compute target deltas.
|
|
8
|
-
3. Resolve symbols and quantities.
|
|
9
|
-
4. Preview proposed orders.
|
|
10
|
-
5. Confirm with the user.
|
|
11
|
-
6. Place live orders.
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
holdings = DhanHQ::Models::Holding.all rescue []
|
|
15
|
-
funds = DhanHQ::Models::Funds.fetch rescue nil
|
|
16
|
-
|
|
17
|
-
if funds
|
|
18
|
-
available_cash = funds.availabel_balance || funds.available_balance || 0.0
|
|
19
|
-
end
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Delivery Sell With eDIS
|
|
23
|
-
|
|
24
|
-
Use this flow for selling demat holdings:
|
|
25
|
-
1. Fetch holdings and identify ISIN.
|
|
26
|
-
2. Generate TPIN: `DhanHQ::Models::EDIS.generate_tpin`
|
|
27
|
-
3. Open authorization form: `DhanHQ::Models::EDIS.open_browser_for_tpin(isin: "...", qty: 5, exchange: "NSE")`
|
|
28
|
-
4. Check inquiry: `DhanHQ::Models::EDIS.inquiry(isin: "...")`
|
|
29
|
-
5. Place the sell order.
|
|
30
|
-
|
|
31
|
-
```ruby
|
|
32
|
-
# Generate TPIN
|
|
33
|
-
DhanHQ::Models::EDIS.generate_tpin
|
|
34
|
-
|
|
35
|
-
# Open authorization portal
|
|
36
|
-
DhanHQ::Models::EDIS.open_browser_for_tpin(isin: "INE002A01018", qty: 5, exchange: "NSE")
|
|
37
|
-
|
|
38
|
-
# Inquiry
|
|
39
|
-
status = DhanHQ::Models::EDIS.inquiry(isin: "INE002A01018")
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Single-Leg F&O Execution
|
|
43
|
-
|
|
44
|
-
Recommended sequence:
|
|
45
|
-
1. Resolve current contract from option chain or security master.
|
|
46
|
-
2. Resolve lot size.
|
|
47
|
-
3. Validate quantity.
|
|
48
|
-
4. Check margin.
|
|
49
|
-
5. Preview & Confirm.
|
|
50
|
-
6. Place live order.
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
require_relative "../scripts/dhan_helpers"
|
|
54
|
-
|
|
55
|
-
chain_df, spot = fetch_chain_df(under_security_id: 13, expiry: "2025-03-27")
|
|
56
|
-
atm = find_atm_row(chain_df, spot)
|
|
57
|
-
|
|
58
|
-
margin = check_margin(
|
|
59
|
-
security_id: atm["ce_security_id"],
|
|
60
|
-
exchange_segment: "NSE_FNO",
|
|
61
|
-
transaction_type: "BUY",
|
|
62
|
-
quantity: 75,
|
|
63
|
-
product_type: "INTRADAY",
|
|
64
|
-
price: atm["ce_ltp"].to_f
|
|
65
|
-
)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Daily P&L Summary
|
|
69
|
-
|
|
70
|
-
```ruby
|
|
71
|
-
require_relative "../scripts/dhan_helpers"
|
|
72
|
-
|
|
73
|
-
holdings = DhanHQ::Models::Holding.all
|
|
74
|
-
positions = DhanHQ::Models::Position.all
|
|
75
|
-
summary = format_pnl_report(holdings, positions)
|
|
76
|
-
```
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Error Codes — Complete Reference (Ruby SDK)
|
|
2
|
-
|
|
3
|
-
In the Ruby SDK, raw API error codes are automatically mapped in the client layer and raised as structured exceptions.
|
|
4
|
-
|
|
5
|
-
## Exception Mapping
|
|
6
|
-
|
|
7
|
-
The Ruby client maps Dhan error codes to specific error classes under the `DhanHQ` module:
|
|
8
|
-
|
|
9
|
-
| Error Code | Error Class | Meaning |
|
|
10
|
-
|------------|-------------|---------|
|
|
11
|
-
| `DH-901` | `DhanHQ::InvalidAuthenticationError` | Client ID or access token is invalid or expired |
|
|
12
|
-
| `DH-902` | `DhanHQ::InvalidAccessError` | User does not have required Data API or Trading API access |
|
|
13
|
-
| `DH-903` | `DhanHQ::UserAccountError` | Account setup issue or segment activation requirement |
|
|
14
|
-
| `DH-904` | `DhanHQ::RateLimitError` | Rate limit exceeded |
|
|
15
|
-
| `DH-905` | `DhanHQ::InputExceptionError` | Missing or invalid request fields |
|
|
16
|
-
| `DH-906` | `DhanHQ::OrderError` | Order request cannot be processed |
|
|
17
|
-
| `DH-907` | `DhanHQ::DataError` | Data unavailable or parameters invalid |
|
|
18
|
-
| `DH-908` | `DhanHQ::InternalServerError` | Server-side failure |
|
|
19
|
-
| `DH-909` | `DhanHQ::NetworkError` | Backend communication failure |
|
|
20
|
-
| `DH-1111` | `DhanHQ::NoHoldingsError` | No holdings present in the account |
|
|
21
|
-
| `DH-910` / other | `DhanHQ::OtherError` / `DhanHQ::Error` | Other failure reasons |
|
|
22
|
-
|
|
23
|
-
## Data API Errors
|
|
24
|
-
|
|
25
|
-
| Code | Exception | Meaning |
|
|
26
|
-
|------|-----------|---------|
|
|
27
|
-
| `800` | `DhanHQ::InternalServerError` | Internal Server Error |
|
|
28
|
-
| `804` | `DhanHQ::Error` | Requested number of instruments exceeds limit |
|
|
29
|
-
| `805` | `DhanHQ::RateLimitError` | Too many requests or connections |
|
|
30
|
-
| `806` | `DhanHQ::DataError` | Data APIs not subscribed |
|
|
31
|
-
| `807` | `DhanHQ::TokenExpiredError` | Access token is expired |
|
|
32
|
-
| `808` | `DhanHQ::AuthenticationFailedError` | Authentication failed - client ID or access token invalid |
|
|
33
|
-
| `809` | `DhanHQ::InvalidTokenError` | Access token is invalid |
|
|
34
|
-
| `810` | `DhanHQ::InvalidClientIDError` | Client ID is invalid |
|
|
35
|
-
| `811` | `DhanHQ::InvalidRequestError` | Invalid expiry date |
|
|
36
|
-
| `812` | `DhanHQ::InvalidRequestError` | Invalid date format |
|
|
37
|
-
| `813` | `DhanHQ::InvalidRequestError` | Invalid security ID |
|
|
38
|
-
| `814` | `DhanHQ::InvalidRequestError` | Invalid request |
|
|
39
|
-
|
|
40
|
-
## User Action Checklist
|
|
41
|
-
|
|
42
|
-
### Invalid Data Subscription (`806` or `DH-902`)
|
|
43
|
-
If you receive access errors:
|
|
44
|
-
1. Log in to `web.dhan.co`.
|
|
45
|
-
2. Go to **My Profile** -> **Access DhanHQ APIs**.
|
|
46
|
-
3. Verify that the **Data API** plan is active.
|
|
47
|
-
4. If not active, activate it, generate a fresh access token, and retry.
|
|
48
|
-
|
|
49
|
-
### Static IP Error (`DH-911` or IP issue)
|
|
50
|
-
If placing or managing orders fails with IP errors, ensure that the server's public IP is whitelisted in your Dhan console.
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Funds & Margin — Complete Reference (Ruby SDK)
|
|
2
|
-
|
|
3
|
-
The Ruby SDK exposes first-class models `DhanHQ::Models::Funds` and `DhanHQ::Models::Margin` for funds retrieval and pre-flight margin checks (both single-order and multi-leg).
|
|
4
|
-
|
|
5
|
-
## Fund Limits
|
|
6
|
-
|
|
7
|
-
Use `DhanHQ::Models::Funds.fetch`:
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
funds = DhanHQ::Models::Funds.fetch
|
|
11
|
-
|
|
12
|
-
puts "Available Balance: Rs. #{funds.availabel_balance || funds.available_balance}"
|
|
13
|
-
puts "Utilized: Rs. #{funds.utilized_amount}"
|
|
14
|
-
puts "Collateral: Rs. #{funds.collateral_amount}"
|
|
15
|
-
puts "Withdrawable: Rs. #{funds.withdrawable_balance}"
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Normalized model attributes:
|
|
19
|
-
- `dhan_client_id`
|
|
20
|
-
- `availabel_balance` (or alias `available_balance`)
|
|
21
|
-
- `sod_limit`
|
|
22
|
-
- `collateral_amount`
|
|
23
|
-
- `receiveable_amount`
|
|
24
|
-
- `utilized_amount`
|
|
25
|
-
- `blocked_payout_amount`
|
|
26
|
-
- `withdrawable_balance`
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Margin Calculator — Single Order
|
|
31
|
-
|
|
32
|
-
Use `DhanHQ::Models::Margin.calculate(params)`:
|
|
33
|
-
|
|
34
|
-
```ruby
|
|
35
|
-
margin = DhanHQ::Models::Margin.calculate(
|
|
36
|
-
security_id: "2885",
|
|
37
|
-
exchange_segment: "NSE_EQ",
|
|
38
|
-
transaction_type: "BUY",
|
|
39
|
-
quantity: 10,
|
|
40
|
-
product_type: "CNC",
|
|
41
|
-
price: 2450.0
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
puts "Total Margin: Rs. #{margin.total_margin}"
|
|
45
|
-
puts "Available Balance: Rs. #{margin.available_balance}"
|
|
46
|
-
puts "Brokerage Charges: Rs. #{margin.brokerage}"
|
|
47
|
-
puts "Leverage Offered: #{margin.leverage}x"
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Multi-Order Margin
|
|
53
|
-
|
|
54
|
-
Unlike the Python SDK, the Ruby SDK has first-class support for multi-leg portfolio margin calculation via `DhanHQ::Models::Margin.calculate_multi(params)`:
|
|
55
|
-
|
|
56
|
-
```ruby
|
|
57
|
-
margin = DhanHQ::Models::Margin.calculate_multi(
|
|
58
|
-
include_position: true,
|
|
59
|
-
include_orders: true,
|
|
60
|
-
scripts: [
|
|
61
|
-
{ exchange_segment: "NSE_EQ", transaction_type: "BUY", quantity: 100, product_type: "CNC", security_id: "1333", price: 1428.0 },
|
|
62
|
-
{ exchange_segment: "NSE_EQ", transaction_type: "SELL", quantity: 50, product_type: "INTRADAY", security_id: "11536", price: 3000.0 }
|
|
63
|
-
]
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
puts "Portfolio Total Margin Required: Rs. #{margin.total_margin}"
|
|
67
|
-
```
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Instruments — Complete Reference (Ruby SDK)
|
|
2
|
-
|
|
3
|
-
Use the security master as the primary source for `security_id`, lot size, expiry, strike, tick size, and display symbol.
|
|
4
|
-
|
|
5
|
-
## Preferred SDK Entry Point
|
|
6
|
-
|
|
7
|
-
In the Ruby SDK, search and load instruments segment-wise using:
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
# Retrieve compact list for a single segment (returns Array of Instrument objects)
|
|
11
|
-
instruments = DhanHQ::Models::Instrument.by_segment("NSE_EQ")
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Official instrument sources (managed by the SDK internally):
|
|
15
|
-
- Compact CSV: `https://images.dhan.co/api-data/api-scrip-master.csv`
|
|
16
|
-
- Detailed CSV: `https://images.dhan.co/api-data/api-scrip-master-detailed.csv`
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Key Columns (Instrument Attributes)
|
|
21
|
-
|
|
22
|
-
| Attribute | Meaning |
|
|
23
|
-
|-----------|---------|
|
|
24
|
-
| `security_id` | Security ID (String) |
|
|
25
|
-
| `exchange` | Exchange ID (`NSE`, `BSE`, `MCX`) |
|
|
26
|
-
| `instrument` | Instrument Type (`EQUITY`, `OPTIDX`, `OPTSTK`, etc.) |
|
|
27
|
-
| `symbol_name` | Exchange trading symbol |
|
|
28
|
-
| `display_name` | Dhan custom symbol |
|
|
29
|
-
| `lot_size` | Lot size (Integer) |
|
|
30
|
-
| `tick_size` | Tick size (Float) |
|
|
31
|
-
| `expiry_date` | Expiry date (String) |
|
|
32
|
-
| `strike_price` | Strike price (Float) |
|
|
33
|
-
| `option_type` | Option Type (`CALL` or `PUT`) |
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Recommended Resolution Flow
|
|
38
|
-
|
|
39
|
-
Use the SDK's built-in helper methods on the `Instrument` class:
|
|
40
|
-
|
|
41
|
-
```ruby
|
|
42
|
-
# Find specific instrument in a segment (exact match)
|
|
43
|
-
inst = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
|
|
44
|
-
|
|
45
|
-
# Search across multiple segments (finds any match)
|
|
46
|
-
inst = DhanHQ::Models::Instrument.find_anywhere("RELIANCE")
|
|
47
|
-
|
|
48
|
-
# Fuzzy search across multiple segments
|
|
49
|
-
results = DhanHQ::Models::Instrument.search("RELIANCE")
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Or leverage the helper layer in `scripts/dhan_helpers.rb`:
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
require_relative "../scripts/dhan_helpers"
|
|
56
|
-
|
|
57
|
-
cash = resolve_symbol("RELIANCE", "NSE_EQ")
|
|
58
|
-
contract = resolve_derivative("NIFTY", strike: 24000, option_type: "CE", expiry: "2025-03-27")
|
|
59
|
-
lot_size = get_lot_size(underlying: "NIFTY")
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Quick-Reference Fallback IDs
|
|
65
|
-
|
|
66
|
-
### Index Underlyings
|
|
67
|
-
|
|
68
|
-
| Underlying | security_id | Underlying Segment |
|
|
69
|
-
|------------|-------------|-------------------|
|
|
70
|
-
| NIFTY 50 | `13` | `IDX_I` |
|
|
71
|
-
| BANK NIFTY | `25` | `IDX_I` |
|
|
72
|
-
| FINNIFTY | `27` | `IDX_I` |
|
|
73
|
-
| MIDCPNIFTY | `442` | `IDX_I` |
|
|
74
|
-
| SENSEX | `51` | `IDX_I` |
|
|
75
|
-
|
|
76
|
-
### Common NSE Equities
|
|
77
|
-
|
|
78
|
-
| Symbol | security_id |
|
|
79
|
-
|--------|-------------|
|
|
80
|
-
| RELIANCE | `2885` |
|
|
81
|
-
| HDFCBANK | `1333` |
|
|
82
|
-
| TCS | `11536` |
|
|
83
|
-
| INFY | `1594` |
|
|
84
|
-
| ICICIBANK | `4963` |
|
|
85
|
-
| SBIN | `3045` |
|