iron_warbler 2.0.7.18 → 2.0.7.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascript/iron_warbler/application.js +2 -0
  3. data/app/assets/javascript/iron_warbler/gameui.js +9 -0
  4. data/app/assets/stylesheets/iron_warbler/application.css +13 -3
  5. data/app/assets/stylesheets/iron_warbler/positions.scss +9 -1
  6. data/app/assets/stylesheets/iron_warbler/purses_gameui.scss +191 -0
  7. data/app/assets/stylesheets/iron_warbler/purses_gameui.scss-bk +112 -0
  8. data/app/assets/stylesheets/iron_warbler/utils.scss +82 -0
  9. data/app/controllers/iro/api/stocks_controller.rb +87 -0
  10. data/app/controllers/iro/api_controller.rb +19 -0
  11. data/app/controllers/iro/positions_controller.rb +16 -4
  12. data/app/controllers/iro/purses_controller.rb +8 -3
  13. data/app/controllers/iro/strategies_controller.rb +7 -7
  14. data/app/helpers/iro/application_helper.rb +5 -2
  15. data/app/models/iro/alert.rb +2 -1
  16. data/app/models/iro/datapoint.rb +48 -8
  17. data/app/models/iro/position.rb +55 -14
  18. data/app/models/iro/position_covered_call.rb +4 -0
  19. data/app/models/iro/position_debit_spread.rb +251 -0
  20. data/app/models/iro/price_item.rb +51 -1
  21. data/app/models/iro/stock.rb +5 -2
  22. data/app/models/iro/strategy.rb +76 -10
  23. data/app/models/tda/stock.rb +3 -0
  24. data/app/views/iro/_main_header.haml +18 -17
  25. data/app/views/iro/api/stocks/show.jbuilder +11 -0
  26. data/app/views/iro/positions/_form.haml +11 -38
  27. data/app/views/iro/positions/_formpart_4data.haml +46 -0
  28. data/app/views/iro/positions/_gameui_covered_call.haml +36 -0
  29. data/app/views/iro/positions/_gameui_covered_call.haml-bk +59 -0
  30. data/app/views/iro/positions/_gameui_long_debit_call_spread.haml +37 -0
  31. data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +39 -0
  32. data/app/views/iro/positions/_header.haml +6 -0
  33. data/app/views/iro/positions/_header_covered_call.haml +6 -0
  34. data/app/views/iro/positions/_header_long_debit_call_spread.haml +14 -0
  35. data/app/views/iro/positions/_header_short_debit_put_spread.haml +1 -0
  36. data/app/views/iro/positions/_table.haml +118 -123
  37. data/app/views/iro/positions/roll.haml +83 -0
  38. data/app/views/iro/positions/trash/_header_short_debit_put_spread.haml +9 -0
  39. data/app/views/iro/purses/_header.haml +18 -0
  40. data/app/views/iro/purses/gameui.haml +18 -0
  41. data/app/views/iro/purses/gameui.haml-bk +44 -0
  42. data/app/views/iro/purses/gameui.haml-bk2 +89 -0
  43. data/app/views/iro/purses/show.haml +1 -7
  44. data/app/views/iro/stocks/_grid_is_long.haml +13 -0
  45. data/app/views/iro/stocks/_grid_is_short.haml +13 -0
  46. data/app/views/iro/strategies/_form.haml +12 -5
  47. data/app/views/iro/strategies/_show.haml +3 -2
  48. data/app/views/iro/strategies/_table.haml +16 -10
  49. data/app/views/iro/strategies/index.haml +8 -0
  50. data/app/views/layouts/iro/application.haml +1 -1
  51. data/config/routes.rb +14 -1
  52. data/lib/tasks/db_tasks.rake +9 -3
  53. metadata +29 -8
  54. data/app/assets/stylesheets/iron_warbler/alerts.css +0 -8
  55. data/app/assets/stylesheets/iron_warbler/datapoints.css +0 -0
  56. data/app/assets/stylesheets/iron_warbler/profiles.css +0 -4
  57. data/app/assets/stylesheets/iron_warbler/strategies.scss +0 -0
  58. data/app/assets/stylesheets/iron_warbler/utils.css +0 -44
  59. data/app/jobs/iro/application_job.rb-trash +0 -4
@@ -5,15 +5,34 @@ class Iro::Datapoint
5
5
  include Mongoid::Timestamps
6
6
  store_in collection: 'iro_datapoints'
7
7
 
8
- field :date
8
+ field :kind ## PUT, CALL, STOCK, CURRENCY, CRYPTO
9
+ validates :kind, presence: true
10
+ index({ kind: -1 })
11
+
12
+ field :symbol ## ticker, but use 'symbol' ONLY
13
+
14
+ field :date, type: Date ## @obsolete, use quote_at
9
15
  index({ kind: -1, date: -1 })
10
16
 
17
+ field :quote_at, type: DateTime
18
+ index({ kind: -1, quote_at: -1 })
19
+ validates :quote_at, uniqueness: { scope: [ :kind, :symbol ] }
20
+
21
+ field :open, type: Float
22
+ field :high, type: Float
23
+ field :low, type: Float
24
+
11
25
  field :value, type: Float
12
26
  validates :value, presence: true
27
+ def close
28
+ value
29
+ end
30
+ def close= a
31
+ value= a
32
+ end
33
+
34
+ field :volume, type: Integer
13
35
 
14
- field :kind
15
- validates :kind, presence: true
16
- index({ kind: -1 })
17
36
 
18
37
  def self.test_0trash
19
38
  add_fields = { '$addFields': {
@@ -21,10 +40,10 @@ class Iro::Datapoint
21
40
  '$dateToString': { 'format': "%Y-%m-%d", 'date': "$created_at" }
22
41
  }
23
42
  } }
24
- group = { '$group': {
25
- '_id': "$date_string",
26
- 'my_doc': { '$first': "$$ROOT" }
27
- } }
43
+ # group = { '$group': {
44
+ # '_id': "$date_string",
45
+ # 'my_doc': { '$first': "$$ROOT" }
46
+ # } }
28
47
  group = { '$group': {
29
48
  '_id': "$date",
30
49
  'my_doc': { '$first': "$$ROOT" }
@@ -113,4 +132,25 @@ class Iro::Datapoint
113
132
  # puts! outs.to_a, 'result'
114
133
  end
115
134
 
135
+ def self.import_stock symbol:, path:
136
+ csv = CSV.read(path, headers: true)
137
+ csv.each do |row|
138
+ flag = create({
139
+ kind: 'STOCK',
140
+ symbol: symbol,
141
+ date: row['date'],
142
+ quote_at: row['date'],
143
+
144
+ volume: row['volume'],
145
+
146
+ open: row['open'],
147
+ high: row['high'],
148
+ low: row['low'],
149
+ value: row['close'],
150
+ })
151
+ print '.' if flag.persisted?
152
+ end
153
+ puts 'ok'
154
+ end
155
+
116
156
  end
@@ -4,6 +4,8 @@ class Iro::Position
4
4
  include Mongoid::Timestamps
5
5
  store_in collection: 'iro_positions'
6
6
 
7
+ attr_accessor :gain_loss_amount
8
+
7
9
  STATUS_ACTIVE = 'active'
8
10
  STATUS_PROPOSED = 'proposed'
9
11
  STATUSES = [ nil, 'active', 'inactive', 'proposed' ]
@@ -12,34 +14,48 @@ class Iro::Position
12
14
  scope :active, ->{ where( status: 'active' ) }
13
15
 
14
16
  belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
17
+ index({ purse_id: 1, ticker: 1 })
18
+
19
+ belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
20
+ def ticker
21
+ stock&.ticker || '-'
22
+ end
23
+
15
24
  belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
16
25
 
17
- field :ticker
18
- validates :ticker, presence: true
19
- index({ purse_id: 1, ticker: 1 })
26
+ # field :ticker
27
+ # validates :ticker, presence: true
20
28
 
21
- KINDS = [ nil, 'covered_call', 'credit_put_spread', 'credit_call_spread' ]
22
- field :kind
29
+ field :outer_strike, type: :float
30
+ # validates :outer_strike, presence: true
23
31
 
24
- field :strike, type: :float
25
- validates :strike, presence: true
32
+ field :inner_strike, type: :float
33
+ validates :inner_strike, presence: true
26
34
 
27
35
  field :expires_on
28
36
  validates :expires_on, presence: true
29
37
 
30
38
  field :quantity, type: :integer
31
39
  validates :quantity, presence: true
40
+ def q; quantity; end
32
41
 
33
42
  field :begin_on
34
- field :begin_price, type: :float
35
- field :begin_delta, type: :float
43
+ field :begin_outer_price, type: :float
44
+ field :begin_outer_delta, type: :float
45
+
46
+ field :begin_inner_price, type: :float
47
+ field :begin_inner_delta, type: :float
36
48
 
37
49
  field :end_on
38
- field :end_price, type: :float
39
- field :end_delta, type: :float
50
+ field :end_outer_price, type: :float
51
+ field :end_outer_delta, type: :float
40
52
 
41
- field :net_amount
42
- field :net_percent
53
+ field :end_inner_price, type: :float
54
+ field :end_inner_delta, type: :float
55
+
56
+ def breakeven
57
+ inner_strike - begin_outer_price + begin_inner_price
58
+ end
43
59
 
44
60
  def current_underlying_strike
45
61
  Iro::Stock.find_by( ticker: ticker ).last
@@ -59,6 +75,19 @@ class Iro::Position
59
75
  print '_'
60
76
  end
61
77
 
78
+ def net_percent
79
+ net_amount / max_gain
80
+ end
81
+ def net_amount # each
82
+ strategy.send("net_amount_#{strategy.kind}", self)
83
+ end
84
+ def max_gain # each
85
+ strategy.send("max_gain_#{strategy.kind}", self)
86
+ end
87
+ def max_loss # each
88
+ strategy.send("max_loss_#{strategy.kind}", self)
89
+ end
90
+
62
91
 
63
92
  field :next_delta, type: :float
64
93
  field :next_outcome, type: :float
@@ -67,6 +96,10 @@ class Iro::Position
67
96
  field :next_reasons, type: :array, default: []
68
97
  field :should_rollp, type: :float
69
98
 
99
+ ##
100
+ ## decisions
101
+ ##
102
+
70
103
  def should_roll?
71
104
  puts! 'shold_roll?'
72
105
 
@@ -83,7 +116,7 @@ class Iro::Position
83
116
  if end_delta < strategy.threshold_delta
84
117
  next_reasons.push "delta is lower than threshold"
85
118
  out = 0.91
86
- elsif 1 - end_price/begin_price > strategy.threshold_netp
119
+ elsif 1 - end_outer_price/begin_outer_price > strategy.threshold_netp
87
120
  next_reasons.push "made enough percent profit (dubious)"
88
121
  out = 0.61
89
122
  else
@@ -219,4 +252,12 @@ class Iro::Position
219
252
  return out
220
253
  end
221
254
 
255
+ def to_s
256
+ out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.kind_short} ["
257
+ if outer_strike
258
+ out = out + "$#{outer_strike}->"
259
+ end
260
+ out = out + "$#{inner_strike}] "
261
+ return out
262
+ end
222
263
  end
@@ -0,0 +1,4 @@
1
+
2
+ class Iro::PositionCoveredCall < Iro::Position
3
+
4
+ end
@@ -0,0 +1,251 @@
1
+
2
+ class Iro::PositionDebitSpread < Iro::Positin
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'iro_positions'
6
+
7
+ STATUS_ACTIVE = 'active'
8
+ STATUS_PROPOSED = 'proposed'
9
+ STATUSES = [ nil, 'active', 'inactive', 'proposed' ]
10
+ field :status
11
+ validates :status, presence: true
12
+ scope :active, ->{ where( status: 'active' ) }
13
+
14
+ belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
15
+ index({ purse_id: 1, ticker: 1 })
16
+
17
+ belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
18
+ def ticker
19
+ stock&.ticker || '-'
20
+ end
21
+
22
+ belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
23
+
24
+ field :outer_strike, type: :float
25
+ validates :outer_strike, presence: true
26
+
27
+ field :inner_strike, type: :float
28
+ validates :inner_strike, presence: true
29
+
30
+ field :expires_on
31
+ validates :expires_on, presence: true
32
+
33
+ field :quantity, type: :integer
34
+ validates :quantity, presence: true
35
+
36
+ field :begin_on
37
+ field :begin_outer_price, type: :float
38
+ field :begin_outer_delta, type: :float
39
+
40
+ field :begin_inner_price, type: :float
41
+ field :begin_inner_delta, type: :float
42
+
43
+ field :end_on
44
+ field :end_outer_price, type: :float
45
+ field :end_outer_delta, type: :float
46
+
47
+ field :end_inner_price, type: :float
48
+ field :end_inner_delta, type: :float
49
+
50
+ field :net_amount
51
+ field :net_percent
52
+
53
+ def current_underlying_strike
54
+ Iro::Stock.find_by( ticker: ticker ).last
55
+ end
56
+
57
+ def refresh
58
+ out = Tda::Option.get_quote({
59
+ contractType: 'CALL',
60
+ strike: strike,
61
+ expirationDate: expires_on,
62
+ ticker: ticker,
63
+ })
64
+ update({
65
+ end_delta: out[:delta],
66
+ end_price: out[:last],
67
+ })
68
+ print '_'
69
+ end
70
+
71
+ def net_amount # total
72
+ outer = 0 - begin_outer_price + end_outer_price
73
+ inner = begin_inner_price - end_inner_price
74
+ out = ( outer + inner ) * 100 * quantity
75
+ end
76
+ def max_gain # total
77
+ 100 * ( begin_outer_price - begin_inner_price ) * quantity
78
+ end
79
+ def max_loss
80
+ out = 100 * ( outer_strike - inner_strike ) * quantity
81
+ if strategy.long_or_short == Iro::Strategy::SHORT
82
+ out = out * -1
83
+ end
84
+ return out
85
+ end
86
+
87
+
88
+ field :next_delta, type: :float
89
+ field :next_outcome, type: :float
90
+ field :next_symbol
91
+ field :next_mark
92
+ field :next_reasons, type: :array, default: []
93
+ field :should_rollp, type: :float
94
+
95
+ ##
96
+ ## decisions
97
+ ##
98
+
99
+ def should_roll?
100
+ puts! 'shold_roll?'
101
+
102
+ update({
103
+ next_reasons: [],
104
+ next_symbol: nil,
105
+ next_delta: nil,
106
+ })
107
+
108
+ if must_roll?
109
+ out = 1.0
110
+ elsif can_roll?
111
+
112
+ if end_delta < strategy.threshold_delta
113
+ next_reasons.push "delta is lower than threshold"
114
+ out = 0.91
115
+ elsif 1 - end_outer_price/begin_outer_price > strategy.threshold_netp
116
+ next_reasons.push "made enough percent profit (dubious)"
117
+ out = 0.61
118
+ else
119
+ next_reasons.push "neutral"
120
+ out = 0.33
121
+ end
122
+
123
+ else
124
+ out = 0.0
125
+ end
126
+
127
+ update({
128
+ next_delta: next_position[:delta],
129
+ next_outcome: next_position[:mark] - end_price,
130
+ next_symbol: next_position[:symbol],
131
+ next_mark: next_position[:mark],
132
+ should_rollp: out,
133
+ # status: Iro::Position::STATE_PROPOSED,
134
+ })
135
+
136
+ puts! next_reasons, 'next_reasons'
137
+ puts! out, 'out'
138
+ return out > 0.5
139
+ end
140
+
141
+
142
+ ## expires_on = cc.expires_on ; nil
143
+ def can_roll?
144
+ ## only if less than 7 days left
145
+ ( expires_on.to_date - Time.now.to_date ).to_i < 7
146
+ end
147
+
148
+ ## If I'm near below water
149
+ ##
150
+ ## expires_on = cc.expires_on ; strategy = cc.strategy ; strike = cc.strike ; nil
151
+ def must_roll?
152
+ if ( current_underlying_strike + strategy.buffer_above_water ) > strike
153
+ return true
154
+ end
155
+ ## @TODO: This one should not happen, I should log appropriately. _vp_ 2023-03-19
156
+ if ( expires_on.to_date - Time.now.to_date ).to_i < 1
157
+ return true
158
+ end
159
+ end
160
+
161
+ ## strike = cc.strike ; strategy = cc.strategy ; nil
162
+ def near_below_water?
163
+ strike < current_underlying_strike + strategy.buffer_above_water
164
+ end
165
+
166
+
167
+
168
+ ## 2023-03-18 _vp_ Continue.
169
+ ## 2023-03-19 _vp_ Continue.
170
+ ## 2023-08-05 _vp_ an Important method
171
+ ##
172
+ ## expires_on = cc.expires_on ; strategy = cc.strategy ; ticker = cc.ticker ; end_price = cc.end_price ; next_expires_on = cc.next_expires_on ; nil
173
+ ##
174
+ ## out.map { |p| [ p[:strikePrice], p[:delta] ] }
175
+ ##
176
+ def next_position
177
+ return @next_position if @next_position
178
+ return {} if ![ STATUS_ACTIVE, STATUS_PROPOSED ].include?( status )
179
+
180
+ ## 7 days ahead - not configurable so far
181
+ out = Tda::Option.get_quotes({
182
+ ticker: ticker,
183
+ expirationDate: next_expires_on,
184
+ contractType: 'CALL',
185
+ })
186
+
187
+ ## above_water
188
+ if strategy.buffer_above_water.present?
189
+ out = out.select do |i|
190
+ i[:strikePrice] > current_underlying_strike + strategy.buffer_above_water
191
+ end
192
+ # next_reasons.push "buffer_above_water above #{current_underlying_strike + strategy.buffer_above_water}"
193
+ end
194
+
195
+ if near_below_water?
196
+ msg = "Panic! climb at a loss. Skip the rest of the calculation."
197
+ next_reasons.push msg
198
+ ## @TODO: if not enough money in the purse, cannot roll? 2023-03-19
199
+
200
+ # byebug
201
+
202
+ ## Take a small loss here.
203
+ prev = nil
204
+ out.each_with_index do |i, idx|
205
+ next if idx == 0
206
+ if i[:last] < end_price
207
+ prev ||= i
208
+ end
209
+ end
210
+ out = [ prev ]
211
+
212
+ else
213
+ ## Normal flow, making money.
214
+ ## @TODO: test! _vp_ 2023-03-19
215
+
216
+ ## next_min_strike
217
+ if strategy.next_min_strike.present?
218
+ out = out.select do |i|
219
+ i[:strikePrice] >= strategy.next_min_strike
220
+ end
221
+ # next_reasons.push "next_min_strike above #{strategy.next_min_strike}"
222
+ end
223
+ # json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_min_strike'
224
+
225
+ ## max_delta
226
+ if strategy.next_max_delta.present?
227
+ out = out.select do |i|
228
+ i[:delta] = 0.0 if i[:delta] == "NaN"
229
+ i[:delta] <= strategy.next_max_delta
230
+ end
231
+ # next_reasons.push "next_max_delta below #{strategy.next_max_delta}"
232
+ end
233
+ # json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_max_delta'
234
+ end
235
+
236
+ @next_position = out[0] || {}
237
+ end
238
+
239
+ ## @TODO: Test this. _vp_ 2023-04-01
240
+ def next_expires_on
241
+ out = expires_on.to_time + 7.days
242
+ while !out.friday?
243
+ out = out + 1.day
244
+ end
245
+ while !out.workday?
246
+ out = out - 1.day
247
+ end
248
+ return out
249
+ end
250
+
251
+ end
@@ -4,6 +4,7 @@ class Iro::PriceItem
4
4
  include Mongoid::Timestamps
5
5
  store_in collection: 'iro_price_items'
6
6
 
7
+ ## PUT, CALL, STOCK
7
8
  field :putCall, type: String
8
9
  field :symbol, type: String
9
10
  field :ticker, type: String
@@ -15,9 +16,9 @@ class Iro::PriceItem
15
16
  field :last, type: Float
16
17
 
17
18
  field :openPrice, type: Float
18
- field :closePrice, type: Float
19
19
  field :lowPrice, type: Float
20
20
  field :highPrice, type: Float
21
+ field :closePrice, type: Float
21
22
 
22
23
  field :quoteTimeInLong, type: Integer
23
24
  field :timestamp, type: Integer
@@ -26,4 +27,53 @@ class Iro::PriceItem
26
27
  field :exchangeName, type: String
27
28
  field :volatility, type: Float
28
29
 
30
+ def self.my_find props={}
31
+ lookup = { '$lookup': {
32
+ 'from': 'iro_price_items',
33
+ 'localField': 'date',
34
+ 'foreignField': 'date',
35
+ 'pipeline': [
36
+ { '$sort': { 'value': -1 } },
37
+ ],
38
+ 'as': 'dates',
39
+ } }
40
+ lookup_merge = { '$replaceRoot': {
41
+ 'newRoot': { '$mergeObjects': [
42
+ { '$arrayElemAt': [ "$dates", 0 ] }, "$$ROOT"
43
+ ] }
44
+ } }
45
+
46
+
47
+ match = { '$match': {
48
+ 'date': {
49
+ '$gte': props[:begin_on],
50
+ '$lte': props[:end_on],
51
+ }
52
+ } }
53
+
54
+ group = { '$group': {
55
+ '_id': "$date",
56
+ 'my_doc': { '$first': "$$ROOT" }
57
+ } }
58
+
59
+ outs = Iro::Date.collection.aggregate([
60
+ match,
61
+
62
+ lookup,
63
+ lookup_merge,
64
+
65
+ group,
66
+ { '$replaceRoot': { 'newRoot': "$my_doc" } },
67
+ # { '$replaceRoot': { 'newRoot': "$my_doc" } },
68
+
69
+
70
+ { '$project': { '_id': 0, 'date': 1, 'value': 1 } },
71
+ { '$sort': { 'date': 1 } },
72
+ ])
73
+
74
+ puts! 'result'
75
+ pp outs.to_a
76
+ # puts! outs.to_a, 'result'
77
+ end
78
+
29
79
  end
@@ -18,13 +18,16 @@ class Iro::Stock
18
18
 
19
19
  field :last, type: :float
20
20
 
21
-
22
- # has_many :strategies, class_name: 'Iro::Strategy', inverse_of: :stock
21
+ has_many :positions, class_name: 'Iro::Position', inverse_of: :stock
22
+ has_many :strategies, class_name: 'Iro::Strategy', inverse_of: :stock
23
23
 
24
24
  def to_s
25
25
  ticker
26
26
  end
27
27
  def self.list
28
+ [[nil,nil]] + all.map { |sss| [ sss.ticker, sss.id ] }
29
+ end
30
+ def self.tickers_list
28
31
  [[nil,nil]] + all.map { |sss| [ sss.ticker, sss.ticker ] }
29
32
  end
30
33
  end
@@ -7,17 +7,43 @@ class Iro::Strategy
7
7
  field :slug
8
8
  validates :slug, presence: true, uniqueness: true
9
9
 
10
+ LONG = 'is_long'
11
+ SHORT = 'is_short'
12
+ field :long_or_short, type: :string
13
+ validates :long_or_short, presence: true
14
+
15
+ field :description
16
+
10
17
  has_many :positions, class_name: 'Iro::Position', inverse_of: :strategy
11
18
 
12
- ## multiple strategies per ticker
13
- field :ticker
14
- validates :ticker, presence: true
15
- index({ ticker: 1 })
16
- # belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
19
+ belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
20
+
21
+ KIND_COVERED_CALL = 'covered_call'
22
+ KIND_LONG_DEBIT_CALL_SPREAD = 'long_debit_call_spread'
23
+ KIND_SHORT_DEBIT_PUT_SPREAD = 'long_debit_call_spread'
24
+ KINDS = [ nil,
25
+ KIND_COVERED_CALL,
26
+ KIND_LONG_DEBIT_CALL_SPREAD,
27
+ KIND_SHORT_DEBIT_PUT_SPREAD,
28
+ ]
29
+ field :kind
17
30
 
31
+ def kind_short
32
+ case kind
33
+ when KIND_COVERED_CALL
34
+ 'cc'
35
+ when KIND_LONG_DEBIT_CALL_SPREAD
36
+ 'long-spread'
37
+ when KIND_SHORT_DEBIT_PUT_SPREAD
38
+ 'short-spread'
39
+ else
40
+ '@TODO-zez'
41
+ end
42
+ end
18
43
 
19
44
  field :buffer_above_water, type: :float
20
- field :next_max_delta, type: :float
45
+ field :next_max_inner_delta, type: :float
46
+ field :next_max_outer_delta, type: :float
21
47
  field :next_min_strike, type: :float
22
48
  field :threshold_delta, type: :float
23
49
  field :threshold_netp, type: :float
@@ -26,13 +52,53 @@ class Iro::Strategy
26
52
  where( ticker: ticker )
27
53
  end
28
54
 
55
+ def max_gain_covered_call p
56
+ # return p.begin_inner_price
57
+ p.begin_inner_price * 100 - 0.66
58
+ end
59
+ def max_gain_long_debit_call_spread p
60
+ ## 100 * disalloed for gameui
61
+ ( p.inner_strike - p.outer_strike - p.begin_outer_price + p.begin_inner_price ) # - 2*0.66
62
+ end
63
+ def max_gain_short_debit_put_spread p
64
+ ## 100 * disalloed for gameui
65
+ ( p.outer_strike - p.inner_strike - p.begin_outer_price + p.begin_inner_price ) # - 2*0.66
66
+ end
29
67
 
30
- def to_s
31
- slug
68
+ def max_loss_covered_call p
69
+ return 'inf'
70
+ end
71
+ def max_loss_long_debit_call_spread p
72
+ out = 100 * ( p.outer_strike - p.inner_strike )
73
+ end
74
+ def max_loss_short_debit_put_spread p
75
+ out = -100 * ( p.outer_strike - p.inner_strike )
32
76
  end
33
77
 
34
- def self.list
35
- [[nil,nil]] + all.map { |ttt| [ ttt.slug, ttt.id ] }
78
+ def net_amount_covered_call p
79
+ ( p.begin_inner_price - p.end_inner_price ) * 100
36
80
  end
81
+ def net_amount_long_debit_call_spread p
82
+ outer = p.end_outer_price - p.begin_outer_price
83
+ inner = p.begin_inner_price - p.end_inner_price
84
+ out = ( outer + inner ) * 100
85
+ end
86
+ alias_method :net_amount_short_debit_put_spread, :net_amount_long_debit_call_spread
87
+
88
+
89
+ def net_amount_long_debit_call_spread p
90
+ outer = p.end_outer_price - p.begin_outer_price
91
+ inner = p.begin_inner_price - p.end_inner_price
92
+ out = ( outer + inner ) * 100
93
+ end
94
+
37
95
 
96
+
97
+ def to_s
98
+ slug
99
+ end
100
+ def self.list long_or_short = nil
101
+ these = long_or_short ? where( long_or_short: long_or_short ) : all
102
+ [[nil,nil]] + these.map { |ttt| [ ttt.slug, ttt.id ] }
103
+ end
38
104
  end
@@ -16,6 +16,9 @@ class Tda::Stock
16
16
  path = "/v1/marketdata/quotes"
17
17
  inns = self.get path, { query: { apikey: ::TD_AMERITRADE[:apiKey], symbol: tickers } }
18
18
  inns = inns.parsed_response
19
+ if [ NilClass, String ].include?( inns.class )
20
+ return []
21
+ end
19
22
  inns.each do |k, v|
20
23
  inns[k] = v.deep_symbolize_keys
21
24
  end