Class: Iro::PositionDebitSpread

Inherits:
Positin
  • Object
show all
Includes:
Mongoid::Document, Mongoid::Timestamps
Defined in:
app/models/iro/trash/position_debit_spread.rb

Constant Summary collapse

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

Instance Method Summary collapse

Instance Method Details

#can_roll?Boolean

expires_on = cc.expires_on ; nil

Returns:

  • (Boolean)


143
144
145
146
# File 'app/models/iro/trash/position_debit_spread.rb', line 143

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



53
54
55
# File 'app/models/iro/trash/position_debit_spread.rb', line 53

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

#max_gainObject

total



76
77
78
# File 'app/models/iro/trash/position_debit_spread.rb', line 76

def max_gain # total
  100 * ( begin_outer_price - begin_inner_price ) * quantity
end

#max_lossObject



79
80
81
82
83
84
85
# File 'app/models/iro/trash/position_debit_spread.rb', line 79

def max_loss
  out = 100 * ( outer_strike - inner_strike ) * quantity
  if strategy.long_or_short == Iro::Strategy::SHORT
    out = out * -1
  end
  return out
end

#must_roll?Boolean

If I’m near below water

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

Returns:

  • (Boolean)


151
152
153
154
155
156
157
158
159
# File 'app/models/iro/trash/position_debit_spread.rb', line 151

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)


162
163
164
# File 'app/models/iro/trash/position_debit_spread.rb', line 162

def near_below_water?
  strike < current_underlying_strike + strategy.buffer_above_water
end

#net_amountObject

total



71
72
73
74
75
# File 'app/models/iro/trash/position_debit_spread.rb', line 71

def net_amount # total
  outer = 0 - begin_outer_price + end_outer_price
  inner = begin_inner_price - end_inner_price
  out = ( outer + inner ) * 100 * quantity
end

#next_expires_onObject

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



240
241
242
243
244
245
246
247
248
249
# File 'app/models/iro/trash/position_debit_spread.rb', line 240

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



176
177
178
179
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
# File 'app/models/iro/trash/position_debit_spread.rb', line 176

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

#refreshObject



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/models/iro/trash/position_debit_spread.rb', line 57

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)


99
100
101
102
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
# File 'app/models/iro/trash/position_debit_spread.rb', line 99

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



18
19
20
# File 'app/models/iro/trash/position_debit_spread.rb', line 18

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