iron_warbler 2.0.7.42 → 2.0.7.45

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/iron_warbler/utils.scss +9 -9
  3. data/app/controllers/iro/positions_controller.rb +1 -0
  4. data/app/controllers/iro/purses_controller.rb +3 -1
  5. data/app/controllers/iro/strategies_controller.rb +8 -3
  6. data/app/mailers/iro/alert_mailer.rb +1 -1
  7. data/app/views/iro/positions/_form.haml +1 -1
  8. data/app/views/iro/positions/{_gameui_long_debit_call_spread.haml-trash → _gameui_spread.haml} +9 -8
  9. data/app/views/iro/positions/_header_spread.haml +12 -0
  10. data/app/views/iro/positions/_new.haml +4 -0
  11. data/app/views/iro/positions/done/_gameui_short_debit_put_spread.haml +1 -0
  12. data/app/views/iro/positions/done/_header_short_debit_put_spread.haml +1 -0
  13. data/app/views/iro/purses/_form.haml +3 -3
  14. data/app/views/iro/purses/index.haml +3 -2
  15. data/app/views/iro/strategies/_form_spread.haml +69 -0
  16. data/app/views/iro/strategies/{_form.haml → _form_wheel.haml} +12 -12
  17. data/app/views/iro/strategies/_header.haml +2 -1
  18. data/app/views/iro/strategies/_table.haml +3 -5
  19. data/app/views/iro/strategies/edit.haml +1 -1
  20. data/app/views/iro/strategies/index.haml +2 -1
  21. data/app/views/iro/strategies/new.haml +1 -1
  22. data/app/views/layouts/iro/application.haml +1 -1
  23. data/config/routes.rb +2 -0
  24. data/lib/iron_warbler.rb +2 -1
  25. data/lib/tasks/iro_tasks.rake +6 -0
  26. metadata +20 -30
  27. data/app/models/iro/alert.rb +0 -63
  28. data/app/models/iro/datapoint.rb +0 -187
  29. data/app/models/iro/date.rb +0 -10
  30. data/app/models/iro/option.rb +0 -151
  31. data/app/models/iro/option_black_scholes.rb +0 -149
  32. data/app/models/iro/position.rb +0 -299
  33. data/app/models/iro/priceitem.rb +0 -93
  34. data/app/models/iro/purse.rb +0 -72
  35. data/app/models/iro/stock.rb +0 -134
  36. data/app/models/iro/strategy.rb +0 -264
  37. data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +0 -1
  38. data/app/views/iro/positions/_gameui_short_debit_put_spread.haml-trash +0 -40
  39. data/app/views/iro/positions/_header_short_debit_put_spread.haml +0 -1
  40. data/app/views/iro/positions/roll.haml-trash +0 -42
  41. /data/app/views/iro/positions/{_gameui_covered_call.haml-bk → done/_gameui_covered_call.haml-bk} +0 -0
  42. /data/app/views/iro/positions/{_gameui_long_credit_put_spread.haml → done/_gameui_long_credit_put_spread.haml} +0 -0
  43. /data/app/views/iro/positions/{_gameui_long_debit_call_spread.haml → done/_gameui_long_debit_call_spread.haml} +0 -0
  44. /data/app/views/iro/positions/{_gameui_short_credit_call_spread.haml → done/_gameui_short_credit_call_spread.haml} +0 -0
  45. /data/app/views/iro/positions/{_header_long_credit_put_spread.haml → done/_header_long_credit_put_spread.haml} +0 -0
  46. /data/app/views/iro/positions/{_header_long_debit_call_spread.haml → done/_header_long_debit_call_spread.haml} +0 -0
  47. /data/app/views/iro/positions/{_header_short_credit_call_spread.haml → done/_header_short_credit_call_spread.haml} +0 -0
  48. /data/app/views/iro/positions/{roll-cc.haml-bk → done/roll-cc.haml-bk} +0 -0
  49. /data/app/views/iro/purses/{gameui.haml-bk → done/gameui.haml-bk} +0 -0
  50. /data/app/views/iro/purses/{gameui.haml-bk2 → done/gameui.haml-bk2} +0 -0
  51. /data/app/views/iro/stocks/{_grid_is_long.haml → _grid_long.haml} +0 -0
  52. /data/app/views/iro/stocks/{_grid_is_short.haml → _grid_short.haml} +0 -0
@@ -1,299 +0,0 @@
1
-
2
- class Iro::Position
3
- include Mongoid::Document
4
- include Mongoid::Timestamps
5
- include Mongoid::Paranoia
6
- store_in collection: 'iro_positions'
7
-
8
- field :prev_gain_loss_amount, type: :float
9
- attr_accessor :next_gain_loss_amount
10
- def prev_gain_loss_amount
11
- out = autoprev.outer.end_price - autoprev.inner.end_price
12
- out += inner.begin_price - outer.begin_price
13
- end
14
-
15
-
16
- STATUS_ACTIVE = 'active'
17
- STATUS_CLOSED = 'closed'
18
- STATUS_PROPOSED = 'proposed'
19
- ## one more, 'selected' after proposed?
20
- STATUS_PENDING = 'pending' ## 'working'
21
- STATUSES = [ nil, STATUS_CLOSED, STATUS_ACTIVE, STATUS_PROPOSED, STATUS_PENDING ]
22
- field :status
23
- validates :status, presence: true
24
- scope :active, ->{ where( status: 'active' ) }
25
-
26
- belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
27
- index({ purse_id: 1, ticker: 1 })
28
-
29
- belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
30
- delegate :ticker, to: :stock
31
-
32
- belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
33
- delegate :put_call, to: :strategy
34
- delegate :long_or_short, to: :strategy
35
-
36
- belongs_to :next_strategy, class_name: 'Iro::Strategy', inverse_of: :next_position, optional: true
37
-
38
-
39
- belongs_to :prev, class_name: 'Iro::Position', inverse_of: :nxts, optional: true
40
- belongs_to :autoprev, class_name: 'Iro::Position', inverse_of: :autonxt, optional: true
41
- ## there are many of these, for viewing on the 'roll' view
42
- has_many :nxts, class_name: 'Iro::Position', inverse_of: :prev
43
- has_one :autonxt, class_name: 'Iro::Position', inverse_of: :autoprev
44
-
45
- ## Options
46
-
47
- belongs_to :inner, class_name: 'Iro::Option', inverse_of: :inner
48
- validates_associated :inner
49
-
50
- belongs_to :outer, class_name: 'Iro::Option', inverse_of: :outer
51
- validates_associated :outer
52
-
53
- accepts_nested_attributes_for :inner, :outer
54
-
55
- field :outer_strike, type: :float
56
- # validates :outer_strike, presence: true
57
-
58
- field :inner_strike, type: :float
59
- # validates :inner_strike, presence: true
60
-
61
- field :expires_on
62
- validates :expires_on, presence: true
63
-
64
- field :quantity, type: :integer
65
- validates :quantity, presence: true
66
- def q; quantity; end
67
-
68
- field :begin_on
69
-
70
- field :end_on
71
-
72
- def begin_delta
73
- strategy.send("begin_delta_#{strategy.kind}", self)
74
- end
75
- def end_delta
76
- strategy.send("end_delta_#{strategy.kind}", self)
77
- end
78
-
79
- def breakeven
80
- strategy.send("breakeven_#{strategy.kind}", self)
81
- end
82
-
83
- def current_underlying_strike
84
- Iro::Stock.find_by( ticker: ticker ).last
85
- end
86
-
87
- def refresh
88
- out = Tda::Option.get_quote({
89
- contractType: 'CALL',
90
- strike: strike,
91
- expirationDate: expires_on,
92
- ticker: ticker,
93
- })
94
- update({
95
- end_delta: out[:delta],
96
- end_price: out[:last],
97
- })
98
- print '^'
99
- end
100
-
101
- def net_percent
102
- net_amount / max_gain
103
- end
104
- def net_amount # each
105
- strategy.send("net_amount_#{strategy.kind}", self)
106
- end
107
- def max_gain # each
108
- strategy.send("max_gain_#{strategy.kind}", self)
109
- end
110
- def max_loss # each
111
- strategy.send("max_loss_#{strategy.kind}", self)
112
- end
113
-
114
-
115
- def sync
116
- inner.sync
117
- outer.sync
118
- end
119
-
120
-
121
- ##
122
- ## decisions
123
- ##
124
-
125
- field :next_reasons, type: :array, default: []
126
- field :rollp, type: :float
127
-
128
- ## should_roll?
129
- def calc_rollp
130
- self.next_reasons = []
131
- # self.next_symbol = nil
132
- # self.next_delta = nil
133
-
134
- out = strategy.send( "calc_rollp_#{strategy.kind}", self )
135
-
136
- self.rollp = out[0]
137
- self.next_reasons.push out[1]
138
- save
139
- end
140
-
141
- def calc_nxt
142
- pos = self
143
-
144
- ## 7 days ahead - not configurable so far
145
- outs = Tda::Option.get_quotes({
146
- contractType: pos.put_call,
147
- expirationDate: next_expires_on,
148
- ticker: ticker,
149
- })
150
- outs_bk = outs.dup
151
-
152
- outs = outs.select do |out|
153
- out[:bidSize] + out[:askSize] > 0
154
- end
155
-
156
- if 'CALL' == pos.put_call
157
- ;
158
- elsif 'PUT' == pos.put_call
159
- outs = outs.reverse
160
- end
161
-
162
- ## next_inner_strike
163
- outs = outs.select do |out|
164
- if Iro::Strategy::SHORT == pos.long_or_short
165
- out[:strikePrice] >= strategy.next_inner_strike
166
- elsif Iro::Strategy::LONG == pos.long_or_short
167
- out[:strikePrice] <= strategy.next_inner_strike
168
- else
169
- raise 'zz3 - this cannot happen'
170
- end
171
- end
172
- puts! outs[0][:strikePrice], 'after calc next_inner_strike'
173
- puts! outs, 'outs'
174
-
175
- ## next_buffer_above_water
176
- outs = outs.select do |out|
177
- if Iro::Strategy::SHORT == pos.long_or_short
178
- out[:strikePrice] > strategy.next_buffer_above_water + strategy.stock.last
179
- elsif Iro::Strategy::LONG == pos.long_or_short
180
- out[:strikePrice] < strategy.stock.last - strategy.next_buffer_above_water
181
- else
182
- raise 'zz4 - this cannot happen'
183
- end
184
- end
185
- puts! outs[0][:strikePrice], 'after calc next_buffer_above_water'
186
- puts! outs, 'outs'
187
-
188
- ## next_inner_delta
189
- outs = outs.select do |out|
190
- if 'CALL' == pos.put_call
191
- out_delta = out[:delta] rescue 1
192
- out_delta <= strategy.next_inner_delta
193
- elsif 'PUT' == pos.put_call
194
- out_delta = out[:delta] rescue 0
195
- out_delta <= strategy.next_inner_delta
196
- else
197
- raise 'zz5 - this cannot happen'
198
- end
199
- end
200
- puts! outs[0][:strikePrice], 'after calc next_inner_delta'
201
- puts! outs, 'outs'
202
-
203
- inner = outs[0]
204
- outs = outs.select do |out|
205
- if 'CALL' == pos.put_call
206
- out[:strikePrice] >= inner[:strikePrice].to_f + strategy.next_spread_amount
207
- elsif 'PUT' == pos.put_call
208
- out[:strikePrice] <= inner[:strikePrice].to_f - strategy.next_spread_amount
209
- end
210
- end
211
- outer = outs[0]
212
-
213
- if inner && outer
214
- o_attrs = {
215
- expires_on: next_expires_on,
216
- put_call: pos.put_call,
217
- stock_id: pos.stock_id,
218
- }
219
- inner_ = Iro::Option.new(o_attrs.merge({
220
- strike: inner[:strikePrice],
221
- begin_price: ( inner[:bid] + inner[:ask] )/2,
222
- begin_delta: inner[:delta],
223
- end_price: ( inner[:bid] + inner[:ask] )/2,
224
- end_delta: inner[:delta],
225
- }))
226
- outer_ = Iro::Option.new(o_attrs.merge({
227
- strike: outer[:strikePrice],
228
- begin_price: ( outer[:bid] + outer[:ask] )/2,
229
- begin_delta: outer[:delta],
230
- end_price: ( outer[:bid] + outer[:ask] )/2,
231
- end_delta: outer[:delta],
232
- }))
233
- pos.autonxt ||= Iro::Position.new
234
- pos.autonxt.update({
235
- prev_gain_loss_amount: 'a',
236
- status: 'proposed',
237
- stock: strategy.stock,
238
- inner: inner_,
239
- outer: outer_,
240
- inner_strike: inner_.strike,
241
- outer_strike: outer_.strike,
242
- begin_on: Time.now.to_date,
243
- expires_on: next_expires_on,
244
- purse: purse,
245
- strategy: strategy,
246
- quantity: 1,
247
- autoprev: pos,
248
- })
249
-
250
- pos.autonxt.sync
251
- pos.autonxt.save!
252
- pos.save
253
- return pos
254
-
255
- else
256
- throw 'zmq - should not happen'
257
- end
258
- end
259
-
260
-
261
-
262
- ## ok
263
- def next_expires_on
264
- out = expires_on.to_datetime.next_occurring(:monday).next_occurring(:friday)
265
- if !out.workday?
266
- out = Time.previous_business_day(out)
267
- end
268
- return out
269
- end
270
-
271
- ## ok
272
- def self.long
273
- where( long_or_short: Iro::Strategy::LONG )
274
- end
275
-
276
- ## ok
277
- def self.short
278
- where( long_or_short: Iro::Strategy::SHORT )
279
- end
280
-
281
- def to_s
282
- out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.long_or_short} ["
283
- if Iro::Strategy::LONG == long_or_short
284
- if outer.strike
285
- out = out + "$#{outer.strike}->"
286
- end
287
- out = out + "$#{inner.strike}"
288
- else
289
- out = out + "$#{inner.strike}"
290
- if outer.strike
291
- out = out + "<-$#{outer.strike}"
292
- end
293
- end
294
- out += "] "
295
- return out
296
- end
297
- end
298
-
299
-
@@ -1,93 +0,0 @@
1
-
2
- ##
3
- ## Specifically Option or Stock priceitem?
4
- ## Priceitems are intra-day! See Datapoint for daily data
5
- ##
6
- class Iro::Priceitem
7
- include Mongoid::Document
8
- include Mongoid::Timestamps
9
- store_in collection: 'iro_price_items'
10
-
11
- ## PUT, CALL, STOCK
12
- field :putCall, type: String ## kind
13
- field :symbol, type: String
14
- field :description, type: String
15
- field :ticker, type: String
16
- # belongs_to :stock, inverse_of: :priceitems
17
-
18
- field :bid, type: Float
19
- field :bidSize, type: Integer
20
- field :ask, type: Float
21
- field :askSize, type: Integer
22
- field :last, type: Float
23
-
24
- field :openPrice, type: Float
25
- field :lowPrice, type: Float
26
- field :highPrice, type: Float
27
- field :closePrice, type: Float
28
-
29
- field :quote_at, type: DateTime
30
- field :quoteTimeInLong, type: Integer
31
- field :timestamp, type: Integer
32
- field :totalVolume, type: Integer
33
- field :mark, type: Float
34
- field :exchangeName, type: String
35
- field :volatility, type: Float
36
-
37
- field :expirationDate, type: :date
38
- field :delta, type: Float
39
- field :gamma, type: Float
40
- field :theta, type: Float
41
- field :openInterest, type: Integer
42
- field :strikePrice, type: Float
43
-
44
- def self.my_find props={}
45
- lookup = { '$lookup': {
46
- 'from': 'iro_price_items',
47
- 'localField': 'date',
48
- 'foreignField': 'date',
49
- 'pipeline': [
50
- { '$sort': { 'value': -1 } },
51
- ],
52
- 'as': 'dates',
53
- } }
54
- lookup_merge = { '$replaceRoot': {
55
- 'newRoot': { '$mergeObjects': [
56
- { '$arrayElemAt': [ "$dates", 0 ] }, "$$ROOT"
57
- ] }
58
- } }
59
-
60
-
61
- match = { '$match': {
62
- 'date': {
63
- '$gte': props[:begin_on],
64
- '$lte': props[:end_on],
65
- }
66
- } }
67
-
68
- group = { '$group': {
69
- '_id': "$date",
70
- 'my_doc': { '$first': "$$ROOT" }
71
- } }
72
-
73
- outs = Iro::Date.collection.aggregate([
74
- match,
75
-
76
- lookup,
77
- lookup_merge,
78
-
79
- group,
80
- { '$replaceRoot': { 'newRoot': "$my_doc" } },
81
- # { '$replaceRoot': { 'newRoot': "$my_doc" } },
82
-
83
-
84
- { '$project': { '_id': 0, 'date': 1, 'value': 1 } },
85
- { '$sort': { 'date': 1 } },
86
- ])
87
-
88
- puts! 'result'
89
- pp outs.to_a
90
- # puts! outs.to_a, 'result'
91
- end
92
-
93
- end
@@ -1,72 +0,0 @@
1
-
2
- require 'distribution'
3
- N = Distribution::Normal
4
-
5
- class Iro::Purse
6
- include Mongoid::Document
7
- include Mongoid::Timestamps
8
- include Mongoid::Paranoia
9
- store_in collection: 'iro_purses'
10
-
11
- field :slug
12
- validates :slug, presence: true, uniqueness: true
13
- index({ slug: -1 }, { unique: true })
14
-
15
- has_many :positions, class_name: 'Iro::Position', inverse_of: :purse
16
-
17
- has_and_belongs_to_many :strategies, class_name: 'Iro::Strategy', inverse_of: :purses
18
-
19
- belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
20
-
21
- field :unit, type: :integer, default: 10
22
- ## with unit 10, .001
23
- ## with unit 100, .0001
24
- field :summary_unit, type: :float, default: 0.001
25
-
26
- ## for rolling only:
27
- field :height, type: :integer, default: 100
28
-
29
- field :mark_every_n_usd, type: :float, default: 1
30
- field :n_next_positions, type: :integer, default: 5
31
-
32
- field :available_amount, type: :float
33
- def available
34
- available_amount
35
- end
36
-
37
- def balance
38
- 0.01
39
- end
40
-
41
- def delta_wt_avg( begin_end, long_short, inner_outer )
42
- max_loss_total = 0
43
-
44
- out = positions.send( long_short ).map do |pos|
45
- max_loss_total += pos.max_loss * pos.q
46
- pos.max_loss * pos.q * pos.send( inner_outer ).send( "#{begin_end}_delta" )
47
- end
48
- # puts! out, 'delta_wt_avg 1'
49
- out = out.reduce( &:+ ) / max_loss_total rescue 0
50
- # puts! out, 'delta_wt_avg 2'
51
- return out
52
- end
53
- ## delta to plot percentage
54
- ## convert to normal between 0 and 3 std
55
- def delta_to_plot_p( *args )
56
- x = delta_wt_avg( *args ).abs
57
- if x < 0.5
58
- y = 1
59
- else
60
- y = 2 - 1/( 1.5 - x )
61
- end
62
- y_ = "#{ (y*100) .to_i}%"
63
- return y_
64
- end
65
-
66
- def to_s
67
- slug
68
- end
69
- def self.list
70
- [[nil,nil]] + all.map { |p| [p, p.id] }
71
- end
72
- end
@@ -1,134 +0,0 @@
1
- include Math
2
- require 'business_time'
3
-
4
- ##
5
- ## https://www.macrotrends.net/stocks/charts/META/meta-platforms/stock-price-history
6
- ##
7
- class Iro::Stock
8
- include Mongoid::Document
9
- include Mongoid::Timestamps
10
- include Mongoid::Paranoia
11
- store_in collection: 'iro_stocks'
12
-
13
- STATUS_ACTIVE = 'active'
14
- STATUS_INACTIVE = 'inactive'
15
- STATUSES = [ nil, 'active', 'inactive' ]
16
- def self.active
17
- where( status: STATUS_ACTIVE )
18
- end
19
- field :status, default: STATUS_ACTIVE
20
-
21
- field :ticker
22
- validates :ticker, uniqueness: true, presence: true
23
- index({ ticker: -1 }, { unique: true })
24
- def symbol; ticker; end
25
- def symbol= a; ticker = a; end
26
-
27
- field :last, type: :float
28
- field :options_price_increment, type: :float
29
-
30
- field :stdev, type: :float
31
-
32
- has_many :positions, class_name: 'Iro::Position', inverse_of: :stock
33
- has_many :strategies, class_name: 'Iro::Strategy', inverse_of: :stock
34
- has_many :purses, class_name: 'Iro::Purse', inverse_of: :stock
35
- has_many :options, class_name: 'Iro::Option', inverse_of: :stock
36
- has_many :priceitems, inverse_of: :stock
37
-
38
- default_scope { order_by({ ticker: :asc }) }
39
-
40
- ## my_find
41
- def self.f ticker
42
- self.find_by ticker: ticker
43
- end
44
-
45
- def to_s
46
- ticker
47
- end
48
- def self.list
49
- [[nil,nil]] + all.map { |sss| [ sss.ticker, sss.id ] }
50
- end
51
- def self.tickers_list
52
- [[nil,nil]] + all.map { |sss| [ sss.ticker, sss.ticker ] }
53
- end
54
-
55
- =begin
56
- stock = Iro::Stock.find_by( ticker: 'NVDA' )
57
-
58
- duration = 1.month
59
- stock.volatility_from_mo
60
-
61
- duration = 1.year
62
- stock.volatility_from_yr
63
-
64
- =end
65
- field :volatility, type: :float
66
- def volatility duration: 1.year, recompute: false
67
- if self[:volatility]
68
- if !recompute
69
- return self[:volatility]
70
- end
71
- end
72
-
73
- stock = self
74
- begin_on = Time.now - duration - 1.day
75
- points = Iro::Datapoint.where( kind: 'STOCK', symbol: stock.ticker,
76
- :date.gte => begin_on,
77
- ).order_by( date: :asc )
78
-
79
- puts! [points.first.date, points.last.date], "from,to"
80
-
81
- points_p = []
82
- points.each_with_index do |p, idx|
83
- next if idx == 0
84
- prev = points[idx-1]
85
-
86
- out = p.value / prev.value - 1
87
- points_p.push out
88
- end
89
- n = points_p.length
90
-
91
- avg = points_p.reduce(&:+) / n
92
- _sum_of_sq = []
93
- points_p.map do |p|
94
- _sum_of_sq.push( ( p - avg )*( p - avg ) )
95
- end
96
- sum_of_sq = _sum_of_sq.reduce( &:+ ) / n
97
-
98
- # n_periods = begin_on.to_date.business_days_until( Date.today )
99
- out = Math.sqrt( sum_of_sq )*sqrt( n )
100
- adjustment = 2.0
101
- out = out * adjustment
102
- puts! out, 'volatility (adjusted)'
103
- self.update volatility: out
104
- return out
105
- end
106
-
107
- def volatility_from_mo
108
- volatility( duration: 1.month )
109
- end
110
- def volatility_from_yr
111
- volatility( duration: 1.year )
112
- end
113
- def stdev recompute: nil
114
- if !self[:stdev] || recompute
115
- out = volatility_from_yr
116
- self[:stdev] = out
117
- save( validate: false )
118
- return out
119
- else
120
- self[:stdev]
121
- end
122
- end
123
-
124
- ## stdev
125
- ## From: https://stackoverflow.com/questions/19484891/how-do-i-find-the-standard-deviation-in-ruby
126
- # contents = [1,2,3,4,5,6,7,8,9]
127
- # n = contents.size # => 9
128
- # contents.map!(&:to_f) # => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
129
- # mean = contents.reduce(&:+)/n # => 5.0
130
- # sum_sqr = contents.map {|x| x * x}.reduce(&:+) # => 285.0
131
- # std_dev = Math.sqrt((sum_sqr - n * mean * mean)/(n-1)) # => 2.7386127875258306
132
-
133
-
134
- end