Class: Iro::Position

Inherits:
Object
  • Object
show all
Includes:
Mongoid::Document, 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

#gain_loss_amountObject

Returns the value of attribute gain_loss_amount.



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

def gain_loss_amount
  @gain_loss_amount
end

Instance Method Details

#breakevenObject



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

def breakeven
  strategy.breakeven(self)
end

#can_roll?Boolean

expires_on = cc.expires_on ; nil

Returns:

  • (Boolean)


147
148
149
150
# File 'app/models/iro/position.rb', line 147

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



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

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

#max_gainObject

each



84
85
86
# File 'app/models/iro/position.rb', line 84

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

#max_lossObject

each



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

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

#must_roll?Boolean

If I’m near below water

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

Returns:

  • (Boolean)


155
156
157
158
159
160
161
162
163
# File 'app/models/iro/position.rb', line 155

def must_roll?
  if ( current_underlying_strike + strategy.buffer_above_water ) > strike
    return true
  end
  ## @TODO: This one should not happen, I should log appropriately. _vp_ 2023-03-19
  if ( expires_on.to_date - Time.now.to_date ).to_i < 1
    return true
  end
end

#near_below_water?Boolean

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

Returns:

  • (Boolean)


166
167
168
# File 'app/models/iro/position.rb', line 166

def near_below_water?
  strike < current_underlying_strike + strategy.buffer_above_water
end

#net_amountObject

each



81
82
83
# File 'app/models/iro/position.rb', line 81

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

#net_percentObject



78
79
80
# File 'app/models/iro/position.rb', line 78

def net_percent
  net_amount / max_gain
end

#next_expires_onObject

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



244
245
246
247
248
249
250
251
252
253
# File 'app/models/iro/position.rb', line 244

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



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'app/models/iro/position.rb', line 180

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



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

def q; quantity; end

#refreshObject



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'app/models/iro/position.rb', line 64

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

#should_roll?Boolean

decisions

Returns:

  • (Boolean)


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/iro/position.rb', line 103

def should_roll?
  puts! 'shold_roll?'

  update({
    next_reasons: [],
    next_symbol: nil,
    next_delta: nil,
  })

  if must_roll?
    out = 1.0
  elsif can_roll?

    if end_delta < strategy.threshold_delta
      next_reasons.push "delta is lower than threshold"
      out = 0.91
    elsif 1 - end_outer_price/begin_outer_price > strategy.threshold_netp
      next_reasons.push "made enough percent profit (dubious)"
      out = 0.61
    else
      next_reasons.push "neutral"
      out = 0.33
    end

  else
    out = 0.0
  end

  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,
  })

  puts! next_reasons, 'next_reasons'
  puts! out, 'out'
  return out > 0.5
end

#tickerObject



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

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

#to_sObject



255
256
257
258
259
260
261
262
# File 'app/models/iro/position.rb', line 255

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