Class: Iro::Position

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

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



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

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

#breakevenObject



70
71
72
# File 'app/models/iro/position.rb', line 70

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

#calc_rollpObject

decisions



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/iro/position.rb', line 211

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)


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

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



74
75
76
# File 'app/models/iro/position.rb', line 74

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

#end_deltaObject



66
67
68
# File 'app/models/iro/position.rb', line 66

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

#max_gainObject

each



98
99
100
# File 'app/models/iro/position.rb', line 98

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

#max_lossObject

each



101
102
103
# File 'app/models/iro/position.rb', line 101

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)


240
241
242
# File 'app/models/iro/position.rb', line 240

def near_below_water?
  strike < current_underlying_strike + strategy.buffer_above_water
end

#net_amountObject

each



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

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

#net_percentObject



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

def net_percent
  net_amount / max_gain
end

#next_expires_onObject

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



318
319
320
321
322
323
324
325
326
327
# File 'app/models/iro/position.rb', line 318

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 ] }



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
310
311
312
313
314
315
# File 'app/models/iro/position.rb', line 254

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



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

def q; quantity; end

#refreshObject



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'app/models/iro/position.rb', line 78

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



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/models/iro/position.rb', line 157

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



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/models/iro/position.rb', line 182

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



24
25
26
# File 'app/models/iro/position.rb', line 24

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

#to_sObject



329
330
331
332
333
334
335
336
# File 'app/models/iro/position.rb', line 329

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