DhanHQ 2.5.0 → 2.6.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/.rubocop.yml +9 -1
- data/CHANGELOG.md +78 -6
- data/GUIDE.md +57 -39
- data/README.md +24 -23
- data/docs/API_DOCS_GAPS.md +128 -0
- data/docs/API_VERIFICATION.md +10 -11
- data/docs/ARCHIVE_README.md +16 -16
- data/docs/AUTHENTICATION.md +1 -1
- data/docs/CONSTANTS_REFERENCE.md +477 -0
- data/docs/DATA_API_PARAMETERS.md +7 -7
- data/docs/{rails_websocket_integration.md → RAILS_WEBSOCKET_INTEGRATION.md} +10 -10
- data/docs/{standalone_ruby_websocket_integration.md → STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md} +32 -32
- data/docs/{technical_analysis.md → TECHNICAL_ANALYSIS.md} +3 -3
- data/docs/TESTING_GUIDE.md +84 -82
- data/docs/{websocket_integration.md → WEBSOCKET_INTEGRATION.md} +19 -19
- data/docs/WEBSOCKET_PROTOCOL.md +2 -2
- data/lib/DhanHQ/constants.rb +456 -151
- data/lib/DhanHQ/contracts/alert_order_contract.rb +37 -10
- data/lib/DhanHQ/contracts/base_contract.rb +22 -0
- data/lib/DhanHQ/contracts/edis_contract.rb +25 -0
- data/lib/DhanHQ/contracts/margin_calculator_contract.rb +27 -4
- data/lib/DhanHQ/contracts/modify_order_contract.rb +65 -12
- data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +23 -0
- data/lib/DhanHQ/contracts/order_contract.rb +171 -39
- data/lib/DhanHQ/contracts/place_order_contract.rb +14 -141
- data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +20 -0
- data/lib/DhanHQ/contracts/position_conversion_contract.rb +15 -3
- data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -1
- data/lib/DhanHQ/contracts/user_ip_contract.rb +14 -0
- data/lib/DhanHQ/core/base_model.rb +13 -4
- data/lib/DhanHQ/helpers/response_helper.rb +2 -2
- data/lib/DhanHQ/helpers/validation_helper.rb +1 -1
- data/lib/DhanHQ/models/alert_order.rb +7 -11
- data/lib/DhanHQ/models/concerns/api_response_handler.rb +46 -0
- data/lib/DhanHQ/models/edis.rb +0 -9
- data/lib/DhanHQ/models/expired_options_data.rb +6 -12
- data/lib/DhanHQ/models/forever_order.rb +8 -5
- data/lib/DhanHQ/models/historical_data.rb +0 -8
- data/lib/DhanHQ/models/instrument.rb +1 -7
- data/lib/DhanHQ/models/instrument_helpers.rb +4 -4
- data/lib/DhanHQ/models/kill_switch.rb +1 -11
- data/lib/DhanHQ/models/margin.rb +2 -2
- data/lib/DhanHQ/models/order.rb +107 -126
- data/lib/DhanHQ/models/order_update.rb +7 -13
- data/lib/DhanHQ/models/pnl_exit.rb +1 -9
- data/lib/DhanHQ/models/position.rb +1 -1
- data/lib/DhanHQ/models/postback.rb +4 -13
- data/lib/DhanHQ/models/profile.rb +0 -10
- data/lib/DhanHQ/models/super_order.rb +13 -3
- data/lib/DhanHQ/models/trade.rb +11 -23
- data/lib/DhanHQ/resources/ip_setup.rb +16 -5
- data/lib/DhanHQ/resources/kill_switch.rb +9 -7
- data/lib/DhanHQ/resources/orders.rb +41 -41
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/cmd_bus.rb +1 -1
- data/lib/DhanHQ/ws/orders/client.rb +6 -6
- data/lib/DhanHQ/ws/singleton_lock.rb +2 -1
- data/lib/dhanhq/analysis/options_buying_advisor.rb +2 -2
- data/lib/rubocop/cop/dhanhq/use_constants.rb +171 -0
- metadata +20 -23
- data/TODO-1.md +0 -14
- data/TODO.md +0 -127
- data/app/services/live/order_update_guard_support.rb +0 -75
- data/app/services/live/order_update_hub.rb +0 -76
- data/app/services/live/order_update_persistence_support.rb +0 -68
- data/docs/PR_2.2.0.md +0 -48
- data/examples/comprehensive_websocket_examples.rb +0 -148
- data/examples/instrument_finder_test.rb +0 -195
- data/examples/live_order_updates.rb +0 -118
- data/examples/market_depth_example.rb +0 -144
- data/examples/market_feed_example.rb +0 -81
- data/examples/order_update_example.rb +0 -105
- data/examples/trading_fields_example.rb +0 -215
- /data/docs/{live_order_updates.md → LIVE_ORDER_UPDATES.md} +0 -0
- /data/docs/{rails_integration.md → RAILS_INTEGRATION.md} +0 -0
|
@@ -1,70 +1,70 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module DhanHQ
|
|
4
|
-
# REST API wrappers grouped by resource type.
|
|
5
4
|
module Resources
|
|
6
|
-
#
|
|
5
|
+
# Handles order placement, modification, and cancellation
|
|
7
6
|
class Orders < BaseAPI
|
|
8
|
-
# Orders are routed through the trading API tier.
|
|
9
7
|
API_TYPE = :order_api
|
|
10
|
-
# Base path for order endpoints.
|
|
11
8
|
HTTP_PATH = "/v2/orders"
|
|
12
9
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
def all
|
|
17
|
-
get("")
|
|
18
|
-
end
|
|
10
|
+
# --------------------------------------------------
|
|
11
|
+
# PUBLIC API
|
|
12
|
+
# --------------------------------------------------
|
|
19
13
|
|
|
20
|
-
# Places a new order using the provided payload.
|
|
21
|
-
#
|
|
22
|
-
# @param params [Hash]
|
|
23
|
-
# @return [Hash]
|
|
24
14
|
def create(params)
|
|
15
|
+
validate_place_order!(params)
|
|
25
16
|
post("", params: params)
|
|
26
17
|
end
|
|
27
18
|
|
|
28
|
-
# Fetches a single order by broker order id.
|
|
29
|
-
#
|
|
30
|
-
# @param order_id [String]
|
|
31
|
-
# @return [Hash]
|
|
32
|
-
def find(order_id)
|
|
33
|
-
get("/#{order_id}")
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Modifies an existing order.
|
|
37
|
-
#
|
|
38
|
-
# @param order_id [String]
|
|
39
|
-
# @param params [Hash]
|
|
40
|
-
# @return [Hash]
|
|
41
19
|
def update(order_id, params)
|
|
20
|
+
validate_modify_order!(params.merge(order_id: order_id))
|
|
42
21
|
put("/#{order_id}", params: params)
|
|
43
22
|
end
|
|
44
23
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
def slicing(params)
|
|
25
|
+
validate_place_order!(params)
|
|
26
|
+
post("/slicing", params: params)
|
|
27
|
+
end
|
|
28
|
+
|
|
49
29
|
def cancel(order_id)
|
|
50
30
|
delete("/#{order_id}")
|
|
51
31
|
end
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def
|
|
58
|
-
|
|
33
|
+
def all
|
|
34
|
+
get("")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find(order_id)
|
|
38
|
+
get("/#{order_id}")
|
|
59
39
|
end
|
|
60
40
|
|
|
61
|
-
# Retrieve an order by client-supplied correlation id.
|
|
62
|
-
#
|
|
63
|
-
# @param correlation_id [String]
|
|
64
|
-
# @return [Hash]
|
|
65
41
|
def by_correlation(correlation_id)
|
|
66
42
|
get("/external/#{correlation_id}")
|
|
67
43
|
end
|
|
44
|
+
|
|
45
|
+
# --------------------------------------------------
|
|
46
|
+
# VALIDATION LAYER
|
|
47
|
+
# --------------------------------------------------
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def validate_place_order!(params)
|
|
52
|
+
result = Contracts::PlaceOrderContract.new.call(normalize_keys_for_validation(params))
|
|
53
|
+
raise_validation_error!(result) unless result.success?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def validate_modify_order!(params)
|
|
57
|
+
result = Contracts::ModifyOrderContract.new.call(normalize_keys_for_validation(params))
|
|
58
|
+
raise_validation_error!(result) unless result.success?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def normalize_keys_for_validation(params)
|
|
62
|
+
snake_case(params)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def raise_validation_error!(result)
|
|
66
|
+
raise DhanHQ::Error, "Validation Error: #{result.errors.to_h}"
|
|
67
|
+
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
end
|
data/lib/DhanHQ/version.rb
CHANGED
data/lib/DhanHQ/ws/cmd_bus.rb
CHANGED
|
@@ -201,17 +201,17 @@ module DhanHQ
|
|
|
201
201
|
# @param _previous_state [OrderUpdate, nil] Previous order state (unused parameter)
|
|
202
202
|
def emit_status_specific_events(order_update, _previous_state)
|
|
203
203
|
case order_update.status
|
|
204
|
-
when
|
|
204
|
+
when DhanHQ::Constants::OrderStatus::TRANSIT
|
|
205
205
|
emit(:order_transit, order_update)
|
|
206
|
-
when
|
|
206
|
+
when DhanHQ::Constants::OrderStatus::PENDING
|
|
207
207
|
emit(:order_pending, order_update)
|
|
208
|
-
when
|
|
208
|
+
when DhanHQ::Constants::OrderStatus::REJECTED
|
|
209
209
|
emit(:order_rejected, order_update)
|
|
210
|
-
when
|
|
210
|
+
when DhanHQ::Constants::OrderStatus::CANCELLED
|
|
211
211
|
emit(:order_cancelled, order_update)
|
|
212
|
-
when
|
|
212
|
+
when DhanHQ::Constants::OrderStatus::TRADED
|
|
213
213
|
emit(:order_traded, order_update)
|
|
214
|
-
when
|
|
214
|
+
when DhanHQ::Constants::OrderStatus::EXPIRED
|
|
215
215
|
emit(:order_expired, order_update)
|
|
216
216
|
end
|
|
217
217
|
end
|
|
@@ -14,7 +14,8 @@ module DhanHQ
|
|
|
14
14
|
key = Digest::SHA256.hexdigest("#{client_id}:#{token}")[0, 12]
|
|
15
15
|
@path = File.expand_path("tmp/dhanhq_ws_#{key}.lock", Dir.pwd)
|
|
16
16
|
FileUtils.mkdir_p(File.dirname(@path))
|
|
17
|
-
|
|
17
|
+
# Lock file must stay open until release!; block form would close it and release the lock.
|
|
18
|
+
@fh = File.open(@path, File::RDWR | File::CREAT, 0o644) # rubocop:disable Style/FileOpen
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
# Attempts to acquire the lock for the current process.
|
|
@@ -91,7 +91,7 @@ module DhanHQ
|
|
|
91
91
|
|
|
92
92
|
# Use OptionChain model: pick nearest/next expiry and fetch chain
|
|
93
93
|
sid = @data.dig(:meta, :security_id)
|
|
94
|
-
seg = @data.dig(:meta, :exchange_segment) ||
|
|
94
|
+
seg = @data.dig(:meta, :exchange_segment) || DhanHQ::Constants::ExchangeSegment::IDX_I
|
|
95
95
|
return unless sid && seg
|
|
96
96
|
|
|
97
97
|
expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(underlying_scrip: sid.to_i, underlying_seg: seg)
|
|
@@ -128,7 +128,7 @@ module DhanHQ
|
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def index_instrument?(meta)
|
|
131
|
-
meta[:instrument].to_s ==
|
|
131
|
+
meta[:instrument].to_s == DhanHQ::Constants::InstrumentType::INDEX || meta[:exchange_segment].to_s == DhanHQ::Constants::ExchangeSegment::IDX_I
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def select_strike(side:, moneyness:)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rubocop"
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
module DhanHQ
|
|
8
|
+
# Enforces the use of `DhanHQ::Constants` instead of hardcoded strings.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# transaction_type: "BUY"
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# transaction_type: DhanHQ::Constants::TransactionType::BUY
|
|
16
|
+
class UseConstants < Base
|
|
17
|
+
extend AutoCorrector
|
|
18
|
+
|
|
19
|
+
MSG = "Use `%<constant>s` instead of hardcoded string `%<string>s`."
|
|
20
|
+
|
|
21
|
+
# Unambiguous constants — flagged in any non-key string context.
|
|
22
|
+
CONSTANTS_MAP = {
|
|
23
|
+
"SELL" => "DhanHQ::Constants::TransactionType::SELL",
|
|
24
|
+
"BUY" => "DhanHQ::Constants::TransactionType::BUY",
|
|
25
|
+
"LIMIT" => "DhanHQ::Constants::OrderType::LIMIT",
|
|
26
|
+
"MARKET" => "DhanHQ::Constants::OrderType::MARKET",
|
|
27
|
+
"STOP_LOSS" => "DhanHQ::Constants::OrderType::STOP_LOSS",
|
|
28
|
+
"STOP_LOSS_MARKET" => "DhanHQ::Constants::OrderType::STOP_LOSS_MARKET",
|
|
29
|
+
"IOC" => "DhanHQ::Constants::Validity::IOC",
|
|
30
|
+
"DAY" => "DhanHQ::Constants::Validity::DAY",
|
|
31
|
+
"TRANSIT" => "DhanHQ::Constants::OrderStatus::TRANSIT",
|
|
32
|
+
"PENDING" => "DhanHQ::Constants::OrderStatus::PENDING",
|
|
33
|
+
"CLOSED" => "DhanHQ::Constants::OrderStatus::CLOSED",
|
|
34
|
+
"TRIGGERED" => "DhanHQ::Constants::OrderStatus::TRIGGERED",
|
|
35
|
+
"REJECTED" => "DhanHQ::Constants::OrderStatus::REJECTED",
|
|
36
|
+
"CANCELLED" => "DhanHQ::Constants::OrderStatus::CANCELLED",
|
|
37
|
+
"PART_TRADED" => "DhanHQ::Constants::OrderStatus::PART_TRADED",
|
|
38
|
+
"TRADED" => "DhanHQ::Constants::OrderStatus::TRADED",
|
|
39
|
+
"EXPIRED" => "DhanHQ::Constants::OrderStatus::EXPIRED",
|
|
40
|
+
"MODIFIED" => "DhanHQ::Constants::OrderStatus::MODIFIED",
|
|
41
|
+
"PRE_OPEN" => "DhanHQ::Constants::AmoTime::PRE_OPEN",
|
|
42
|
+
"OPEN_30" => "DhanHQ::Constants::AmoTime::OPEN_30",
|
|
43
|
+
"OPEN_60" => "DhanHQ::Constants::AmoTime::OPEN_60",
|
|
44
|
+
"FUTIDX" => "DhanHQ::Constants::InstrumentType::FUTIDX",
|
|
45
|
+
"OPTIDX" => "DhanHQ::Constants::InstrumentType::OPTIDX",
|
|
46
|
+
"EQUITY" => "DhanHQ::Constants::InstrumentType::EQUITY",
|
|
47
|
+
"FUTSTK" => "DhanHQ::Constants::InstrumentType::FUTSTK",
|
|
48
|
+
"OPTSTK" => "DhanHQ::Constants::InstrumentType::OPTSTK",
|
|
49
|
+
"FUTCOM" => "DhanHQ::Constants::InstrumentType::FUTCOM",
|
|
50
|
+
"OPTFUT" => "DhanHQ::Constants::InstrumentType::OPTFUT",
|
|
51
|
+
"FUTCUR" => "DhanHQ::Constants::InstrumentType::FUTCUR",
|
|
52
|
+
"OPTCUR" => "DhanHQ::Constants::InstrumentType::OPTCUR",
|
|
53
|
+
"CALL" => "DhanHQ::Constants::OptionType::CALL",
|
|
54
|
+
"PUT" => "DhanHQ::Constants::OptionType::PUT",
|
|
55
|
+
"ENTRY_LEG" => "DhanHQ::Constants::LegName::ENTRY_LEG",
|
|
56
|
+
"TARGET_LEG" => "DhanHQ::Constants::LegName::TARGET_LEG",
|
|
57
|
+
"STOP_LOSS_LEG" => "DhanHQ::Constants::LegName::STOP_LOSS_LEG",
|
|
58
|
+
"SINGLE" => "DhanHQ::Constants::OrderFlag::SINGLE",
|
|
59
|
+
"OCO" => "DhanHQ::Constants::OrderFlag::OCO",
|
|
60
|
+
"LONG" => "DhanHQ::Constants::PositionType::LONG",
|
|
61
|
+
"SHORT" => "DhanHQ::Constants::PositionType::SHORT",
|
|
62
|
+
"TECHNICAL_WITH_VALUE" => "DhanHQ::Constants::ComparisonType::TECHNICAL_WITH_VALUE",
|
|
63
|
+
"TECHNICAL_WITH_INDICATOR" => "DhanHQ::Constants::ComparisonType::TECHNICAL_WITH_INDICATOR",
|
|
64
|
+
"TECHNICAL_WITH_CLOSE" => "DhanHQ::Constants::ComparisonType::TECHNICAL_WITH_CLOSE",
|
|
65
|
+
"PRICE_WITH_VALUE" => "DhanHQ::Constants::ComparisonType::PRICE_WITH_VALUE",
|
|
66
|
+
"EMA_200" => "DhanHQ::Constants::IndicatorName::EMA_200",
|
|
67
|
+
"BB_UPPER" => "DhanHQ::Constants::IndicatorName::BB_UPPER",
|
|
68
|
+
"BB_LOWER" => "DhanHQ::Constants::IndicatorName::BB_LOWER",
|
|
69
|
+
"RSI_14" => "DhanHQ::Constants::IndicatorName::RSI_14",
|
|
70
|
+
"ATR_14" => "DhanHQ::Constants::IndicatorName::ATR_14",
|
|
71
|
+
"STOCHASTIC" => "DhanHQ::Constants::IndicatorName::STOCHASTIC",
|
|
72
|
+
"STOCHRSI_14" => "DhanHQ::Constants::IndicatorName::STOCHRSI_14",
|
|
73
|
+
"MACD_26" => "DhanHQ::Constants::IndicatorName::MACD_26",
|
|
74
|
+
"MACD_12" => "DhanHQ::Constants::IndicatorName::MACD_12",
|
|
75
|
+
"MACD_HIST" => "DhanHQ::Constants::IndicatorName::MACD_HIST",
|
|
76
|
+
"SMA_5" => "DhanHQ::Constants::IndicatorName::SMA_5",
|
|
77
|
+
"SMA_10" => "DhanHQ::Constants::IndicatorName::SMA_10",
|
|
78
|
+
"SMA_20" => "DhanHQ::Constants::IndicatorName::SMA_20",
|
|
79
|
+
"SMA_50" => "DhanHQ::Constants::IndicatorName::SMA_50",
|
|
80
|
+
"SMA_100" => "DhanHQ::Constants::IndicatorName::SMA_100",
|
|
81
|
+
"SMA_200" => "DhanHQ::Constants::IndicatorName::SMA_200",
|
|
82
|
+
"EMA_5" => "DhanHQ::Constants::IndicatorName::EMA_5",
|
|
83
|
+
"EMA_10" => "DhanHQ::Constants::IndicatorName::EMA_10",
|
|
84
|
+
"EMA_20" => "DhanHQ::Constants::IndicatorName::EMA_20",
|
|
85
|
+
"EMA_50" => "DhanHQ::Constants::IndicatorName::EMA_50",
|
|
86
|
+
"EMA_100" => "DhanHQ::Constants::IndicatorName::EMA_100",
|
|
87
|
+
"GREATER_THAN_EQUAL" => "DhanHQ::Constants::Operator::GREATER_THAN_EQUAL",
|
|
88
|
+
"LESS_THAN_EQUAL" => "DhanHQ::Constants::Operator::LESS_THAN_EQUAL",
|
|
89
|
+
"EQUAL" => "DhanHQ::Constants::Operator::EQUAL",
|
|
90
|
+
"NOT_EQUAL" => "DhanHQ::Constants::Operator::NOT_EQUAL",
|
|
91
|
+
"CROSSING_UP" => "DhanHQ::Constants::Operator::CROSSING_UP",
|
|
92
|
+
"CROSSING_DOWN" => "DhanHQ::Constants::Operator::CROSSING_DOWN",
|
|
93
|
+
"CROSSING_ANY_SIDE" => "DhanHQ::Constants::Operator::CROSSING_ANY_SIDE",
|
|
94
|
+
"GREATER_THAN" => "DhanHQ::Constants::Operator::GREATER_THAN",
|
|
95
|
+
"LESS_THAN" => "DhanHQ::Constants::Operator::LESS_THAN",
|
|
96
|
+
"ACTIVE" => "DhanHQ::Constants::TriggerStatus::ACTIVE",
|
|
97
|
+
"IDX_I" => "DhanHQ::Constants::ExchangeSegment::IDX_I",
|
|
98
|
+
"NSE_EQ" => "DhanHQ::Constants::ExchangeSegment::NSE_EQ",
|
|
99
|
+
"NSE_FNO" => "DhanHQ::Constants::ExchangeSegment::NSE_FNO",
|
|
100
|
+
"NSE_CURRENCY" => "DhanHQ::Constants::ExchangeSegment::NSE_CURRENCY",
|
|
101
|
+
"NSE_COMM" => "DhanHQ::Constants::ExchangeSegment::NSE_COMM",
|
|
102
|
+
"BSE_EQ" => "DhanHQ::Constants::ExchangeSegment::BSE_EQ",
|
|
103
|
+
"MCX_COMM" => "DhanHQ::Constants::ExchangeSegment::MCX_COMM",
|
|
104
|
+
"BSE_CURRENCY" => "DhanHQ::Constants::ExchangeSegment::BSE_CURRENCY",
|
|
105
|
+
"BSE_FNO" => "DhanHQ::Constants::ExchangeSegment::BSE_FNO",
|
|
106
|
+
"DH-910" => "DhanHQ::Constants::TradingErrorCode::OTHERS",
|
|
107
|
+
"DH-901" => "DhanHQ::Constants::TradingErrorCode::INVALID_AUTHENTICATION",
|
|
108
|
+
"DH-902" => "DhanHQ::Constants::TradingErrorCode::INVALID_ACCESS",
|
|
109
|
+
"DH-903" => "DhanHQ::Constants::TradingErrorCode::USER_ACCOUNT",
|
|
110
|
+
"DH-904" => "DhanHQ::Constants::TradingErrorCode::RATE_LIMIT",
|
|
111
|
+
"DH-905" => "DhanHQ::Constants::TradingErrorCode::INPUT_EXCEPTION",
|
|
112
|
+
"DH-906" => "DhanHQ::Constants::TradingErrorCode::ORDER_ERROR",
|
|
113
|
+
"DH-907" => "DhanHQ::Constants::TradingErrorCode::DATA_ERROR",
|
|
114
|
+
"DH-908" => "DhanHQ::Constants::TradingErrorCode::INTERNAL_SERVER_ERROR",
|
|
115
|
+
"DH-909" => "DhanHQ::Constants::TradingErrorCode::NETWORK_ERROR",
|
|
116
|
+
"CNC" => "DhanHQ::Constants::ProductType::CNC",
|
|
117
|
+
"INTRADAY" => "DhanHQ::Constants::ProductType::INTRADAY",
|
|
118
|
+
"MARGIN" => "DhanHQ::Constants::ProductType::MARGIN",
|
|
119
|
+
"MTF" => "DhanHQ::Constants::ProductType::MTF",
|
|
120
|
+
"CO" => "DhanHQ::Constants::ProductType::CO",
|
|
121
|
+
"BO" => "DhanHQ::Constants::ProductType::BO"
|
|
122
|
+
}.freeze
|
|
123
|
+
|
|
124
|
+
# Ambiguous constants — only flagged when the string is the VALUE side of a hash pair.
|
|
125
|
+
# These strings also appear in natural language contexts (error messages, log output).
|
|
126
|
+
AMBIGUOUS_CONSTANTS = {
|
|
127
|
+
"OPEN" => "DhanHQ::Constants::AmoTime::OPEN",
|
|
128
|
+
"INDEX" => "DhanHQ::Constants::InstrumentType::INDEX"
|
|
129
|
+
}.freeze
|
|
130
|
+
|
|
131
|
+
def on_str(node)
|
|
132
|
+
value = node.value
|
|
133
|
+
return unless value.length >= 2
|
|
134
|
+
return if value.include?(" ")
|
|
135
|
+
|
|
136
|
+
parent = node.parent
|
|
137
|
+
return unless parent
|
|
138
|
+
|
|
139
|
+
# Skip hash keys
|
|
140
|
+
return if parent.pair_type? && parent.key == node
|
|
141
|
+
|
|
142
|
+
# Skip typical ignored methods
|
|
143
|
+
return if parent.send_type? && %i[require require_relative puts print warn raise fail
|
|
144
|
+
class_eval instance_eval].include?(parent.method_name)
|
|
145
|
+
|
|
146
|
+
# Skip %w[] / %i[] array literals
|
|
147
|
+
in_percent_array = parent.array_type? && parent.loc.begin&.source&.start_with?("%w", "%W", "%i", "%I")
|
|
148
|
+
return if in_percent_array
|
|
149
|
+
|
|
150
|
+
# Check unambiguous constants first
|
|
151
|
+
if CONSTANTS_MAP.key?(value)
|
|
152
|
+
constant_path = CONSTANTS_MAP[value]
|
|
153
|
+
add_offense(node, message: format(MSG, constant: constant_path, string: value)) do |corrector|
|
|
154
|
+
corrector.replace(node, constant_path)
|
|
155
|
+
end
|
|
156
|
+
return
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Ambiguous constants — only flag when the node is the value side of a hash pair
|
|
160
|
+
return unless AMBIGUOUS_CONSTANTS.key?(value)
|
|
161
|
+
return unless parent.pair_type? && parent.value == node
|
|
162
|
+
|
|
163
|
+
constant_path = AMBIGUOUS_CONSTANTS[value]
|
|
164
|
+
add_offense(node, message: format(MSG, constant: constant_path, string: value)) do |corrector|
|
|
165
|
+
corrector.replace(node, constant_path)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: DhanHQ
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shubham Taywade
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -178,7 +178,9 @@ dependencies:
|
|
|
178
178
|
- - ">="
|
|
179
179
|
- !ruby/object:Gem::Version
|
|
180
180
|
version: '0'
|
|
181
|
-
description:
|
|
181
|
+
description: A pure-Ruby client wrapping the DhanHQ v2 REST and WebSocket API. Provides
|
|
182
|
+
typed model classes, dry-validation contracts, a token-bucket rate limiter, and
|
|
183
|
+
WebSocket streaming for live market data and order updates.
|
|
182
184
|
email:
|
|
183
185
|
- shubhamtaywade82@gmail.com
|
|
184
186
|
executables:
|
|
@@ -200,40 +202,29 @@ files:
|
|
|
200
202
|
- REVIEW_SUMMARY.md
|
|
201
203
|
- Rakefile
|
|
202
204
|
- TAGS
|
|
203
|
-
- TODO-1.md
|
|
204
|
-
- TODO.md
|
|
205
205
|
- VERSION_UPDATE.md
|
|
206
|
-
- app/services/live/order_update_guard_support.rb
|
|
207
|
-
- app/services/live/order_update_hub.rb
|
|
208
|
-
- app/services/live/order_update_persistence_support.rb
|
|
209
206
|
- config/initializers/order_update_hub.rb
|
|
210
207
|
- core
|
|
211
208
|
- diagram.html
|
|
212
209
|
- diagram.md
|
|
210
|
+
- docs/API_DOCS_GAPS.md
|
|
213
211
|
- docs/API_VERIFICATION.md
|
|
214
212
|
- docs/ARCHIVE_README.md
|
|
215
213
|
- docs/AUTHENTICATION.md
|
|
216
214
|
- docs/CONFIGURATION.md
|
|
215
|
+
- docs/CONSTANTS_REFERENCE.md
|
|
217
216
|
- docs/DATA_API_PARAMETERS.md
|
|
218
|
-
- docs/
|
|
217
|
+
- docs/LIVE_ORDER_UPDATES.md
|
|
218
|
+
- docs/RAILS_INTEGRATION.md
|
|
219
|
+
- docs/RAILS_WEBSOCKET_INTEGRATION.md
|
|
219
220
|
- docs/RELEASE_GUIDE.md
|
|
221
|
+
- docs/STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md
|
|
220
222
|
- docs/SUPER_ORDERS.md
|
|
223
|
+
- docs/TECHNICAL_ANALYSIS.md
|
|
221
224
|
- docs/TESTING_GUIDE.md
|
|
222
225
|
- docs/TROUBLESHOOTING.md
|
|
226
|
+
- docs/WEBSOCKET_INTEGRATION.md
|
|
223
227
|
- docs/WEBSOCKET_PROTOCOL.md
|
|
224
|
-
- docs/live_order_updates.md
|
|
225
|
-
- docs/rails_integration.md
|
|
226
|
-
- docs/rails_websocket_integration.md
|
|
227
|
-
- docs/standalone_ruby_websocket_integration.md
|
|
228
|
-
- docs/technical_analysis.md
|
|
229
|
-
- docs/websocket_integration.md
|
|
230
|
-
- examples/comprehensive_websocket_examples.rb
|
|
231
|
-
- examples/instrument_finder_test.rb
|
|
232
|
-
- examples/live_order_updates.rb
|
|
233
|
-
- examples/market_depth_example.rb
|
|
234
|
-
- examples/market_feed_example.rb
|
|
235
|
-
- examples/order_update_example.rb
|
|
236
|
-
- examples/trading_fields_example.rb
|
|
237
228
|
- exe/DhanHQ
|
|
238
229
|
- lib/DhanHQ/auth.rb
|
|
239
230
|
- lib/DhanHQ/auth/token_generator.rb
|
|
@@ -244,19 +235,23 @@ files:
|
|
|
244
235
|
- lib/DhanHQ/constants.rb
|
|
245
236
|
- lib/DhanHQ/contracts/alert_order_contract.rb
|
|
246
237
|
- lib/DhanHQ/contracts/base_contract.rb
|
|
238
|
+
- lib/DhanHQ/contracts/edis_contract.rb
|
|
247
239
|
- lib/DhanHQ/contracts/expired_options_data_contract.rb
|
|
248
240
|
- lib/DhanHQ/contracts/historical_data_contract.rb
|
|
249
241
|
- lib/DhanHQ/contracts/instrument_list_contract.rb
|
|
250
242
|
- lib/DhanHQ/contracts/margin_calculator_contract.rb
|
|
251
243
|
- lib/DhanHQ/contracts/modify_order_contract.rb
|
|
244
|
+
- lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb
|
|
252
245
|
- lib/DhanHQ/contracts/option_chain_contract.rb
|
|
253
246
|
- lib/DhanHQ/contracts/order_contract.rb
|
|
254
247
|
- lib/DhanHQ/contracts/place_order_contract.rb
|
|
248
|
+
- lib/DhanHQ/contracts/pnl_based_exit_contract.rb
|
|
255
249
|
- lib/DhanHQ/contracts/position_conversion_contract.rb
|
|
256
250
|
- lib/DhanHQ/contracts/slice_order_contract.rb
|
|
257
251
|
- lib/DhanHQ/contracts/trade_by_order_id_contract.rb
|
|
258
252
|
- lib/DhanHQ/contracts/trade_contract.rb
|
|
259
253
|
- lib/DhanHQ/contracts/trade_history_contract.rb
|
|
254
|
+
- lib/DhanHQ/contracts/user_ip_contract.rb
|
|
260
255
|
- lib/DhanHQ/core/auth_api.rb
|
|
261
256
|
- lib/DhanHQ/core/base_api.rb
|
|
262
257
|
- lib/DhanHQ/core/base_model.rb
|
|
@@ -272,6 +267,7 @@ files:
|
|
|
272
267
|
- lib/DhanHQ/helpers/validation_helper.rb
|
|
273
268
|
- lib/DhanHQ/json_loader.rb
|
|
274
269
|
- lib/DhanHQ/models/alert_order.rb
|
|
270
|
+
- lib/DhanHQ/models/concerns/api_response_handler.rb
|
|
275
271
|
- lib/DhanHQ/models/edis.rb
|
|
276
272
|
- lib/DhanHQ/models/expired_options_data.rb
|
|
277
273
|
- lib/DhanHQ/models/forever_order.rb
|
|
@@ -354,6 +350,7 @@ files:
|
|
|
354
350
|
- lib/dhanhq/analysis/multi_timeframe_analyzer.rb
|
|
355
351
|
- lib/dhanhq/analysis/options_buying_advisor.rb
|
|
356
352
|
- lib/dhanhq/contracts/options_buying_advisor_contract.rb
|
|
353
|
+
- lib/rubocop/cop/dhanhq/use_constants.rb
|
|
357
354
|
- lib/ta.rb
|
|
358
355
|
- lib/ta/candles.rb
|
|
359
356
|
- lib/ta/fetcher.rb
|
|
@@ -389,5 +386,5 @@ requirements: []
|
|
|
389
386
|
rubygems_version: 3.5.11
|
|
390
387
|
signing_key:
|
|
391
388
|
specification_version: 4
|
|
392
|
-
summary:
|
|
389
|
+
summary: Ruby client for the DhanHQ v2 REST and WebSocket API (NSE/BSE).
|
|
393
390
|
test_files: []
|
data/TODO-1.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# TODO List
|
|
2
|
-
|
|
3
|
-
- [x] Wire `DhanHQ::Models::Order.place` to the resource’s `create` endpoint so order placement stops raising `NoMethodError` (`lib/DhanHQ/models/order.rb:73`, `lib/DhanHQ/resources/orders.rb:14`).
|
|
4
|
-
- [x] Align `Order#cancel` with `DhanHQ::Resources::Orders#cancel` to restore cancellation support (`lib/DhanHQ/models/order.rb:120`, `lib/DhanHQ/resources/orders.rb:26`).
|
|
5
|
-
- [x] Rework `Order#modify` to send a proper payload and capture the response instead of delegating to the broken generic update flow (`lib/DhanHQ/models/order.rb:99`, `lib/DhanHQ/core/base_model.rb:155`).
|
|
6
|
-
- [x] Repair or replace the shared CRUD helpers in `BaseModel` so URL construction and response handling behave (`lib/DhanHQ/core/base_model.rb:120`, `lib/DhanHQ/core/base_model.rb:129`, `lib/DhanHQ/core/base_model.rb:155`).
|
|
7
|
-
- [x] Make `BaseModel#save!` raise a real exception type (e.g. `DhanHQ::Error`) to avoid the current `TypeError` (`lib/DhanHQ/core/base_model.rb:165`).
|
|
8
|
-
- [x] Strip read-only attributes before posting modify requests to pass API validation (`lib/DhanHQ/models/order.rb:166`, `lib/DhanHQ/core/base_model.rb:197`).
|
|
9
|
-
- [x] Require `fileutils` in the WebSocket singleton lock so acquiring the lock no longer raises (`lib/DhanHQ/ws/singleton_lock.rb:11`).
|
|
10
|
-
- [x] Implement EDIS and kill-switch endpoints surfaced in the OpenAPI spec so the client can call `/edis/bulkform`, `/edis/form`, `/edis/inquire/{isin}`, `/edis/tpin`, and `/killswitch` (`/home/nemesis/dhanhq-bundled.json:827`, `/home/nemesis/dhanhq-bundled.json:873`, `/home/nemesis/dhanhq-bundled.json:949`).
|
|
11
|
-
- [x] Correct `ForeverOrders#all` to hit `/v2/forever/orders` instead of the undocumented `/v2/forever/all` path (`lib/DhanHQ/resources/forever_orders.rb:9`, `/home/nemesis/dhanhq-bundled.json:578`).
|
|
12
|
-
- [x] Run the existing `MarginCalculatorContract` before posting to `/margincalculator` so required fields like `transactionType` and `productType` are enforced client-side (`lib/DhanHQ/models/margin.rb:23`, `lib/DhanHQ/contracts/margin_calculator_contract.rb:5`).
|
|
13
|
-
- [ ] Validate slice-order payloads with `SliceOrderContract` to uphold STOP_LOSS requirements before calling `/orders/slicing` (`lib/DhanHQ/models/order.rb:217`, `lib/DhanHQ/contracts/slice_order_contract.rb:30`, `/home/nemesis/dhanhq-bundled.json:234`).
|
|
14
|
-
- [x] Add a contract-backed validation for `Position.convert` so `PositionConversionRequest` fields like `fromProductType` and `convertQty` are checked prior to hitting `/positions/convert` (`lib/DhanHQ/models/position.rb:39`, `/home/nemesis/dhanhq-bundled.json:1391`).
|
data/TODO.md
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
Below are several suggestions and improvements you can consider to further enhance your gem’s structure, error handling, validations, and overall design:
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
### **1. Decouple HTTP/Resource Handling from Models**
|
|
6
|
-
|
|
7
|
-
- **Inject the API instance:**
|
|
8
|
-
Instead of having your BaseModel inherit from or tightly couple to BaseAPI, you already moved to instantiating a shared API object via the `api` method. This is good for testing and future flexibility. Consider allowing dependency injection so that in tests you can supply a mock client.
|
|
9
|
-
|
|
10
|
-
- **Separate Resource Objects:**
|
|
11
|
-
Create separate “Resource” classes for each endpoint (e.g. Orders, Funds, MarketFeed, etc.) that are solely responsible for forming the correct URL and HTTP call. Then, your models (Order, Funds, etc.) become thin wrappers on top of these resources.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
### **2. Improved Error Handling**
|
|
16
|
-
|
|
17
|
-
- **Consistent Error Hierarchy:**
|
|
18
|
-
You already have a structured set of custom errors. Ensure that all API responses are wrapped in a uniform error response. For instance, for network errors, consider implementing a retry mechanism for transient errors (such as timeouts or 429 responses).
|
|
19
|
-
|
|
20
|
-
- **Retry Mechanism:**
|
|
21
|
-
Enhance your Client by adding an optional retry strategy (with exponential backoff) for cases when a request fails due to rate limiting or temporary network issues.
|
|
22
|
-
|
|
23
|
-
- **Detailed Logging:**
|
|
24
|
-
Enable more granular logging (perhaps controlled via configuration) to help troubleshoot errors without exposing sensitive information.
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
### **3. Enhanced Validations**
|
|
29
|
-
|
|
30
|
-
- **Centralize Contracts:**
|
|
31
|
-
Use your `BaseContract` as a foundation for all contracts so that common rules and messages are shared. Consider adding custom predicates if you need more complex business rules.
|
|
32
|
-
For example, you can write a custom predicate for “valid_order_quantity” that could check against exchange limits.
|
|
33
|
-
|
|
34
|
-
- **Error Messages & Localization:**
|
|
35
|
-
Consider standardizing error messages across contracts so that errors returned by your gem are predictable. If needed, use a localization mechanism to map raw error keys to user-friendly messages.
|
|
36
|
-
|
|
37
|
-
- **Use Dry-Struct (Optional):**
|
|
38
|
-
If you want stronger typing and immutability for your models, you might consider using [dry-struct](https://dry-rb.org/gems/dry-struct/) in combination with dry-validation. This can help enforce attribute types and reduce runtime errors.
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
### **4. Model Improvements and Convenience Methods**
|
|
43
|
-
|
|
44
|
-
- **Dynamic Attribute Getters/Setters:**
|
|
45
|
-
Your current approach to dynamically assign attribute getters is good. You might consider also generating setters if you want to allow updating the local object state before pushing an update to the API.
|
|
46
|
-
|
|
47
|
-
- **CRUD Methods Consistency:**
|
|
48
|
-
Ensure that your instance methods like `update`, `delete`, `refresh`, etc., always return either a new instance of the model (with updated values) or a well-formed error object. This will make it easier for users to chain operations.
|
|
49
|
-
|
|
50
|
-
- **Merge Updated Attributes Correctly:**
|
|
51
|
-
In your `modify` method (for orders, for instance), make sure you correctly merge the existing attributes with the new ones before issuing the PUT request. Currently, there’s a commented line and then an immediate call to `update(attributes)`—this should be updated to use the merged `updated_params`.
|
|
52
|
-
|
|
53
|
-
- **Caching or Memoization:**
|
|
54
|
-
If your API endpoints don’t change frequently (e.g., for retrieving configuration or instruments), consider caching responses to minimize API calls.
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
### **5. Testing & VCR**
|
|
59
|
-
|
|
60
|
-
- **VCR Cassette Management:**
|
|
61
|
-
Ensure that your VCR cassettes capture both successful and error responses. When you need to simulate scenarios (e.g., update order, cancellation), manually edit the cassette files to reflect those states if the real API cannot produce them reliably.
|
|
62
|
-
|
|
63
|
-
- **Spec Coverage:**
|
|
64
|
-
Write comprehensive specs for each model that exercises both the “happy path” and error cases. For instance, ensure that Order.create returns an order with the proper attributes, and that Order.update merges new attributes as expected.
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
### **6. Configuration Improvements**
|
|
69
|
-
|
|
70
|
-
- **Global Configuration Object:**
|
|
71
|
-
Continue iterating on the existing configuration helpers while keeping `DhanHQ.configure_with_env` as the primary entrypoint. Provide any additional toggles by reading from ENV so docs can focus on the single bootstrap path.
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
### **7. Overall Architecture and Documentation**
|
|
76
|
-
|
|
77
|
-
- **Document Model Methods:**
|
|
78
|
-
Ensure that each model (Order, Funds, OptionChain, etc.) is well documented. Explain the expected inputs/outputs and any side effects.
|
|
79
|
-
|
|
80
|
-
- **Separation of Concerns:**
|
|
81
|
-
Keep your gem’s responsibilities clear:
|
|
82
|
-
|
|
83
|
-
- **Client:** Low-level HTTP calls with error handling and rate limiting.
|
|
84
|
-
- **Resources:** Form URLs and endpoint-specific logic.
|
|
85
|
-
- **Models:** Map resource data to business objects and provide CRUD operations with validation.
|
|
86
|
-
- **Contracts:** Define dry-validation contracts for input validation.
|
|
87
|
-
|
|
88
|
-
- **Extensibility:**
|
|
89
|
-
Consider ways to allow users to extend models or override default behavior. For instance, provide hooks (callbacks) before or after an update or create operation.
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
### **Example of a Revised Modify Method in Order Model**
|
|
94
|
-
|
|
95
|
-
Here’s a small snippet that shows how you might update the `modify` method in the Order model to merge attributes properly:
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
def modify(new_params)
|
|
99
|
-
raise "Order ID is required to modify an order" unless id
|
|
100
|
-
|
|
101
|
-
# Merge current attributes with new ones
|
|
102
|
-
updated_params = attributes.merge(new_params)
|
|
103
|
-
validate_params!(updated_params, DhanHQ::Contracts::ModifyOrderContract)
|
|
104
|
-
|
|
105
|
-
# Perform the PUT request with merged parameters
|
|
106
|
-
response = self.class.api.put("#{self.class.resource_path}/#{id}", params: updated_params)
|
|
107
|
-
|
|
108
|
-
# If the response indicates a transitional status (e.g., "TRANSIT"), re-fetch the order
|
|
109
|
-
if success_response?(response) && response[:orderStatus] == "TRANSIT"
|
|
110
|
-
return self.class.find(id)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
DhanHQ::ErrorObject.new(response)
|
|
114
|
-
end
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
### **Conclusion**
|
|
120
|
-
|
|
121
|
-
Implementing these improvements will result in a more robust, testable, and maintainable gem. Enhancing error handling, validations, and separation of concerns not only eases future modifications but also improves the overall developer experience when using the gem.
|
|
122
|
-
|
|
123
|
-
Feel free to ask if you’d like more details or examples on any of these suggestions!
|
|
124
|
-
|
|
125
|
-
PROGRESS:
|
|
126
|
-
|
|
127
|
-
1. OptionChain working
|