iron_warbler 2.0.7.25 → 2.0.7.26
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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/iron_warbler/Card.scss +6 -0
- data/app/assets/stylesheets/iron_warbler/positions.scss +1 -1
- data/app/assets/stylesheets/iron_warbler/purses_summary.scss +31 -19
- data/app/controllers/iro/application_controller.rb +6 -0
- data/app/controllers/iro/positions_controller.rb +183 -200
- data/app/controllers/iro/purses_controller.rb +3 -1
- data/app/controllers/iro/stocks_controller.rb +2 -2
- data/app/models/iro/option.rb +38 -150
- data/app/models/iro/option_black_scholes.rb +149 -0
- data/app/models/iro/position.rb +154 -209
- data/app/models/iro/purse.rb +34 -4
- data/app/models/iro/strategy.rb +49 -47
- data/app/views/iro/_main_header.haml +4 -2
- data/app/views/iro/options/_show_mini.haml +8 -0
- data/app/views/iro/positions/_form.haml +8 -3
- data/app/views/iro/positions/_formpart_4data.haml +41 -38
- data/app/views/iro/positions/_gameui_long_debit_call_spread.haml +4 -4
- data/app/views/iro/positions/_gameui_long_debit_call_spread.haml-trash +42 -0
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +1 -0
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml-trash +40 -0
- data/app/views/iro/positions/_header.haml +2 -0
- data/app/views/iro/positions/_header_long_debit_call_spread.haml +43 -25
- data/app/views/iro/positions/_prepare_long_debit_call_spread.haml +2 -3
- data/app/views/iro/positions/_prepare_short_debit_put_spread.haml +2 -1
- data/app/views/iro/positions/_table.haml +25 -26
- data/app/views/iro/positions/prepare.haml +6 -4
- data/app/views/iro/positions/prepare2.haml +15 -4
- data/app/views/iro/purses/_form_extra_fields.haml +7 -3
- data/app/views/iro/purses/_header.haml +19 -5
- data/app/views/iro/purses/_summary.haml +69 -62
- data/app/views/iro/purses/show.haml +1 -1
- data/app/views/iro/strategies/_form.haml +26 -19
- data/app/views/iro/strategies/_show.haml +6 -4
- data/config/routes.rb +5 -3
- metadata +7 -2
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +0 -40
data/app/models/iro/position.rb
CHANGED
@@ -5,11 +5,18 @@ class Iro::Position
|
|
5
5
|
include Mongoid::Paranoia
|
6
6
|
store_in collection: 'iro_positions'
|
7
7
|
|
8
|
+
field :prev_gain_loss_amount, type: :float
|
8
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
|
9
14
|
|
10
15
|
STATUS_ACTIVE = 'active'
|
11
16
|
STATUS_PROPOSED = 'proposed'
|
12
|
-
|
17
|
+
STATUS_CLOSED = 'closed'
|
18
|
+
STATUS_PENDING = 'pending'
|
19
|
+
STATUSES = [ nil, STATUS_ACTIVE, STATUS_PROPOSED, STATUS_CLOSED, STATUS_PENDING ]
|
13
20
|
field :status
|
14
21
|
validates :status, presence: true
|
15
22
|
scope :active, ->{ where( status: 'active' ) }
|
@@ -17,27 +24,40 @@ class Iro::Position
|
|
17
24
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
18
25
|
index({ purse_id: 1, ticker: 1 })
|
19
26
|
|
20
|
-
belongs_to :prev, class_name: 'Iro::Position', inverse_of: :nxt
|
21
|
-
has_one :nxt, class_name: 'Iro::Position', inverse_of: :prev
|
22
|
-
|
23
27
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
|
24
|
-
|
25
|
-
stock&.ticker || '-'
|
26
|
-
end
|
28
|
+
delegate :ticker, to: :stock
|
27
29
|
|
28
30
|
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
31
|
+
field :long_or_short
|
32
|
+
|
33
|
+
def put_call
|
34
|
+
case strategy.kind
|
35
|
+
when Iro::Strategy::KIND_LONG_DEBIT_CALL_SPREAD
|
36
|
+
put_call = 'CALL'
|
37
|
+
when Iro::Strategy::KIND_SHORT_DEBIT_PUT_SPREAD
|
38
|
+
put_call = 'PUT'
|
39
|
+
when Iro::Strategy::KIND_COVERED_CALL
|
40
|
+
put_call = 'CALL'
|
41
|
+
end
|
42
|
+
end
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
belongs_to :prev, class_name: 'Iro::Position', inverse_of: :nxt, optional: true
|
45
|
+
belongs_to :autoprev, class_name: 'Iro::Position', inverse_of: :autonxt, optional: true
|
46
|
+
## there are many of these, for viewing on the 'roll' view
|
47
|
+
has_many :nxt, class_name: 'Iro::Position', inverse_of: :prev
|
48
|
+
has_one :autonxt, class_name: 'Iro::Position', inverse_of: :autoprev
|
49
|
+
|
50
|
+
## Options
|
32
51
|
|
33
|
-
belongs_to :outer, class_name: 'Iro::Option', inverse_of: :outer
|
34
52
|
belongs_to :inner, class_name: 'Iro::Option', inverse_of: :inner
|
53
|
+
belongs_to :outer, class_name: 'Iro::Option', inverse_of: :outer
|
54
|
+
accepts_nested_attributes_for :inner, :outer
|
35
55
|
|
36
56
|
field :outer_strike, type: :float
|
37
57
|
# validates :outer_strike, presence: true
|
38
58
|
|
39
59
|
field :inner_strike, type: :float
|
40
|
-
validates :inner_strike, presence: true
|
60
|
+
# validates :inner_strike, presence: true
|
41
61
|
|
42
62
|
field :expires_on
|
43
63
|
validates :expires_on, presence: true
|
@@ -47,18 +67,8 @@ class Iro::Position
|
|
47
67
|
def q; quantity; end
|
48
68
|
|
49
69
|
field :begin_on
|
50
|
-
field :begin_outer_price, type: :float
|
51
|
-
field :begin_outer_delta, type: :float
|
52
|
-
|
53
|
-
field :begin_inner_price, type: :float
|
54
|
-
field :begin_inner_delta, type: :float
|
55
70
|
|
56
71
|
field :end_on
|
57
|
-
field :end_outer_price, type: :float
|
58
|
-
field :end_outer_delta, type: :float
|
59
|
-
|
60
|
-
field :end_inner_price, type: :float
|
61
|
-
field :end_inner_delta, type: :float
|
62
72
|
|
63
73
|
def begin_delta
|
64
74
|
strategy.send("begin_delta_#{strategy.kind}", self)
|
@@ -86,7 +96,7 @@ class Iro::Position
|
|
86
96
|
end_delta: out[:delta],
|
87
97
|
end_price: out[:last],
|
88
98
|
})
|
89
|
-
print '
|
99
|
+
print '^'
|
90
100
|
end
|
91
101
|
|
92
102
|
def net_percent
|
@@ -101,237 +111,172 @@ class Iro::Position
|
|
101
111
|
def max_loss # each
|
102
112
|
strategy.send("max_loss_#{strategy.kind}", self)
|
103
113
|
end
|
104
|
-
# def gain_loss_amount
|
105
|
-
# strategy.send("gain_loss_amount_#{strategy.kind}", self)
|
106
|
-
# end
|
107
|
-
|
108
|
-
|
109
|
-
field :next_delta, type: :float
|
110
|
-
field :next_outcome, type: :float
|
111
|
-
field :next_symbol
|
112
|
-
field :next_mark
|
113
|
-
field :next_reasons, type: :array, default: []
|
114
|
-
field :rollp, type: :float
|
115
114
|
|
116
115
|
|
117
|
-
## covered call
|
118
|
-
# def sync
|
119
|
-
# puts! [ inner_strike, expires_on, stock.ticker ], 'init sync'
|
120
|
-
# out = Tda::Option.get_quote({
|
121
|
-
# contractType: 'CALL',
|
122
|
-
# strike: inner_strike,
|
123
|
-
# expirationDate: expires_on,
|
124
|
-
# ticker: stock.ticker,
|
125
|
-
# })
|
126
|
-
# puts! out, 'sync'
|
127
|
-
# self.end_inner_price = ( out.bid + out.ask ) / 2
|
128
|
-
# self.end_inner_delta = out.delta
|
129
|
-
# end
|
130
|
-
|
131
|
-
## long call spread
|
132
|
-
# def sync
|
133
|
-
# # puts! [
|
134
|
-
# # [ inner_strike, expires_on, stock.ticker ],
|
135
|
-
# # [ outer_strike, expires_on, stock.ticker ],
|
136
|
-
# # ], 'init sync inner, outer'
|
137
|
-
# inner = Tda::Option.get_quote({
|
138
|
-
# contractType: 'CALL',
|
139
|
-
# strike: inner_strike,
|
140
|
-
# expirationDate: expires_on,
|
141
|
-
# ticker: stock.ticker,
|
142
|
-
# })
|
143
|
-
# outer = Tda::Option.get_quote({
|
144
|
-
# contractType: 'CALL',
|
145
|
-
# strike: outer_strike,
|
146
|
-
# expirationDate: expires_on,
|
147
|
-
# ticker: stock.ticker,
|
148
|
-
# })
|
149
|
-
# puts! [inner, outer], 'sync inner, outer'
|
150
|
-
# self.end_outer_price = ( outer.bid + outer.ask ) / 2
|
151
|
-
# self.end_outer_delta = outer.delta
|
152
|
-
|
153
|
-
# self.end_inner_price = ( inner.bid + inner.ask ) / 2
|
154
|
-
# self.end_inner_delta = inner.delta
|
155
|
-
# end
|
156
|
-
|
157
116
|
def sync
|
158
|
-
|
159
|
-
|
160
|
-
[ inner_strike, expires_on, stock.ticker ],
|
161
|
-
[ outer_strike, expires_on, stock.ticker ],
|
162
|
-
], 'init sync inner, outer'
|
163
|
-
inner = Tda::Option.get_quote({
|
164
|
-
contractType: put_call,
|
165
|
-
strike: inner_strike,
|
166
|
-
expirationDate: expires_on,
|
167
|
-
ticker: stock.ticker,
|
168
|
-
})
|
169
|
-
outer = Tda::Option.get_quote({
|
170
|
-
contractType: put_call,
|
171
|
-
strike: outer_strike,
|
172
|
-
expirationDate: expires_on,
|
173
|
-
ticker: stock.ticker,
|
174
|
-
})
|
175
|
-
puts! [inner, outer], 'sync inner, outer'
|
176
|
-
self.end_outer_price = ( outer.bid + outer.ask ) / 2
|
177
|
-
self.end_outer_delta = outer.delta
|
178
|
-
|
179
|
-
self.end_inner_price = ( inner.bid + inner.ask ) / 2
|
180
|
-
self.end_inner_delta = inner.delta
|
117
|
+
inner.sync
|
118
|
+
outer.sync
|
181
119
|
end
|
182
|
-
def sync_short_debit_put_spread
|
183
|
-
puts! [
|
184
|
-
[ inner_strike, expires_on, stock.ticker ],
|
185
|
-
[ outer_strike, expires_on, stock.ticker ],
|
186
|
-
], 'init sync inner, outer'
|
187
|
-
inner = Tda::Option.get_quote({
|
188
|
-
contractType: 'PUT',
|
189
|
-
strike: inner_strike,
|
190
|
-
expirationDate: expires_on,
|
191
|
-
ticker: stock.ticker,
|
192
|
-
})
|
193
|
-
outer = Tda::Option.get_quote({
|
194
|
-
contractType: 'PUT',
|
195
|
-
strike: outer_strike,
|
196
|
-
expirationDate: expires_on,
|
197
|
-
ticker: stock.ticker,
|
198
|
-
})
|
199
|
-
puts! [inner, outer], 'sync inner, outer'
|
200
|
-
self.end_outer_price = ( outer.bid + outer.ask ) / 2
|
201
|
-
self.end_outer_delta = outer.delta
|
202
120
|
|
203
|
-
self.end_inner_price = ( inner.bid + inner.ask ) / 2
|
204
|
-
self.end_inner_delta = inner.delta
|
205
|
-
end
|
206
121
|
|
207
122
|
##
|
208
123
|
## decisions
|
209
124
|
##
|
210
125
|
|
126
|
+
field :next_reasons, type: :array, default: []
|
127
|
+
field :rollp, type: :float
|
128
|
+
|
129
|
+
## should_roll?
|
211
130
|
def calc_rollp
|
212
131
|
self.next_reasons = []
|
213
|
-
self.next_symbol
|
214
|
-
self.next_delta
|
132
|
+
# self.next_symbol = nil
|
133
|
+
# self.next_delta = nil
|
215
134
|
|
216
135
|
out = strategy.send( "calc_rollp_#{strategy.kind}", self )
|
217
136
|
|
218
137
|
self.rollp = out[0]
|
219
138
|
self.next_reasons.push out[1]
|
220
139
|
save
|
221
|
-
|
222
|
-
# update({
|
223
|
-
# next_delta: next_position[:delta],
|
224
|
-
# next_outcome: next_position[:mark] - end_price,
|
225
|
-
# next_symbol: next_position[:symbol],
|
226
|
-
# next_mark: next_position[:mark],
|
227
|
-
# should_rollp: out,
|
228
|
-
# # status: Iro::Position::STATE_PROPOSED,
|
229
|
-
# })
|
230
|
-
end
|
231
|
-
|
232
|
-
|
233
|
-
## expires_on = cc.expires_on ; nil
|
234
|
-
def can_roll?
|
235
|
-
## only if less than 7 days left
|
236
|
-
( expires_on.to_date - Time.now.to_date ).to_i < 7
|
237
140
|
end
|
238
141
|
|
239
|
-
|
240
|
-
|
241
|
-
strike < current_underlying_strike + strategy.buffer_above_water
|
242
|
-
end
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
## 2023-03-18 _vp_ Continue.
|
247
|
-
## 2023-03-19 _vp_ Continue.
|
248
|
-
## 2023-08-05 _vp_ an Important method
|
249
|
-
##
|
250
|
-
## expires_on = cc.expires_on ; strategy = cc.strategy ; ticker = cc.ticker ; end_price = cc.end_price ; next_expires_on = cc.next_expires_on ; nil
|
251
|
-
##
|
252
|
-
## out.map { |p| [ p[:strikePrice], p[:delta] ] }
|
253
|
-
##
|
254
|
-
def next_position
|
255
|
-
return @next_position if @next_position
|
256
|
-
return {} if ![ STATUS_ACTIVE, STATUS_PROPOSED ].include?( status )
|
142
|
+
def calc_nxt
|
143
|
+
pos = self
|
257
144
|
|
258
145
|
## 7 days ahead - not configurable so far
|
259
|
-
|
260
|
-
|
146
|
+
outs = Tda::Option.get_quotes({
|
147
|
+
contractType: pos.put_call,
|
261
148
|
expirationDate: next_expires_on,
|
262
|
-
|
149
|
+
ticker: ticker,
|
263
150
|
})
|
151
|
+
outs_bk = outs.dup
|
152
|
+
|
153
|
+
# byebug
|
264
154
|
|
265
|
-
##
|
266
|
-
|
267
|
-
out
|
268
|
-
|
155
|
+
## strike price
|
156
|
+
outs = outs.select do |out|
|
157
|
+
out[:bidSize]+out[:askSize] > 0
|
158
|
+
end
|
159
|
+
outs = outs.select do |out|
|
160
|
+
if Iro::Strategy::SHORT == pos.long_or_short
|
161
|
+
out[:strikePrice] > strategy.next_buffer_above_water + strategy.stock.last
|
162
|
+
elsif Iro::Strategy::LONG == pos.long_or_short
|
163
|
+
out[:strikePrice] < strategy.stock.last - strategy.next_buffer_above_water
|
164
|
+
else
|
165
|
+
throw 'zz4 - this cannot happen'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
puts! outs[0][:strikePrice], 'after calc next_buffer_above_water'
|
169
|
+
|
170
|
+
outs = outs.select do |out|
|
171
|
+
if Iro::Strategy::SHORT == pos.long_or_short
|
172
|
+
out[:strikePrice] > strategy.next_inner_strike
|
173
|
+
elsif Iro::Strategy::LONG == pos.long_or_short
|
174
|
+
out[:strikePrice] < strategy.next_inner_strike
|
175
|
+
else
|
176
|
+
throw 'zz3 - this cannot happen'
|
269
177
|
end
|
270
|
-
# next_reasons.push "buffer_above_water above #{current_underlying_strike + strategy.buffer_above_water}"
|
271
178
|
end
|
179
|
+
puts! outs[0][:strikePrice], 'after calc next_inner_strike'
|
180
|
+
|
181
|
+
## delta
|
182
|
+
outs = outs.select do |out|
|
183
|
+
out_delta = out[:delta].abs rescue 0
|
184
|
+
out_delta >= strategy.next_inner_delta.abs
|
185
|
+
end
|
186
|
+
puts! outs[0][:strikePrice], 'after calc next_inner_delta'
|
272
187
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
188
|
+
inner = outs[0]
|
189
|
+
outs = outs.select do |out|
|
190
|
+
out[:strikePrice] >= inner[:strikePrice].to_f + strategy.next_spread_amount
|
191
|
+
end
|
192
|
+
outer = outs[0]
|
193
|
+
|
194
|
+
# byebug
|
195
|
+
|
196
|
+
if inner && outer
|
197
|
+
o_attrs = {
|
198
|
+
expires_on: next_expires_on,
|
199
|
+
put_call: pos.put_call,
|
200
|
+
stock_id: pos.stock_id,
|
201
|
+
}
|
202
|
+
inner_ = Iro::Option.new(o_attrs.merge({
|
203
|
+
strike: inner[:strikePrice],
|
204
|
+
begin_price: ( inner[:bid] + inner[:ask] )/2,
|
205
|
+
begin_delta: inner[:delta],
|
206
|
+
end_price: ( inner[:bid] + inner[:ask] )/2,
|
207
|
+
end_delta: inner[:delta],
|
208
|
+
}))
|
209
|
+
outer_ = Iro::Option.new(o_attrs.merge({
|
210
|
+
strike: outer[:strikePrice],
|
211
|
+
begin_price: ( outer[:bid] + outer[:ask] )/2,
|
212
|
+
begin_delta: outer[:delta],
|
213
|
+
end_price: ( outer[:bid] + outer[:ask] )/2,
|
214
|
+
end_delta: outer[:delta],
|
215
|
+
}))
|
216
|
+
pos.autonxt ||= Iro::Position.new
|
217
|
+
pos.autonxt.update({
|
218
|
+
prev_gain_loss_amount: 'a',
|
219
|
+
status: 'proposed',
|
220
|
+
stock: strategy.stock,
|
221
|
+
inner: inner_,
|
222
|
+
outer: outer_,
|
223
|
+
inner_strike: inner_.strike,
|
224
|
+
outer_strike: outer_.strike,
|
225
|
+
begin_on: Time.now.to_date,
|
226
|
+
expires_on: next_expires_on,
|
227
|
+
purse: purse,
|
228
|
+
strategy: strategy,
|
229
|
+
quantity: 1,
|
230
|
+
autoprev: pos,
|
231
|
+
})
|
277
232
|
|
278
233
|
# byebug
|
279
234
|
|
280
|
-
|
281
|
-
|
282
|
-
out.each_with_index do |i, idx|
|
283
|
-
next if idx == 0
|
284
|
-
if i[:last] < end_price
|
285
|
-
prev ||= i
|
286
|
-
end
|
287
|
-
end
|
288
|
-
out = [ prev ]
|
235
|
+
autonxt.sync
|
236
|
+
autonxt.save!
|
289
237
|
|
290
238
|
else
|
291
|
-
|
292
|
-
## @TODO: test! _vp_ 2023-03-19
|
293
|
-
|
294
|
-
## next_min_strike
|
295
|
-
if strategy.next_min_strike.present?
|
296
|
-
out = out.select do |i|
|
297
|
-
i[:strikePrice] >= strategy.next_min_strike
|
298
|
-
end
|
299
|
-
# next_reasons.push "next_min_strike above #{strategy.next_min_strike}"
|
300
|
-
end
|
301
|
-
# json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_min_strike'
|
302
|
-
|
303
|
-
## max_delta
|
304
|
-
if strategy.next_max_delta.present?
|
305
|
-
out = out.select do |i|
|
306
|
-
i[:delta] = 0.0 if i[:delta] == "NaN"
|
307
|
-
i[:delta] <= strategy.next_max_delta
|
308
|
-
end
|
309
|
-
# next_reasons.push "next_max_delta below #{strategy.next_max_delta}"
|
310
|
-
end
|
311
|
-
# json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_max_delta'
|
239
|
+
throw 'zmq - should not happen'
|
312
240
|
end
|
313
|
-
|
314
|
-
@next_position = out[0] || {}
|
315
241
|
end
|
316
242
|
|
317
|
-
|
243
|
+
|
244
|
+
|
245
|
+
## ok
|
318
246
|
def next_expires_on
|
319
|
-
out = expires_on.
|
320
|
-
|
321
|
-
out = out
|
322
|
-
end
|
323
|
-
while !out.workday?
|
324
|
-
out = out - 1.day
|
247
|
+
out = expires_on.to_datetime.next_occurring(:monday).next_occurring(:friday)
|
248
|
+
if !out.workday?
|
249
|
+
out = Time.previous_business_day(out )
|
325
250
|
end
|
326
251
|
return out
|
327
252
|
end
|
328
253
|
|
254
|
+
## ok
|
255
|
+
def self.long
|
256
|
+
where( long_or_short: Iro::Strategy::LONG )
|
257
|
+
end
|
258
|
+
|
259
|
+
## ok
|
260
|
+
def self.short
|
261
|
+
where( long_or_short: Iro::Strategy::SHORT )
|
262
|
+
end
|
263
|
+
|
329
264
|
def to_s
|
330
265
|
out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.kind_short} ["
|
331
|
-
if
|
332
|
-
|
266
|
+
if Iro::Strategy::LONG == long_or_short
|
267
|
+
if outer.strike
|
268
|
+
out = out + "$#{outer.strike}->"
|
269
|
+
end
|
270
|
+
out = out + "$#{inner.strike}"
|
271
|
+
else
|
272
|
+
out = out + "$#{inner.strike}"
|
273
|
+
if outer.strike
|
274
|
+
out = out + "<-$#{outer.strike}"
|
275
|
+
end
|
333
276
|
end
|
334
|
-
out
|
277
|
+
out += "] "
|
335
278
|
return out
|
336
279
|
end
|
337
280
|
end
|
281
|
+
|
282
|
+
|
data/app/models/iro/purse.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
|
2
|
+
require 'distribution'
|
3
|
+
N = Distribution::Normal
|
4
|
+
|
2
5
|
class Iro::Purse
|
3
6
|
include Mongoid::Document
|
4
7
|
include Mongoid::Timestamps
|
@@ -9,23 +12,50 @@ class Iro::Purse
|
|
9
12
|
validates :slug, presence: true, uniqueness: true
|
10
13
|
index({ slug: -1 }, { unique: true })
|
11
14
|
|
12
|
-
has_many :positions,
|
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
|
13
18
|
|
14
19
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
|
15
20
|
|
16
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
|
17
25
|
|
18
26
|
## for rolling only:
|
19
27
|
field :height, type: :integer, default: 100
|
20
28
|
|
21
29
|
field :mark_every_n_usd, type: :float, default: 1
|
22
30
|
field :n_next_positions, type: :integer, default: 5
|
23
|
-
## with unit 10, sum_scale .001
|
24
|
-
## with unit 100, sum_scale .0001
|
25
|
-
field :summary_scale, type: :float, default: 0.001
|
26
31
|
|
27
32
|
field :available_amount, type: :float
|
28
33
|
|
34
|
+
def delta_wt_avg( begin_end, long_short, inner_outer )
|
35
|
+
max_loss_total = 0
|
36
|
+
|
37
|
+
out = positions.send( long_short ).map do |pos|
|
38
|
+
max_loss_total += pos.max_loss * pos.q
|
39
|
+
pos.max_loss * pos.q * pos.send( inner_outer ).send( "#{begin_end}_delta" )
|
40
|
+
end
|
41
|
+
puts! out, 'delta_wt_avg 1'
|
42
|
+
out = out.reduce( &:+ ) / max_loss_total rescue 0
|
43
|
+
puts! out, 'delta_wt_avg 2'
|
44
|
+
return out
|
45
|
+
end
|
46
|
+
## delta to plot percentage
|
47
|
+
## convert to normal between 0 and 3 std
|
48
|
+
def delta_to_plot_p( *args )
|
49
|
+
x = delta_wt_avg( *args ).abs
|
50
|
+
if x < 0.5
|
51
|
+
y = 1
|
52
|
+
else
|
53
|
+
y = 2 - 1/( 1.5 - x )
|
54
|
+
end
|
55
|
+
y_ = "#{ (y*100) .to_i}%"
|
56
|
+
return y_
|
57
|
+
end
|
58
|
+
|
29
59
|
def to_s
|
30
60
|
slug
|
31
61
|
end
|