Class: DhanHQ::Models::Position

Inherits:
BaseModel show all
Defined in:
lib/DhanHQ/models/position.rb

Overview

Model for managing intraday and carry-forward positions.

The Positions API lets you retrieve a list of all open positions for the day. This includes all F&O carryforward positions as well. You can also convert positions between product types (e.g., intraday to delivery or vice versa).

Examples:

Fetch all positions

positions = DhanHQ::Models::Position.all
positions.each do |position|
  puts "#{position.trading_symbol}: #{position.net_qty} @ ₹#{position.buy_avg}"
  puts "Unrealized P&L: ₹#{position.unrealized_profit}"
end

Fetch only active (open) positions

open_positions = DhanHQ::Models::Position.active
long_positions = open_positions.select { |p| p.position_type == "LONG" }
puts "Open long positions: #{long_positions.count}"

Convert intraday position to delivery

response = DhanHQ::Models::Position.convert(
  dhan_client_id: "1000000009",
  from_product_type: "INTRADAY",
  to_product_type: "CNC",
  exchange_segment: "NSE_EQ",
  position_type: "LONG",
  security_id: "11536",
  convert_qty: 40
)

Constant Summary collapse

HTTP_PATH =

Base path used by the positions resource.

"/v2/positions"

Constants included from ResponseHelper

ResponseHelper::STATUS_ERROR_FALLBACK

Instance Attribute Summary

Attributes inherited from BaseModel

#attributes, #errors

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseModel

api, api_type, #assign_attributes, attributes, create, #delete, #destroy, find, #id, #initialize, #new_record?, #optionchain_api?, parse_collection_response, #persisted?, resource_path, #save, #save!, #to_request_params, #update, #valid?, validate_attributes, validation_contract, #validation_contract, where

Methods included from APIHelper

#handle_response

Methods included from AttributeHelper

#camelize_keys, #inspect, #normalize_keys, #snake_case, #titleize_keys

Methods included from ValidationHelper

#valid?, #validate!, #validate_params!

Methods included from RequestHelper

#build_from_response

Constructor Details

This class inherits a constructor from DhanHQ::BaseModel

Class Method Details

.activeArray<Position>

Filters the position list to return only active (open) positions.

Removes positions with "CLOSED" status, returning only positions that are currently open (LONG or SHORT positions).

Examples:

Fetch only open positions

open_positions = DhanHQ::Models::Position.active
puts "Open positions: #{open_positions.count}"
open_positions.each do |position|
  puts "#{position.trading_symbol}: #{position.net_qty} (#{position.position_type})"
end

Calculate total open position value

open_positions = DhanHQ::Models::Position.active
total_value = open_positions.sum { |p| p.net_qty * p.buy_avg }
puts "Total open position value: ₹#{total_value}"

Returns:

  • (Array<Position>)

    Array of active Position objects. Excludes positions with position_type "CLOSED"



207
208
209
# File 'lib/DhanHQ/models/position.rb', line 207

def active
  all.reject { |position| position.position_type == DhanHQ::Constants::OrderStatus::CLOSED }
end

.allArray<Position>

Retrieves all positions for the current trading day.

Fetches a list of all open positions including F&O carryforward positions. Returns both active (LONG/SHORT) and closed positions. Use active if you only need open positions.

Examples:

Fetch and analyze positions

positions = DhanHQ::Models::Position.all
total_unrealized = positions.sum(&:unrealized_profit)
total_realized = positions.sum(&:realized_profit)
puts "Total Unrealized P&L: ₹#{total_unrealized}"
puts "Total Realized P&L: ₹#{total_realized}"

Analyze F&O positions

positions = DhanHQ::Models::Position.all
fno_positions = positions.select { |p| p.exchange_segment.include?("FNO") }
fno_positions.each do |pos|
  puts "#{pos.trading_symbol} - Net: #{pos.net_qty}, P&L: ₹#{pos.unrealized_profit}"
end

Returns:

  • (Array<Position>)

    Array of Position objects. Returns empty array if no positions exist. Each Position object contains (keys normalized to snake_case):

    • :dhan_client_id [String] User-specific identification generated by Dhan
    • :trading_symbol [String] Trading symbol of the instrument
    • :security_id [String] Exchange standard ID for each scrip
    • :position_type [String] Position type. Valid values: "LONG", "SHORT", "CLOSED"
    • :exchange_segment [String] Exchange and segment. Valid values: "NSE_EQ", "NSE_FNO", "NSE_CURRENCY", "BSE_EQ", "BSE_FNO", "BSE_CURRENCY", "MCX_COMM"
    • :product_type [String] Product type. Valid values: "CNC", "INTRADAY", "MARGIN", "MTF", "CO", "BO"
    • :buy_avg [Float] Average buy price mark to market
    • :buy_qty [Integer] Total quantity bought
    • :cost_price [Float] Actual cost price
    • :sell_avg [Float] Average sell price mark to market
    • :sell_qty [Integer] Total quantities sold
    • :net_qty [Integer] Net quantity (buy_qty - sell_qty)
    • :realized_profit [Float] Profit or loss booked
    • :unrealized_profit [Float] Profit or loss standing for open position
    • :rbi_reference_rate [Float] RBI mandated reference rate for forex
    • :multiplier [Integer] Multiplying factor for currency F&O
    • :carry_forward_buy_qty [Integer] Carry forward F&O long quantities
    • :carry_forward_sell_qty [Integer] Carry forward F&O short quantities
    • :carry_forward_buy_value [Float] Carry forward F&O long value
    • :carry_forward_sell_value [Float] Carry forward F&O short value
    • :day_buy_qty [Integer] Quantities bought today
    • :day_sell_qty [Integer] Quantities sold today
    • :day_buy_value [Float] Value of quantities bought today
    • :day_sell_value [Float] Value of quantities sold today
    • :drv_expiry_date [String] For F&O, expiry date of contract (format: "YYYY-MM-DD")
    • :drv_option_type [String, nil] Type of Option. "CALL" or "PUT"
    • :drv_strike_price [Float] For Options, Strike Price
    • :cross_currency [Boolean] Check for non INR currency pair


177
178
179
180
181
182
183
184
# File 'lib/DhanHQ/models/position.rb', line 177

def all
  response = resource.all
  return [] unless response.is_a?(Array)

  response.map do |position|
    new(snake_case(position), skip_validation: true)
  end
end

.convert(params) ⇒ Hash, DhanHQ::ErrorObject

Note:

The API returns HTTP 202 Accepted on successful conversion

Converts an existing position from one product type to another.

Allows conversion between eligible product types, most commonly between intraday (INTRADAY) and delivery (CNC) positions. This is useful when you want to hold an intraday position overnight or convert a delivery position to intraday.

Examples:

Convert intraday position to delivery

response = DhanHQ::Models::Position.convert(
  dhan_client_id: "1000000009",
  from_product_type: "INTRADAY",
  to_product_type: "CNC",
  exchange_segment: "NSE_EQ",
  position_type: "LONG",
  security_id: "11536",
  convert_qty: 40
)
if response.is_a?(DhanHQ::ErrorObject)
  puts "Conversion failed: #{response.errors}"
else
  puts "Position converted successfully"
end

Convert delivery position to intraday

response = DhanHQ::Models::Position.convert(
  dhan_client_id: "1000000009",
  from_product_type: "CNC",
  to_product_type: "INTRADAY",
  exchange_segment: "NSE_EQ",
  position_type: "LONG",
  security_id: "11536",
  convert_qty: 10
)

Parameters:

  • params (Hash{Symbol => String, Integer})

    Position conversion parameters @option params [String] :dhan_client_id (required) User-specific identification generated by Dhan. Must be explicitly provided in the params hash @option params [String] :from_product_type (required) Current product type of the position. Valid values: "CNC", "INTRADAY", "MARGIN", "CO", "BO" @option params [String] :to_product_type (required) Desired product type after conversion. Valid values: "CNC", "INTRADAY", "MARGIN", "CO", "BO" Must be different from from_product_type @option params [String] :exchange_segment (required) Exchange and segment in which position is created. Valid values: See Constants::EXCHANGE_SEGMENTS @option params [String] :position_type (required) Position type to convert. Valid values: "LONG", "SHORT", "CLOSED" @option params [String] :security_id (required) Exchange standard ID for each scrip @option params [String] :trading_symbol (optional) Trading symbol of the instrument @option params [Integer] :convert_qty (required) Number of shares for which conversion is desired. Must be greater than 0

Returns:

  • (Hash, DhanHQ::ErrorObject)

    API response hash (typically HTTP 202 Accepted) on success, ErrorObject on failure

Raises:



267
268
269
270
271
272
273
# File 'lib/DhanHQ/models/position.rb', line 267

def convert(params)
  formatted_params = camelize_keys(params)
  validate_params!(formatted_params, DhanHQ::Contracts::PositionConversionContract)

  response = resource.convert(formatted_params)
  success_response?(response) ? response : DhanHQ::ErrorObject.new(response)
end

.exit_all!Hash{Symbol => String}

Exits all active positions and cancels all open orders for the current trading day.

This is a safety endpoint for emergency position closure. It sends a DELETE request to close all positions and cancel all pending orders in one call.

Examples:

Emergency exit all positions

response = DhanHQ::Models::Position.exit_all!
if response[:status] == "SUCCESS"
  puts "✓ All positions exited and orders cancelled"
else
  puts "✗ Failed: #{response[:message]}"
end

Returns:

  • (Hash{Symbol => String})

    Response hash containing operation result.

    • :status [String] "SUCCESS" or "ERROR"
    • :message [String] Description of the result


293
294
295
# File 'lib/DhanHQ/models/position.rb', line 293

def exit_all!
  resource.exit_all
end

.resourceDhanHQ::Resources::Positions

Provides a shared instance of the Positions resource.

Returns:



118
119
120
# File 'lib/DhanHQ/models/position.rb', line 118

def resource
  @resource ||= DhanHQ::Resources::Positions.new
end

Instance Method Details

#closed?Boolean

Returns true if this position is closed.

Returns:

  • (Boolean)


74
75
76
# File 'lib/DhanHQ/models/position.rb', line 74

def closed?
  position_type == DhanHQ::Constants::OrderStatus::CLOSED || net_qty.to_i.zero?
end

#derivatives?Boolean

Returns true if this is an F&O position.

Returns:

  • (Boolean)


99
100
101
# File 'lib/DhanHQ/models/position.rb', line 99

def derivatives?
  drv_expiry_date.present? || drv_strike_price.to_f.positive?
end

#futures?Boolean

Returns true if this is a futures position.

Returns:

  • (Boolean)


109
110
111
# File 'lib/DhanHQ/models/position.rb', line 109

def futures?
  derivatives? && drv_option_type.nil?
end

#in_loss?Boolean

Returns true if this position is in loss (unrealized).

Returns:

  • (Boolean)


89
90
91
# File 'lib/DhanHQ/models/position.rb', line 89

def in_loss?
  unrealized_profit.to_f.negative?
end

#long?Boolean

Returns true if this is a long position.

Returns:

  • (Boolean)


64
65
66
# File 'lib/DhanHQ/models/position.rb', line 64

def long?
  position_type == DhanHQ::Constants::PositionType::LONG
end

#open?Boolean

Returns true if this position has an open quantity.

Returns:

  • (Boolean)


79
80
81
# File 'lib/DhanHQ/models/position.rb', line 79

def open?
  !closed? && net_qty.to_i != 0
end

#options?Boolean

Returns true if this is an options position.

Returns:

  • (Boolean)


104
105
106
# File 'lib/DhanHQ/models/position.rb', line 104

def options?
  derivatives? && drv_option_type.present?
end

#profitable?Boolean

Returns true if this position is profitable (unrealized).

Returns:

  • (Boolean)


84
85
86
# File 'lib/DhanHQ/models/position.rb', line 84

def profitable?
  unrealized_profit.to_f.positive?
end

#short?Boolean

Returns true if this is a short position.

Returns:

  • (Boolean)


69
70
71
# File 'lib/DhanHQ/models/position.rb', line 69

def short?
  position_type == DhanHQ::Constants::PositionType::SHORT
end

#to_promptObject

Returns a concise prompt-friendly summary of the position.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/DhanHQ/models/position.rb', line 48

def to_prompt
  parts = [
    "#{position_type} #{net_qty}x #{trading_symbol || security_id}",
    "on #{exchange_segment}/#{product_type}"
  ]
  parts << "buy_avg=#{buy_avg}" if buy_avg&.positive?
  parts << "sell_avg=#{sell_avg}" if sell_avg&.positive?
  parts << "realized_pnl=#{realized_profit}" if realized_profit
  parts << "unrealized_pnl=#{unrealized_profit}" if unrealized_profit
  parts << "expiry=#{drv_expiry_date}" if drv_expiry_date
  parts << "strike=#{drv_strike_price}" if drv_strike_price&.positive?
  parts << "type=#{drv_option_type}" if drv_option_type
  parts.join(", ")
end

#total_pnlObject

Returns the total unrealized P&L including realized.



94
95
96
# File 'lib/DhanHQ/models/position.rb', line 94

def total_pnl
  realized_profit.to_f + unrealized_profit.to_f
end