Class: Schwab::Resources::Strategy

Inherits:
Base
  • Object
show all
Defined in:
lib/schwab/resources/strategy.rb

Overview

Resource wrapper for strategy objects (complex multi-leg orders) Provides strategy-specific helper methods for options strategies

Instance Method Summary collapse

Methods inherited from Base

#==, #[], #[]=, #attributes, #each, #empty?, field_types, #initialize, #inspect, #key?, #keys, #method_missing, #respond_to_missing?, set_field_type, #to_h, #to_s

Constructor Details

This class inherits a constructor from Schwab::Resources::Base

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Schwab::Resources::Base

Instance Method Details

#breakeven_pointsArray<Float>

Get breakeven points

Returns:

  • (Array<Float>)

    Breakeven points



316
317
318
319
320
# File 'lib/schwab/resources/strategy.rb', line 316

def breakeven_points
  # This would require complex calculations based on strategy type
  # Placeholder for strategy-specific calculations
  []
end

#butterfly?Boolean

Check if this is a butterfly spread

Returns:

  • (Boolean)

    True if butterfly



186
187
188
189
190
191
192
# File 'lib/schwab/resources/strategy.rb', line 186

def butterfly?
  return false unless leg_count == 4

  strikes = strike_prices
  # Butterfly has 3 unique strikes
  strikes.size == 3
end

#calendar_spread?Boolean Also known as: calendar?, horizontal?

Check if this is a calendar spread

Returns:

  • (Boolean)

    True if calendar spread



157
158
159
160
161
162
163
164
165
# File 'lib/schwab/resources/strategy.rb', line 157

def calendar_spread?
  return false unless leg_count == 2

  expirations = expiration_dates
  strikes = strike_prices

  # Different expirations, same strike
  expirations.size == 2 && strikes.size == 1
end

#collar?Boolean

Check if this is a collar

Returns:

  • (Boolean)

    True if collar



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/schwab/resources/strategy.rb', line 272

def collar?
  return false unless leg_count == 3 || leg_count == 2

  # Collar: long stock + long put + short call
  # Or just long put + short call (if stock is held separately)

  has_long_put = legs.any? { |leg| leg_is_put?(leg) && leg_is_long?(leg) }
  has_short_call = legs.any? { |leg| leg_is_call?(leg) && leg_is_short?(leg) }

  has_long_put && has_short_call
end

#condor?Boolean

Check if this is a condor

Returns:

  • (Boolean)

    True if condor



210
211
212
213
214
215
216
# File 'lib/schwab/resources/strategy.rb', line 210

def condor?
  return false unless leg_count == 4

  strikes = strike_prices
  # Condor has 4 unique strikes
  strikes.size == 4
end

#credit_strategy?Boolean

Check if this is a credit strategy

Returns:

  • (Boolean)

    True if net credit



127
128
129
# File 'lib/schwab/resources/strategy.rb', line 127

def credit_strategy?
  net_premium > 0
end

#debit_strategy?Boolean

Check if this is a debit strategy

Returns:

  • (Boolean)

    True if net debit



134
135
136
# File 'lib/schwab/resources/strategy.rb', line 134

def debit_strategy?
  net_premium < 0
end

#descriptionString

Get strategy description

Returns:

  • (String)

    Human-readable strategy description



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/schwab/resources/strategy.rb', line 325

def description
  if vertical_spread?
    "Vertical Spread"
  elsif calendar_spread?
    "Calendar Spread"
  elsif diagonal_spread?
    "Diagonal Spread"
  elsif iron_butterfly?
    "Iron Butterfly"
  elsif butterfly?
    "Butterfly Spread"
  elsif iron_condor?
    "Iron Condor"
  elsif condor?
    "Condor Spread"
  elsif straddle?
    "Straddle"
  elsif strangle?
    "Strangle"
  elsif collar?
    "Collar"
  elsif ratio_spread?
    "Ratio Spread"
  elsif multi_leg?
    "Multi-leg Strategy"
  else
    "Single Option"
  end
end

#diagonal_spread?Boolean Also known as: diagonal?

Check if this is a diagonal spread

Returns:

  • (Boolean)

    True if diagonal spread



172
173
174
175
176
177
178
179
180
# File 'lib/schwab/resources/strategy.rb', line 172

def diagonal_spread?
  return false unless leg_count == 2

  expirations = expiration_dates
  strikes = strike_prices

  # Different expirations and strikes
  expirations.size == 2 && strikes.size == 2
end

#expiration_datesArray<String>

Get all expiration dates in the strategy

Returns:

  • (Array<String>)

    Array of expiration dates



96
97
98
99
100
101
102
# File 'lib/schwab/resources/strategy.rb', line 96

def expiration_dates
  legs.map do |leg|
    if leg[:instrument]
      leg[:instrument][:expirationDate] || leg[:instrument][:expiration_date]
    end
  end.compact.uniq.sort
end

#iron_butterfly?Boolean

Check if this is an iron butterfly

Returns:

  • (Boolean)

    True if iron butterfly



197
198
199
200
201
202
203
204
205
# File 'lib/schwab/resources/strategy.rb', line 197

def iron_butterfly?
  return false unless butterfly?

  # Iron butterfly uses both puts and calls
  has_puts = legs.any? { |leg| leg_is_put?(leg) }
  has_calls = legs.any? { |leg| leg_is_call?(leg) }

  has_puts && has_calls
end

#iron_condor?Boolean

Check if this is an iron condor

Returns:

  • (Boolean)

    True if iron condor



221
222
223
224
225
226
227
228
229
# File 'lib/schwab/resources/strategy.rb', line 221

def iron_condor?
  return false unless condor?

  # Iron condor uses both puts and calls
  has_puts = legs.any? { |leg| leg_is_put?(leg) }
  has_calls = legs.any? { |leg| leg_is_call?(leg) }

  has_puts && has_calls
end

#leg_countInteger

Get number of legs

Returns:

  • (Integer)

    Number of legs in strategy



49
50
51
# File 'lib/schwab/resources/strategy.rb', line 49

def leg_count
  legs.size
end

#legsArray

Get the legs of the strategy

Returns:

  • (Array)

    Array of strategy legs



42
43
44
# File 'lib/schwab/resources/strategy.rb', line 42

def legs
  self[:legs] || self[:strategyLegs] || self[:strategy_legs] || self[:orderLegCollection] || []
end

#max_lossFloat?

Get max loss for the strategy

Returns:

  • (Float, nil)

    Maximum loss potential



307
308
309
310
311
# File 'lib/schwab/resources/strategy.rb', line 307

def max_loss
  # This would require complex calculations based on strategy type
  # Placeholder for strategy-specific calculations
  nil
end

#max_profitFloat?

Get max profit for the strategy

Returns:

  • (Float, nil)

    Maximum profit potential



298
299
300
301
302
# File 'lib/schwab/resources/strategy.rb', line 298

def max_profit
  # This would require complex calculations based on strategy type
  # Placeholder for strategy-specific calculations
  nil
end

#multi_leg?Boolean

Check if this is a multi-leg strategy

Returns:

  • (Boolean)

    True if multi-leg



63
64
65
# File 'lib/schwab/resources/strategy.rb', line 63

def multi_leg?
  leg_count > 1
end

#nameString

Get the strategy name/description

Returns:

  • (String)

    The strategy name



28
29
30
# File 'lib/schwab/resources/strategy.rb', line 28

def name
  self[:strategyName] || self[:strategy_name] || self[:name] || strategy_type
end

#net_premiumFloat?

Get net credit/debit

Returns:

  • (Float, nil)

    Net credit (positive) or debit (negative)



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/schwab/resources/strategy.rb', line 107

def net_premium
  total = 0.0
  legs.each do |leg|
    price = leg[:price] || 0
    quantity = leg[:quantity] || 0
    instruction = leg[:instruction]

    # Selling generates credit, buying generates debit
    if ["SELL", "SELL_TO_OPEN", "SELL_TO_CLOSE"].include?(instruction&.upcase)
      total += price * quantity * 100 # Options are in contracts of 100
    else
      total -= price * quantity * 100
    end
  end
  total
end

#ratio_spread?Boolean

Check if this is a ratio spread

Returns:

  • (Boolean)

    True if ratio spread



287
288
289
290
291
292
293
# File 'lib/schwab/resources/strategy.rb', line 287

def ratio_spread?
  return false unless multi_leg?

  # Ratio spread has unequal quantities
  quantities = legs.map { |leg| leg[:quantity].to_i.abs }.uniq
  quantities.size > 1
end

#single_leg?Boolean

Check if this is a single-leg strategy

Returns:

  • (Boolean)

    True if single leg



56
57
58
# File 'lib/schwab/resources/strategy.rb', line 56

def single_leg?
  leg_count == 1
end

#statusString

Get strategy status

Returns:

  • (String)

    The strategy status



35
36
37
# File 'lib/schwab/resources/strategy.rb', line 35

def status
  self[:status] || self[:strategyStatus] || self[:strategy_status]
end

#straddle?Boolean

Check if this is a straddle

Returns:

  • (Boolean)

    True if straddle



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/schwab/resources/strategy.rb', line 234

def straddle?
  return false unless leg_count == 2

  strikes = strike_prices
  expirations = expiration_dates

  # Same strike and expiration, one put and one call
  if strikes.size == 1 && expirations.size == 1
    has_put = legs.any? { |leg| leg_is_put?(leg) }
    has_call = legs.any? { |leg| leg_is_call?(leg) }
    has_put && has_call
  else
    false
  end
end

#strangle?Boolean

Check if this is a strangle

Returns:

  • (Boolean)

    True if strangle



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/schwab/resources/strategy.rb', line 253

def strangle?
  return false unless leg_count == 2

  strikes = strike_prices
  expirations = expiration_dates

  # Different strikes, same expiration, one put and one call
  if strikes.size == 2 && expirations.size == 1
    has_put = legs.any? { |leg| leg_is_put?(leg) }
    has_call = legs.any? { |leg| leg_is_call?(leg) }
    has_put && has_call
  else
    false
  end
end

#strategy_typeString Also known as: type

Get strategy type

Returns:

  • (String)

    The strategy type



20
21
22
# File 'lib/schwab/resources/strategy.rb', line 20

def strategy_type
  self[:strategyType] || self[:strategy_type] || self[:type]
end

#strike_pricesArray<Float>

Get all strike prices in the strategy

Returns:

  • (Array<Float>)

    Array of strike prices



85
86
87
88
89
90
91
# File 'lib/schwab/resources/strategy.rb', line 85

def strike_prices
  legs.map do |leg|
    if leg[:instrument]
      leg[:instrument][:strikePrice] || leg[:instrument][:strike_price]
    end
  end.compact.uniq.sort
end

#to_display_stringString

Get formatted display string for the strategy

Returns:

  • (String)

    Formatted strategy string



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/schwab/resources/strategy.rb', line 358

def to_display_string
  parts = []
  parts << description
  parts << underlying_symbol if underlying_symbol

  if strike_prices.any?
    parts << "Strikes: #{strike_prices.join("/")}"
  end

  if expiration_dates.any?
    parts << "Exp: #{expiration_dates.first}"
  end

  premium = net_premium
  if premium != 0
    parts << (premium > 0 ? "Credit: $#{premium.abs}" : "Debit: $#{premium.abs}")
  end

  parts.compact.join(" - ")
end

#underlying_symbolString?

Get the underlying symbol

Returns:

  • (String, nil)

    The underlying symbol



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/schwab/resources/strategy.rb', line 70

def underlying_symbol
  # All legs should have the same underlying for a valid strategy
  first_leg = legs.first
  return unless first_leg

  if first_leg[:instrument]
    first_leg[:instrument][:underlyingSymbol] ||
      first_leg[:instrument][:underlying_symbol] ||
      first_leg[:instrument][:symbol]
  end
end

#vertical_spread?Boolean Also known as: vertical?

Check if this is a vertical spread

Returns:

  • (Boolean)

    True if vertical spread



143
144
145
146
147
148
149
150
151
# File 'lib/schwab/resources/strategy.rb', line 143

def vertical_spread?
  return false unless leg_count == 2

  expirations = expiration_dates
  strikes = strike_prices

  # Same expiration, different strikes
  expirations.size == 1 && strikes.size == 2
end