Class: DhanHQ::Risk::PositionSizer

Inherits:
Object
  • Object
show all
Defined in:
lib/DhanHQ/risk.rb

Overview

Calculate optimal position size based on risk parameters.

Supports multiple sizing methods:

  • Fixed risk percentage of account
  • Kelly criterion
  • Volatility-based sizing

Examples:

Calculate position size with 2% risk

size = DhanHQ::Risk::PositionSizer.calculate(
  account_balance: 100_000,
  risk_percent: 2.0,
  entry_price: 2500,
  stop_loss_price: 2450
)
#=> 40

Class Method Summary collapse

Class Method Details

.calculate(account_balance:, risk_percent:, entry_price:, stop_loss_price:, lot_size: 1) ⇒ Integer

Calculate position size based on fixed risk percentage.

Parameters:

  • account_balance (Float)

    Total account balance

  • risk_percent (Float)

    Percentage of account to risk per trade (e.g., 2.0 for 2%)

  • entry_price (Float)

    Planned entry price

  • stop_loss_price (Float)

    Planned stop loss price

  • lot_size (Integer) (defaults to: 1)

    Lot size for the instrument (default: 1)

Returns:

  • (Integer)

    Number of shares/lots to trade



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/DhanHQ/risk.rb', line 31

def self.calculate(account_balance:, risk_percent:, entry_price:, stop_loss_price:, lot_size: 1)
  return 0 if  <= 0 || entry_price <= 0 || stop_loss_price <= 0
  return 0 if entry_price == stop_loss_price

  risk_amount =  * (risk_percent / 100.0)
  risk_per_share = (entry_price - stop_loss_price).abs

  return 0 if risk_per_share.zero?

  raw_shares = (risk_amount / risk_per_share).floor
  (raw_shares / lot_size).floor * lot_size
end

.kelly(win_rate:, avg_win:, avg_loss:, account_balance:, entry_price:, fraction: 0.5) ⇒ Integer

Calculate position size using Kelly Criterion.

Parameters:

  • win_rate (Float)

    Historical win rate (0.0 to 1.0)

  • avg_win (Float)

    Average winning trade amount

  • avg_loss (Float)

    Average losing trade amount (positive number)

  • account_balance (Float)

    Total account balance

  • entry_price (Float)

    Planned entry price

  • fraction (Float) (defaults to: 0.5)

    Kelly fraction to use (default: 0.5 for half-Kelly)

Returns:

  • (Integer)

    Number of shares to trade



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/DhanHQ/risk.rb', line 53

def self.kelly(win_rate:, avg_win:, avg_loss:, account_balance:, entry_price:, fraction: 0.5)
  return 0 if  <= 0 || entry_price <= 0
  return 0 if avg_loss.zero? || win_rate <= 0 || win_rate >= 1

  # Kelly formula: f = (bp - q) / b
  # where b = avg_win/avg_loss, p = win_rate, q = 1 - win_rate
  b = avg_win / avg_loss
  kelly_fraction = ((b * win_rate) - (1 - win_rate)) / b

  return 0 if kelly_fraction <= 0

  # Apply fractional Kelly
  adjusted_fraction = kelly_fraction * fraction
  risk_amount =  * adjusted_fraction
  shares = (risk_amount / entry_price).floor

  [shares, 0].max
end

.volatility_based(account_balance:, risk_percent:, entry_price:, atr:, atr_multiplier: 2.0, lot_size: 1) ⇒ Integer

Calculate position size based on volatility (ATR-based).

Parameters:

  • account_balance (Float)

    Total account balance

  • risk_percent (Float)

    Percentage of account to risk per trade

  • entry_price (Float)

    Planned entry price

  • atr (Float)

    Current ATR value

  • atr_multiplier (Float) (defaults to: 2.0)

    Multiplier for ATR-based stop (default: 2.0)

  • lot_size (Integer) (defaults to: 1)

    Lot size for the instrument (default: 1)

Returns:

  • (Integer)

    Number of shares/lots to trade



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/DhanHQ/risk.rb', line 81

def self.volatility_based(account_balance:, risk_percent:, entry_price:, atr:, atr_multiplier: 2.0, lot_size: 1)
  return 0 if  <= 0 || entry_price <= 0 || atr <= 0

  stop_distance = atr * atr_multiplier
  stop_loss_price = entry_price - stop_distance

  calculate(
    account_balance: ,
    risk_percent: risk_percent,
    entry_price: entry_price,
    stop_loss_price: stop_loss_price,
    lot_size: lot_size
  )
end