Class: Iro::Position

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

Constant Summary collapse

STATUS_ACTIVE =
'active'
STATUS_PROPOSED =
'proposed'
STATUSES =
[ nil, 'active', 'inactive', 'proposed' ]
KINDS =
[ nil, 'covered_call', 'credit_put_spread', 'credit_call_spread' ]

Instance Method Summary collapse

Instance Method Details

#can_roll?Boolean

expires_on = cc.expires_on ; nil

Returns:

  • (Boolean)


114
115
116
117
# File 'app/models/iro/position.rb', line 114

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



44
45
46
# File 'app/models/iro/position.rb', line 44

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

#must_roll?Boolean

If I’m near below water

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

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
# File 'app/models/iro/position.rb', line 122

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)


133
134
135
# File 'app/models/iro/position.rb', line 133

def near_below_water?
  strike < current_underlying_strike + strategy.buffer_above_water
end

#next_expires_onObject

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



211
212
213
214
215
216
217
218
219
220
# File 'app/models/iro/position.rb', line 211

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



147
148
149
150
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
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
# File 'app/models/iro/position.rb', line 147

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



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/models/iro/position.rb', line 48

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

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'app/models/iro/position.rb', line 70

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_price/begin_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