Class: Iro::Strategy
- Inherits:
-
Object
- Object
- Iro::Strategy
- Includes:
- Mongoid::Document, Mongoid::Paranoia, Mongoid::Timestamps
- Defined in:
- app/models/iro/strategy.rb
Constant Summary collapse
- LONG =
'long'- SHORT =
'short'- CREDIT =
'credit'- DEBIT =
'debit'- KIND_COVERED_CALL =
'covered_call'- KIND_DIAG_LONG_CALL_SPREAD =
'diag_long_call_spread'- KIND_DIAG_SHORT_PUT_SPREAD =
'diag_short_put_spread'- KIND_IRON_CONDOR =
'iron_condor'- KIND_LONG_CREDIT_PUT_SPREAD =
'long_credit_put_spread'- KIND_LONG_DEBIT_CALL_SPREAD =
'long_debit_call_spread'- KIND_SHORT_CREDIT_CALL_SPREAD =
'short_credit_call_spread'- KIND_SHORT_DEBIT_PUT_SPREAD =
'short_debit_put_spread'- KIND_UNINVESTABLE =
'uninvestable'- KIND_LONG_ONLY =
'long-only'- KIND_SPREAD =
these are too simple and deprecated:
'spread'- KIND_WHEEL =
@deprecated, be specific
'wheel'- KINDS =
@deprecated, use covered_call
[ nil, KIND_COVERED_CALL, KIND_DIAG_LONG_CALL_SPREAD, KIND_DIAG_SHORT_PUT_SPREAD, KIND_IRON_CONDOR, KIND_LONG_CREDIT_PUT_SPREAD, KIND_LONG_DEBIT_CALL_SPREAD, KIND_SHORT_CREDIT_CALL_SPREAD, KIND_SHORT_DEBIT_PUT_SPREAD, KIND_UNINVESTABLE, KIND_LONG_ONLY, ]
- INTENT_CLOSE =
'try-close'- INTENT_OPEN =
'try-open'- INTENT_ROLL =
'try-roll'- INTENTS =
[ nil, INTENT_CLOSE, INTENT_ROLL ]
Class Method Summary collapse
Instance Method Summary collapse
- #_begin_delta_spread(p) ⇒ Object
- #_end_delta_spread(p) ⇒ Object
- #begin_delta(p) ⇒ Object
- #begin_delta_covered_call(p) ⇒ Object
- #begin_delta_diag_long_call_spread(p) ⇒ Object
- #begin_delta_diag_short_put_spread(p) ⇒ Object
- #begin_delta_long_credit_put_spread(p) ⇒ Object
- #begin_delta_short_credit_call_spread(p) ⇒ Object
-
#calc_rollp_covered_call(p) ⇒ Object
do not use!.
-
#calc_rollp_long_credit_put_spread(p) ⇒ Object
2025-10-12 continue 2026-04-05 continue.
-
#calc_rollp_long_debit_call_spread(p) ⇒ Object
do not use!.
-
#calc_rollp_short_credit_call_spread(p) ⇒ Object
2026-02-21 ok 2026-04-05 ok.
-
#calc_rollp_short_debit_put_spread(p) ⇒ Object
do not use!.
- #end_delta(p) ⇒ Object
- #end_delta_covered_call(p) ⇒ Object
- #end_delta_diag_long_call_spread(p) ⇒ Object
- #end_delta_diag_short_put_spread(p) ⇒ Object
- #end_delta_long_credit_put_spread(p) ⇒ Object
- #end_delta_short_credit_call_spread(p) ⇒ Object
-
#max_gain_covered_call(p) ⇒ Object
each.
-
#max_gain_diag_long_call_spread(p) ⇒ Object
each.
-
#max_gain_diag_short_put_spread(p) ⇒ Object
each.
- #max_gain_long_credit_put_spread(p) ⇒ Object
- #max_gain_long_debit_call_spread(p) ⇒ Object
- #max_gain_short_credit_call_spread(p) ⇒ Object
- #max_gain_short_debit_put_spread(p) ⇒ Object
- #max_gain_spread(p) ⇒ Object
- #max_gain_wheel(p) ⇒ Object
- #max_loss_covered_call(p) ⇒ Object
- #max_loss_diag_long_call_spread(p) ⇒ Object
- #max_loss_diag_short_put_spread(p) ⇒ Object
- #max_loss_long_credit_put_spread(p) ⇒ Object
-
#max_loss_short_credit_call_spread(p) ⇒ Object
def max_loss_long_debit_call_spread p out = p.outer.strike - p.inner.strike end def max_loss_short_debit_put_spread p # different out = p.inner.strike - p.outer.strike end.
- #max_loss_short_debit_put_spread(p) ⇒ Object
- #max_loss_spread(p) ⇒ Object
- #max_loss_wheel(p) ⇒ Object
- #net_amount_spread(p) ⇒ Object
-
#next_inner_strike ⇒ Object
can be nil, poor design?.
-
#next_position ⇒ Object
_TODO: makes no sense...
- #put_call ⇒ Object
- #to_s ⇒ Object
Class Method Details
.for_ticker(ticker) ⇒ Object
scopes
393 394 395 |
# File 'app/models/iro/strategy.rb', line 393 def self.for_ticker ticker where( ticker: ticker ) end |
.list(long_or_short = nil) ⇒ Object
401 402 403 404 |
# File 'app/models/iro/strategy.rb', line 401 def self.list long_or_short = nil these = long_or_short ? where( long_or_short: long_or_short ) : all [[nil,nil]] + these.map { |st| [ st, st.id ] } end |
Instance Method Details
#_begin_delta_spread(p) ⇒ Object
126 127 128 |
# File 'app/models/iro/strategy.rb', line 126 def _begin_delta_spread p p.inner.begin_delta - p.outer.begin_delta end |
#_end_delta_spread(p) ⇒ Object
148 149 150 |
# File 'app/models/iro/strategy.rb', line 148 def _end_delta_spread p p.inner.end_delta - p.outer.end_delta end |
#begin_delta(p) ⇒ Object
120 121 122 |
# File 'app/models/iro/strategy.rb', line 120 def begin_delta p _begin_delta_spread p end |
#begin_delta_covered_call(p) ⇒ Object
123 124 125 |
# File 'app/models/iro/strategy.rb', line 123 def begin_delta_covered_call p p.inner.begin_delta end |
#begin_delta_diag_long_call_spread(p) ⇒ Object
135 136 137 |
# File 'app/models/iro/strategy.rb', line 135 def begin_delta_diag_long_call_spread p _begin_delta_spread p end |
#begin_delta_diag_short_put_spread(p) ⇒ Object
138 139 140 |
# File 'app/models/iro/strategy.rb', line 138 def begin_delta_diag_short_put_spread p _begin_delta_spread p end |
#begin_delta_long_credit_put_spread(p) ⇒ Object
129 130 131 |
# File 'app/models/iro/strategy.rb', line 129 def begin_delta_long_credit_put_spread p _begin_delta_spread p end |
#begin_delta_short_credit_call_spread(p) ⇒ Object
132 133 134 |
# File 'app/models/iro/strategy.rb', line 132 def begin_delta_short_credit_call_spread p _begin_delta_spread p end |
#calc_rollp_covered_call(p) ⇒ Object
do not use!
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'app/models/iro/strategy.rb', line 245 def calc_rollp_covered_call p stock.reload if ( p.expires_on.to_date - Time.now.to_date ).to_i < 1 return [ 0.99, '0 DTE, must exit' ] end if ( stock.last - threshold_usd_above_mark ) < p.inner.strike return [ 0.98, "Last #{'%.2f' % stock.last} is " + "#{'%.2f' % [p.inner.strike + threshold_usd_above_mark - stock.last]} " + "below #{'%.2f' % [p.inner.strike + threshold_usd_above_mark]} water" ] end if p.inner.end_delta < threshold_pos_delta return [ 0.61, "Delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} threshold." ] end if 1 - p.inner.end_price/p.inner.begin_price > threshold_netp return [ 0.51, "made enough #{'%.02f' % [(1.0 - p.inner.end_price/p.inner.begin_price )*100]}% profit." ] end return [ 0.33, '-' ] end |
#calc_rollp_long_credit_put_spread(p) ⇒ Object
2025-10-12 continue 2026-04-05 continue
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'app/models/iro/strategy.rb', line 299 def calc_rollp_long_credit_put_spread p stock.reload if ( p.expires_on.to_date - Time.now.to_date ).to_i < 1 return [ 0.99, '0 DTE, must exit' ] end if ( p.expires_on.to_date - Time.now.to_date ).to_i < 2 return [ 0.99, '1 DTE, must exit' ] end if ( stock.last - threshold_usd_above_mark ) < p.inner.strike return [ 0.95, ":threshold_usd_above_mark <br />Last $#{'%.2f' % stock.last} is " + "#{'%.2f' % [stock.last - p.inner.strike]} near #{p.inner.strike} but should be greater than #{threshold_usd_above_mark} ." ] end if p.inner.end_delta.abs < threshold_pos_delta return [ 0.79, ":threshold_pos_delta <br />Offensive roll: delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} ." ] end if p.inner.end_delta.abs > threshold_neg_delta return [ 0.79, ":threshold_neg_delta <br />Defensive roll: delta #{p.inner.end_delta} is higher than #{threshold_neg_delta} ." ] end if threshold_netp.present? if 1 - p.inner.end_price/p.inner.begin_price > threshold_netp return [ 0.51, ":threshold_netp <br />made enough #{'%.02f' % [(1.0 - p.inner.end_price/p.inner.begin_price )*100]}% profit^" ] end end return [ 0.33, '-' ] end |
#calc_rollp_long_debit_call_spread(p) ⇒ Object
do not use!
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 |
# File 'app/models/iro/strategy.rb', line 270 def calc_rollp_long_debit_call_spread p stock.reload if ( p.expires_on.to_date - Time.now.to_date ).to_i < 1 return [ 0.99, '0 DTE, must exit' ] end if ( p.expires_on.to_date - Time.now.to_date ).to_i < 2 return [ 0.99, '1 DTE, must exit' ] end if ( stock.last - threshold_usd_above_mark ) < p.inner.strike return [ 0.95, "Last #{'%.2f' % stock.last} is " + "#{'%.2f' % [stock.last - p.inner.strike - threshold_usd_above_mark]} " + "below #{'%.2f' % [p.inner.strike + threshold_usd_above_mark]} water" ] end if p.inner.end_delta < threshold_pos_delta return [ 0.79, "Delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} threshold." ] end if 1 - p.inner.end_price/p.inner.begin_price > threshold_netp return [ 0.51, "made enough #{'%.02f' % [(1.0 - p.inner.end_price/p.inner.begin_price )*100]}% profit^" ] end return [ 0.33, '-' ] end |
#calc_rollp_short_credit_call_spread(p) ⇒ Object
2026-02-21 ok 2026-04-05 ok
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'app/models/iro/strategy.rb', line 357 def calc_rollp_short_credit_call_spread p # puts! p, 'calc_rollp_short_credit_call_spread...' stock.reload if ( p.expires_on.to_date - Time.now.to_date ).to_i <= threshold_dte return [ 0.99, "< #{threshold_dte}DTE, must exit" ] end if stock.last + threshold_usd_above_mark > p.inner.strike return [ 0.95, ":threshold_usd_above_mark <br />Last $#{'%.2f' % stock.last} is " + "#{'%.2f' % [p.inner.strike - stock.last]} near #{p.inner.strike} but should be greater than #{threshold_usd_above_mark} ." ] end ## defensive ## inner short call is negative delta, but I'll deal with absolutes anyway. if p.inner.end_delta.abs > threshold_neg_delta.abs return [ 0.88, "Delta #{p.inner.end_delta} is larger than #{threshold_neg_delta} defensive threshold." ] end ## offensive if p.inner.end_delta.abs < threshold_pos_delta.abs return [ 0.69, "Delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} offensive threshold." ] end if threshold_netp.present? if p.net_percent > threshold_netp return [ 0.51, "made enough #{'%.0f' % [p.net_percent*100]}% > #{"%.2f" % [threshold_netp*100]}% profit," ] end end return [ 0.33, '-' ] end |
#calc_rollp_short_debit_put_spread(p) ⇒ Object
do not use!
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'app/models/iro/strategy.rb', line 331 def calc_rollp_short_debit_put_spread p stock.reload if ( p.expires_on.to_date - Time.now.to_date ).to_i <= threshold_dte return [ 0.99, "< #{threshold_dte}DTE, must exit" ] end if stock.last + threshold_usd_above_mark > p.inner.strike return [ 0.98, "Last #{'%.2f' % stock.last} is " + "#{'%.2f' % [stock.last + threshold_usd_above_mark - p.inner.strike]} " + "above #{'%.2f' % [p.inner.strike - threshold_usd_above_mark]} water" ] end if p.inner.end_delta.abs < threshold_pos_delta.abs return [ 0.79, "Delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} threshold." ] end if p.net_percent > threshold_netp return [ 0.51, "made enough #{'%.0f' % [p.net_percent*100]}% > #{"%.2f" % [threshold_netp*100]}% profit," ] end return [ 0.33, '-' ] end |
#end_delta(p) ⇒ Object
142 143 144 |
# File 'app/models/iro/strategy.rb', line 142 def end_delta p _end_delta_spread p end |
#end_delta_covered_call(p) ⇒ Object
145 146 147 |
# File 'app/models/iro/strategy.rb', line 145 def end_delta_covered_call p p.inner.end_delta end |
#end_delta_diag_long_call_spread(p) ⇒ Object
157 158 159 |
# File 'app/models/iro/strategy.rb', line 157 def end_delta_diag_long_call_spread p _end_delta_spread p end |
#end_delta_diag_short_put_spread(p) ⇒ Object
160 161 162 |
# File 'app/models/iro/strategy.rb', line 160 def end_delta_diag_short_put_spread p _end_delta_spread p end |
#end_delta_long_credit_put_spread(p) ⇒ Object
151 152 153 |
# File 'app/models/iro/strategy.rb', line 151 def end_delta_long_credit_put_spread p _end_delta_spread p end |
#end_delta_short_credit_call_spread(p) ⇒ Object
154 155 156 |
# File 'app/models/iro/strategy.rb', line 154 def end_delta_short_credit_call_spread p _end_delta_spread p end |
#max_gain_covered_call(p) ⇒ Object
each
173 174 175 |
# File 'app/models/iro/strategy.rb', line 173 def max_gain_covered_call p ## each p.inner.begin_price # - 0.66 end |
#max_gain_diag_long_call_spread(p) ⇒ Object
each
165 166 167 168 |
# File 'app/models/iro/strategy.rb', line 165 def max_gain_diag_long_call_spread p ## each # p.outer.strike - p.inner.strike + p.outer.begin_price - p.inner.begin_price p.inner.strike - p.outer.strike - p.outer.begin_price + p.inner.begin_price end |
#max_gain_diag_short_put_spread(p) ⇒ Object
each
169 170 171 |
# File 'app/models/iro/strategy.rb', line 169 def max_gain_diag_short_put_spread p ## each p.outer.strike - p.inner.strike - p.outer.begin_price + p.inner.begin_price end |
#max_gain_long_credit_put_spread(p) ⇒ Object
176 177 178 179 |
# File 'app/models/iro/strategy.rb', line 176 def max_gain_long_credit_put_spread p ## 100 * disallowed for gameui p.inner.begin_price - p.outer.begin_price end |
#max_gain_long_debit_call_spread(p) ⇒ Object
180 181 182 183 |
# File 'app/models/iro/strategy.rb', line 180 def max_gain_long_debit_call_spread p ## 100 * disallowed for gameui ( p.inner.strike - p.outer.strike - p.outer.begin_price + p.inner.begin_price ) # - 2*0.66 end |
#max_gain_short_credit_call_spread(p) ⇒ Object
184 185 186 |
# File 'app/models/iro/strategy.rb', line 184 def max_gain_short_credit_call_spread p p.inner.begin_price - p.outer.begin_price end |
#max_gain_short_debit_put_spread(p) ⇒ Object
187 188 189 190 |
# File 'app/models/iro/strategy.rb', line 187 def max_gain_short_debit_put_spread p ## 100 * disallowed for gameui ( p.outer.strike - p.inner.strike - p.outer.begin_price + p.inner.begin_price ) # - 2*0.66 end |
#max_gain_spread(p) ⇒ Object
191 192 193 194 |
# File 'app/models/iro/strategy.rb', line 191 def max_gain_spread p ## 100 * disallowed for gameui ( p.outer.strike - p.inner.strike ).abs - p.outer.begin_price + p.inner.begin_price # - 2*0.66 end |
#max_gain_wheel(p) ⇒ Object
195 196 197 |
# File 'app/models/iro/strategy.rb', line 195 def max_gain_wheel p p.inner.begin_price * 100 - 0.66 # @TODO: is this *100 really? end |
#max_loss_covered_call(p) ⇒ Object
207 208 209 |
# File 'app/models/iro/strategy.rb', line 207 def max_loss_covered_call p p.inner.begin_price*10 # just suppose 10,000% end |
#max_loss_diag_long_call_spread(p) ⇒ Object
200 201 202 203 |
# File 'app/models/iro/strategy.rb', line 200 def max_loss_diag_long_call_spread p # p.inner.strike - p.outer.strike + p.inner.begin_price + p.outer.begin_price p.outer.begin_price - p.inner.begin_price - p.realized_gl end |
#max_loss_diag_short_put_spread(p) ⇒ Object
204 205 206 |
# File 'app/models/iro/strategy.rb', line 204 def max_loss_diag_short_put_spread p p.outer.begin_price - p.inner.begin_price - p.realized_gl ## same as above, max_loss_diag_long_call_spread end |
#max_loss_long_credit_put_spread(p) ⇒ Object
210 211 212 |
# File 'app/models/iro/strategy.rb', line 210 def max_loss_long_credit_put_spread p out = p.inner.strike - p.outer.strike end |
#max_loss_short_credit_call_spread(p) ⇒ Object
def max_loss_long_debit_call_spread p out = p.outer.strike - p.inner.strike end def max_loss_short_debit_put_spread p # different out = p.inner.strike - p.outer.strike end
219 220 221 |
# File 'app/models/iro/strategy.rb', line 219 def max_loss_short_credit_call_spread p out = p.outer.strike - p.inner.strike end |
#max_loss_short_debit_put_spread(p) ⇒ Object
222 223 224 |
# File 'app/models/iro/strategy.rb', line 222 def max_loss_short_debit_put_spread p p.inner.begin_price - p.outer.begin_price end |
#max_loss_spread(p) ⇒ Object
225 226 227 |
# File 'app/models/iro/strategy.rb', line 225 def max_loss_spread p ( p.outer.strike - p.inner.strike ).abs end |
#max_loss_wheel(p) ⇒ Object
228 229 230 |
# File 'app/models/iro/strategy.rb', line 228 def max_loss_wheel p p.inner.begin_price*10 # just suppose 10,000% end |
#net_amount_spread(p) ⇒ Object
234 235 236 |
# File 'app/models/iro/strategy.rb', line 234 def net_amount_spread p p.inner.begin_price - p.inner.end_price - p.outer.begin_price + p.outer.end_price end |
#next_inner_strike ⇒ Object
can be nil, poor design?
98 |
# File 'app/models/iro/strategy.rb', line 98 field :next_inner_strike, type: :float |
#next_position ⇒ Object
_TODO: makes no sense...
22 |
# File 'app/models/iro/strategy.rb', line 22 has_one :next_position, class_name: 'Iro::Position', inverse_of: :next_strategy |
#put_call ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'app/models/iro/strategy.rb', line 54 def put_call case kind when Iro::Strategy::KIND_COVERED_CALL put_call = 'CALL' when Iro::Strategy::KIND_DIAG_LONG_CALL_SPREAD put_call = 'CALL' when Iro::Strategy::KIND_DIAG_SHORT_PUT_SPREAD put_call = 'PUT' when Iro::Strategy::KIND_LONG_CREDIT_PUT_SPREAD put_call = 'PUT' when Iro::Strategy::KIND_LONG_DEBIT_CALL_SPREAD put_call = 'CALL' when Iro::Strategy::KIND_SHORT_CREDIT_CALL_SPREAD put_call = 'CALL' when Iro::Strategy::KIND_SHORT_DEBIT_PUT_SPREAD put_call = 'PUT' when Iro::Strategy::KIND_SPREAD if credit_or_debit == CREDIT if long_or_short == LONG 'PUT' elsif long_or_short == SHORT 'CALL' else throw 'zq5 - should never happen' end else throw 'zq6 - debit spreads are not implemented' end else throw 'zq9 - this should never happen' end end |
#to_s ⇒ Object
398 399 400 |
# File 'app/models/iro/strategy.rb', line 398 def to_s "#{kind} #{stock} #{next_spread_amount}- #{intent} #{descr}" end |