Class: Iro::Position

Inherits:
Object
  • Object
show all
Includes:
Mongoid::Document, Mongoid::Paranoia, Mongoid::Timestamps
Defined in:
app/models/iro/position.rb

Direct Known Subclasses

PositionCoveredCall

Constant Summary collapse

STATUS_ACTIVE =
'active'
STATUS_PROPOSED =
'proposed'
STATUSES =
[ nil, 'active', 'inactive', 'proposed' ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#next_gain_loss_amountObject

Returns the value of attribute next_gain_loss_amount.



8
9
10
# File 'app/models/iro/position.rb', line 8

def next_gain_loss_amount
  @next_gain_loss_amount
end

Instance Method Details

#begin_deltaObject



57
58
59
# File 'app/models/iro/position.rb', line 57

def begin_delta
  strategy.send("begin_delta_#{strategy.kind}", self)
end

#breakevenObject



64
65
66
# File 'app/models/iro/position.rb', line 64

def breakeven
  strategy.send("breakeven_#{strategy.kind}", self)
end

#calc_rollpObject

decisions



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'app/models/iro/position.rb', line 205

def calc_rollp
  self.next_reasons = []
  self.next_symbol = nil
  self.next_delta = nil

  out = strategy.send( "calc_rollp_#{strategy.kind}", self )

  self.rollp = out[0]
  self.next_reasons.push out[1]
  save

  # update({
  #   next_delta:   next_position[:delta],
  #   next_outcome: next_position[:mark] - end_price,
  #   next_symbol:  next_position[:symbol],
  #   next_mark:    next_position[:mark],
  #   should_rollp: out,
  #   # status:       Iro::Position::STATE_PROPOSED,
  # })
end

#can_roll?Boolean

expires_on = cc.expires_on ; nil

Returns:

  • (Boolean)


228
229
230
231
# File 'app/models/iro/position.rb', line 228

def can_roll?
  ## only if less than 7 days left
  ( expires_on.to_date - Time.now.to_date ).to_i < 7
end

#current_underlying_strikeObject



68
69
70
# File 'app/models/iro/position.rb', line 68

def current_underlying_strike
  Iro::Stock.find_by( ticker: ticker ).last
end

#end_deltaObject



60
61
62
# File 'app/models/iro/position.rb', line 60

def end_delta
  strategy.send("end_delta_#{strategy.kind}", self)
end

#max_gainObject

each



92
93
94
# File 'app/models/iro/position.rb', line 92

def max_gain # each
  strategy.send("max_gain_#{strategy.kind}", self)
end

#max_lossObject

each



95
96
97
# File 'app/models/iro/position.rb', line 95

def max_loss # each
  strategy.send("max_loss_#{strategy.kind}", self)
end

#near_below_water?Boolean

strike = cc.strike ; strategy = cc.strategy ; nil

Returns:

  • (Boolean)


234
235
236
# File 'app/models/iro/position.rb', line 234

def near_below_water?
  strike < current_underlying_strike + strategy.buffer_above_water
end

#net_amountObject

each



89
90
91
# File 'app/models/iro/position.rb', line 89

def net_amount # each
  strategy.send("net_amount_#{strategy.kind}", self)
end

#net_percentObject



86
87
88
# File 'app/models/iro/position.rb', line 86

def net_percent
  net_amount / max_gain
end

#next_expires_onObject

@TODO: Test this. vp 2023-04-01



312
313
314
315
316
317
318
319
320
321
# File 'app/models/iro/position.rb', line 312

def next_expires_on
  out = expires_on.to_time + 7.days
  while !out.friday?
    out = out + 1.day
  end
  while !out.workday?
    out = out - 1.day
  end
  return out
end

#next_positionObject

2023-03-18 vp Continue. 2023-03-19 vp Continue. 2023-08-05 vp an Important method

expires_on = cc.expires_on ; strategy = cc.strategy ; ticker = cc.ticker ; end_price = cc.end_price ; next_expires_on = cc.next_expires_on ; nil

out.map { |p| [ p, p ] }



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'app/models/iro/position.rb', line 248

def next_position
  return @next_position if @next_position
  return {} if ![ STATUS_ACTIVE, STATUS_PROPOSED ].include?( status )

  ## 7 days ahead - not configurable so far
  out = Tda::Option.get_quotes({
    ticker: ticker,
    expirationDate: next_expires_on,
    contractType: 'CALL',
  })

  ## above_water
  if strategy.buffer_above_water.present?
    out = out.select do |i|
      i[:strikePrice] > current_underlying_strike + strategy.buffer_above_water
    end
    # next_reasons.push "buffer_above_water above #{current_underlying_strike + strategy.buffer_above_water}"
  end

  if near_below_water?
    msg = "Panic! climb at a loss. Skip the rest of the calculation."
    next_reasons.push msg
    ## @TODO: if not enough money in the purse, cannot roll? 2023-03-19

    # byebug

    ## Take a small loss here.
    prev = nil
    out.each_with_index do |i, idx|
      next if idx == 0
      if i[:last] < end_price
        prev ||= i
      end
    end
    out = [ prev ]

  else
    ## Normal flow, making money.
    ## @TODO: test! _vp_ 2023-03-19

    ## next_min_strike
    if strategy.next_min_strike.present?
      out = out.select do |i|
        i[:strikePrice] >= strategy.next_min_strike
      end
      # next_reasons.push "next_min_strike above #{strategy.next_min_strike}"
    end
    # json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_min_strike'

    ## max_delta
    if strategy.next_max_delta.present?
      out = out.select do |i|
        i[:delta] = 0.0 if i[:delta] == "NaN"
        i[:delta] <= strategy.next_max_delta
      end
      # next_reasons.push "next_max_delta below #{strategy.next_max_delta}"
    end
    # json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_max_delta'
  end

  @next_position = out[0] || {}
end

#qObject



41
# File 'app/models/iro/position.rb', line 41

def q; quantity; end

#refreshObject



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'app/models/iro/position.rb', line 72

def refresh
  out = Tda::Option.get_quote({
    contractType:   'CALL',
    strike:         strike,
    expirationDate: expires_on,
    ticker:         ticker,
  })
  update({
    end_delta: out[:delta],
    end_price: out[:last],
  })
  print '_'
end

#syncObject

self.end_inner_price = ( inner.bid + inner.ask ) / 2

self.end_inner_delta = inner.delta

end



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'app/models/iro/position.rb', line 151

def sync
  put_call = Iro::Strategy::LONG == strategy.long_or_short ? 'CALL' : 'PUT'
  puts! [
    [ inner_strike, expires_on, stock.ticker ],
    [ outer_strike, expires_on, stock.ticker ],
   ], 'init sync inner, outer'
  inner = Tda::Option.get_quote({
    contractType: put_call,
    strike: inner_strike,
    expirationDate: expires_on,
    ticker: stock.ticker,
  })
  outer = Tda::Option.get_quote({
    contractType: put_call,
    strike: outer_strike,
    expirationDate: expires_on,
    ticker: stock.ticker,
  })
  puts! [inner, outer], 'sync inner, outer'
  self.end_outer_price = ( outer.bid + outer.ask ) / 2
  self.end_outer_delta = outer.delta

  self.end_inner_price = ( inner.bid + inner.ask ) / 2
  self.end_inner_delta = inner.delta
end

#sync_short_debit_put_spreadObject



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'app/models/iro/position.rb', line 176

def sync_short_debit_put_spread
  puts! [
    [ inner_strike, expires_on, stock.ticker ],
    [ outer_strike, expires_on, stock.ticker ],
   ], 'init sync inner, outer'
  inner = Tda::Option.get_quote({
    contractType: 'PUT',
    strike: inner_strike,
    expirationDate: expires_on,
    ticker: stock.ticker,
  })
  outer = Tda::Option.get_quote({
    contractType: 'PUT',
    strike: outer_strike,
    expirationDate: expires_on,
    ticker: stock.ticker,
  })
  puts! [inner, outer], 'sync inner, outer'
  self.end_outer_price = ( outer.bid + outer.ask ) / 2
  self.end_outer_delta = outer.delta

  self.end_inner_price = ( inner.bid + inner.ask ) / 2
  self.end_inner_delta = inner.delta
end

#tickerObject



21
22
23
# File 'app/models/iro/position.rb', line 21

def ticker
  stock&.ticker || '-'
end

#to_sObject



323
324
325
326
327
328
329
330
# File 'app/models/iro/position.rb', line 323

def to_s
  out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.kind_short} ["
  if outer_strike
    out = out + "$#{outer_strike}->"
  end
  out = out + "$#{inner_strike}] "
  return out
end