iron_warbler 2.0.7.19 → 2.0.7.21
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/javascript/iron_warbler/application.js +2 -0
- data/app/assets/javascript/iron_warbler/gameui.js +9 -0
- data/app/assets/stylesheets/iron_warbler/application.css +13 -3
- data/app/assets/stylesheets/iron_warbler/positions.scss +24 -5
- data/app/assets/stylesheets/iron_warbler/purses_gameui.scss +214 -0
- data/app/assets/stylesheets/iron_warbler/purses_gameui.scss-bk +112 -0
- data/app/assets/stylesheets/iron_warbler/utils.scss +81 -0
- data/app/controllers/iro/api/stocks_controller.rb +87 -0
- data/app/controllers/iro/api_controller.rb +19 -0
- data/app/controllers/iro/positions_controller.rb +35 -4
- data/app/controllers/iro/purses_controller.rb +11 -4
- data/app/controllers/iro/strategies_controller.rb +7 -7
- data/app/helpers/iro/application_helper.rb +5 -2
- data/app/jobs/tda_job.rb +14 -0
- data/app/models/iro/datapoint.rb +48 -8
- data/app/models/iro/position.rb +55 -14
- data/app/models/iro/price_item.rb +51 -1
- data/app/models/iro/purse.rb +4 -0
- data/app/models/iro/stock.rb +5 -2
- data/app/models/iro/strategy.rb +84 -10
- data/app/models/iro/trash/position_covered_call.rb +4 -0
- data/app/models/iro/trash/position_debit_spread.rb +251 -0
- data/app/models/tda/option.rb +7 -7
- data/app/views/iro/_main_header.haml +18 -17
- data/app/views/iro/api/stocks/show.jbuilder +11 -0
- data/app/views/iro/positions/_form.haml +11 -38
- data/app/views/iro/positions/_formpart_4data.haml +46 -0
- data/app/views/iro/positions/_gameui_covered_call.haml +44 -0
- data/app/views/iro/positions/_gameui_covered_call.haml-bk +59 -0
- data/app/views/iro/positions/_gameui_long_debit_call_spread.haml +37 -0
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +39 -0
- data/app/views/iro/positions/_header.haml +6 -0
- data/app/views/iro/positions/_header_covered_call.haml +6 -0
- data/app/views/iro/positions/_header_long_debit_call_spread.haml +14 -0
- data/app/views/iro/positions/_header_short_debit_put_spread.haml +1 -0
- data/app/views/iro/positions/_table.haml +120 -123
- data/app/views/iro/positions/roll.haml +83 -0
- data/app/views/iro/positions/trash/_header_short_debit_put_spread.haml +9 -0
- data/app/views/iro/purses/_form.haml +1 -0
- data/app/views/iro/purses/_form_extra_fields.haml +14 -0
- data/app/views/iro/purses/_header.haml +22 -0
- data/app/views/iro/purses/gameui.haml +19 -0
- data/app/views/iro/purses/gameui.haml-bk +44 -0
- data/app/views/iro/purses/gameui.haml-bk2 +89 -0
- data/app/views/iro/purses/show.haml +1 -7
- data/app/views/iro/stocks/_grid_is_long.haml +16 -0
- data/app/views/iro/stocks/_grid_is_short.haml +16 -0
- data/app/views/iro/strategies/_form.haml +12 -5
- data/app/views/iro/strategies/_show.haml +3 -2
- data/app/views/iro/strategies/_table.haml +16 -10
- data/app/views/iro/strategies/index.haml +8 -0
- data/app/views/layouts/iro/application.haml +1 -1
- data/config/routes.rb +15 -1
- data/lib/iro/engine.rb +2 -0
- data/lib/tasks/db_tasks.rake +9 -3
- metadata +31 -8
- data/app/assets/stylesheets/iron_warbler/alerts.css +0 -8
- data/app/assets/stylesheets/iron_warbler/datapoints.css +0 -0
- data/app/assets/stylesheets/iron_warbler/profiles.css +0 -4
- data/app/assets/stylesheets/iron_warbler/strategies.scss +0 -0
- data/app/assets/stylesheets/iron_warbler/utils.css +0 -44
- data/app/jobs/iro/application_job.rb-trash +0 -4
@@ -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
|
data/app/models/tda/option.rb
CHANGED
@@ -13,7 +13,7 @@ class Tda::Option
|
|
13
13
|
def self.get_chain params
|
14
14
|
opts = { symbol: params[:ticker] } ## use 'GME' as symbol here even though a symbol is eg 'GME_021023P2.5'
|
15
15
|
query = { apikey: ::TD_AMERITRADE[:apiKey] }.merge opts
|
16
|
-
|
16
|
+
puts! query, 'input opts'
|
17
17
|
|
18
18
|
path = "/v1/marketdata/chains"
|
19
19
|
out = self.get path, { query: query }
|
@@ -55,7 +55,7 @@ class Tda::Option
|
|
55
55
|
## 2023-03-18 _vp_ This is what I should be using to check if a position should be rolled.
|
56
56
|
##
|
57
57
|
def self.get_quote params
|
58
|
-
::Tda::Option.get_quotes(params)[0]
|
58
|
+
OpenStruct.new ::Tda::Option.get_quotes(params)[0]
|
59
59
|
end
|
60
60
|
|
61
61
|
##
|
@@ -79,18 +79,18 @@ class Tda::Option
|
|
79
79
|
if params[s]
|
80
80
|
opts[s] = params[s]
|
81
81
|
else
|
82
|
-
raise
|
82
|
+
raise Iro::InputError.new("Invalid input, missing '#{s}'.")
|
83
83
|
end
|
84
84
|
end
|
85
85
|
if params[:expirationDate]
|
86
86
|
opts[:fromDate] = opts[:toDate] = params[:expirationDate]
|
87
87
|
else
|
88
|
-
raise
|
88
|
+
raise Iro::InputError.new("Invalid input, missing 'date'.")
|
89
89
|
end
|
90
90
|
if params[:ticker]
|
91
91
|
opts[:symbol] = params[:ticker].upcase
|
92
92
|
else
|
93
|
-
raise
|
93
|
+
raise Iro::InputError.new("Invalid input, missing 'ticker'.")
|
94
94
|
end
|
95
95
|
|
96
96
|
if params[:strike]
|
@@ -98,7 +98,7 @@ class Tda::Option
|
|
98
98
|
end
|
99
99
|
|
100
100
|
query = { apikey: ::TD_AMERITRADE[:apiKey] }.merge opts
|
101
|
-
|
101
|
+
puts! query, 'input opts'
|
102
102
|
|
103
103
|
path = "/v1/marketdata/chains"
|
104
104
|
out = self.get path, { query: query }
|
@@ -120,7 +120,7 @@ class Tda::Option
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
-
|
123
|
+
puts! outs, 'outs'
|
124
124
|
return outs
|
125
125
|
end
|
126
126
|
|
@@ -1,23 +1,24 @@
|
|
1
1
|
|
2
|
-
.main-header.
|
2
|
+
.application-main-header.main-header
|
3
|
+
.maxwidth
|
3
4
|
|
4
|
-
|
5
|
-
|
5
|
+
%i.fa.fa-compress.collapse-expand#collapseHeaderTrading
|
6
|
+
Iron Warbler
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
-# .header.collapse-expand#IroMenu
|
9
|
+
-# %h5.title Iron Warbler
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
%ul
|
12
|
+
%li= link_to 'ROOT', root_path
|
13
|
+
%li
|
14
|
+
= link_to "Stocks (#{Iro::Stock.all.length})", stocks_path
|
15
|
+
-# %li= link_to 'Options', options_path
|
16
|
+
-# %li Max Pain
|
17
|
+
%li
|
18
|
+
= link_to "Alerts (#{Iro::Alert.all.length})", alerts_path
|
19
|
+
%li
|
20
|
+
= link_to "Purses (#{Iro::Purse.all.length})", purses_path
|
21
|
+
%li
|
22
|
+
= render '/iro/strategies/header'
|
22
23
|
|
23
24
|
|
@@ -1,59 +1,32 @@
|
|
1
1
|
|
2
|
-
.positions--form
|
2
|
+
.positions--form{ class: params[:long_or_short] || position.strategy&.long_or_short }
|
3
3
|
= form_for position do |f|
|
4
4
|
.actions
|
5
5
|
= f.submit
|
6
6
|
|
7
|
-
.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
%label Status
|
13
|
-
= f.select :status, options_for_select( Iro::Position::STATUSES, selected: position.status )
|
7
|
+
.d-flex
|
8
|
+
.field
|
9
|
+
%label Purse
|
10
|
+
-# = f.select :purse_id, options_for_select( @
|
11
|
+
= f.text_field :purse_id
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
%label Status
|
14
|
+
= f.select :status, options_for_select( Iro::Position::STATUSES, selected: position.status )
|
17
15
|
|
18
16
|
.field
|
19
|
-
%label
|
20
|
-
= f.select :
|
21
|
-
|
22
|
-
|
23
|
-
%label Kind
|
24
|
-
= f.select :kind, options_for_select( Iro::Position::KINDS, selected: position.kind )
|
17
|
+
%label Stock
|
18
|
+
= f.select :stock_id, options_for_select( @stocks_list, selected: position.stock_id )
|
25
19
|
%label Strategy
|
26
20
|
= f.select :strategy_id, options_for_select( @strategies_list, selected: position.strategy_id )
|
27
21
|
|
28
|
-
|
29
|
-
%label Strike
|
30
|
-
= f.text_field :strike
|
31
|
-
|
32
22
|
.field
|
33
23
|
%label Expires on
|
34
24
|
= f.text_field :expires_on
|
35
|
-
|
36
|
-
|
37
25
|
%label Quantity
|
38
26
|
= f.text_field :quantity
|
39
27
|
|
40
28
|
%br
|
41
|
-
|
42
|
-
|
43
|
-
.flex-row
|
44
|
-
%label Begin on
|
45
|
-
= f.text_field :begin_on
|
46
|
-
%label Begin price
|
47
|
-
= f.text_field :begin_price
|
48
|
-
%label Begin delta
|
49
|
-
= f.text_field :begin_delta
|
50
|
-
.flex-row
|
51
|
-
%label End on
|
52
|
-
= f.text_field :end_on
|
53
|
-
%label End price
|
54
|
-
= f.text_field :end_price
|
55
|
-
%label End delta
|
56
|
-
= f.text_field :end_delta
|
29
|
+
= render 'formpart_4data', f: f, position: position
|
57
30
|
|
58
31
|
.actions
|
59
32
|
= f.submit
|
@@ -0,0 +1,46 @@
|
|
1
|
+
.field
|
2
|
+
%label Begin on
|
3
|
+
= f.text_field :begin_on
|
4
|
+
|
5
|
+
.long-or-short-item
|
6
|
+
.d-flex
|
7
|
+
.field
|
8
|
+
%label Outer Strike
|
9
|
+
= f.number_field :outer_strike, step: 0.01
|
10
|
+
.field
|
11
|
+
%label Begin outer price
|
12
|
+
= f.text_field :begin_outer_price
|
13
|
+
.field
|
14
|
+
%label Begin outer delta
|
15
|
+
= f.text_field :begin_outer_delta
|
16
|
+
|
17
|
+
.d-flex
|
18
|
+
.field
|
19
|
+
%label Inner Strike
|
20
|
+
= f.number_field :inner_strike, step: 0.01
|
21
|
+
.field
|
22
|
+
%label Begin inner price
|
23
|
+
= f.text_field :begin_inner_price
|
24
|
+
.field
|
25
|
+
%label Begin inner delta
|
26
|
+
= f.text_field :begin_inner_delta
|
27
|
+
|
28
|
+
%br
|
29
|
+
.flex-row
|
30
|
+
%label End on
|
31
|
+
= f.text_field :end_on
|
32
|
+
.long-or-short-item
|
33
|
+
.d-flex
|
34
|
+
.field
|
35
|
+
%label End outer price
|
36
|
+
= f.text_field :end_outer_price
|
37
|
+
.field
|
38
|
+
%label End outer delta
|
39
|
+
= f.text_field :end_outer_delta
|
40
|
+
.d-flex
|
41
|
+
.field
|
42
|
+
%label End inner price
|
43
|
+
= f.text_field :end_inner_price
|
44
|
+
.field
|
45
|
+
%label End inner delta
|
46
|
+
= f.text_field :end_inner_delta
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
- pos = position
|
3
|
+
- stock = pos.stock
|
4
|
+
- nearest_strike = stock.last.round
|
5
|
+
- u = @unit # pixels per dollar
|
6
|
+
|
7
|
+
.collapse-expand.d-flex{ id: "gameui-pos-#{pos.id}" }
|
8
|
+
[<>]
|
9
|
+
.maxwidth= render "/iro/positions/header", pos: pos
|
10
|
+
.a
|
11
|
+
= render "/iro/positions/header_#{pos.strategy.kind}", pos: pos
|
12
|
+
.StockCoordinatesW
|
13
|
+
.StockCoordinates{ style: "height: #{pos.purse.height}px " }
|
14
|
+
= render "/iro/stocks/grid_#{pos.strategy.long_or_short}", stock: stock
|
15
|
+
|
16
|
+
.Origin{ style: "left: #{ ( nearest_strike - stock.last )* u}px" }
|
17
|
+
.label Last: #{pp_amount stock.last}
|
18
|
+
.c
|
19
|
+
|
20
|
+
-## Good, remove trash, leave as implemented.
|
21
|
+
-# - left = "#{ (stock.last - position.inner_strike - position.begin_inner_price) * u}px"
|
22
|
+
-# - width = "#{ position.begin_inner_price * u}px"
|
23
|
+
- left = "#{ (stock.last - pos.inner_strike) * u}px"
|
24
|
+
- width = "0px"
|
25
|
+
.PositionW{ style: "left: #{left} ; width: #{width} " }
|
26
|
+
.Position
|
27
|
+
.PositionC
|
28
|
+
|
29
|
+
- if position.net_amount >= 0
|
30
|
+
.Net.NetPositive{ style: "width: #{ (position.net_amount / 100) * u }px; right: 0" }
|
31
|
+
.label
|
32
|
+
net
|
33
|
+
= pp_amount position.net_amount
|
34
|
+
- else
|
35
|
+
.Net.NetNegative{ style: "width: #{ (-1 * position.net_amount / 100) * u }px; left: 100%" }
|
36
|
+
.label
|
37
|
+
net
|
38
|
+
= pp_amount position.net_amount
|
39
|
+
|
40
|
+
.Breakeven{ style: "width: #{ pos.begin_inner_price * u }px" }
|
41
|
+
.label
|
42
|
+
Breakeven:
|
43
|
+
= pos.begin_inner_price
|
44
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
- unit = u = 50 # pixels per dollar
|
3
|
+
- grid_size = 100 # dollars to each side of origin
|
4
|
+
.purses-gameui.padded
|
5
|
+
= render '/iro/purses/header', purse: @purse
|
6
|
+
|
7
|
+
- @positions.each_with_index do |position, idx|
|
8
|
+
- stock = position.stock
|
9
|
+
- if idx == 0
|
10
|
+
%h4= stock
|
11
|
+
- else
|
12
|
+
- prev_ = @positions[idx-1]
|
13
|
+
- if stock != prev_.stock
|
14
|
+
%hr
|
15
|
+
%h4= stock
|
16
|
+
|
17
|
+
.header
|
18
|
+
= position.quantity
|
19
|
+
= stock
|
20
|
+
= position.expires_on.to_datetime.strftime("%b %d")
|
21
|
+
= link_to '[roll]', roll_position_path(position)
|
22
|
+
= link_to '[~]', edit_position_path(position)
|
23
|
+
|
24
|
+
- nearest_strike = stock.last.round
|
25
|
+
.StockCoordinatesW
|
26
|
+
|
27
|
+
.StockCoordinates
|
28
|
+
.grid-mark.mark0
|
29
|
+
.label= nearest_strike
|
30
|
+
- (1...grid_size).each_with_index do |idx|
|
31
|
+
.grid-mark{ class: "mark#{idx}", style: "left: -#{idx * u}px" }
|
32
|
+
.label
|
33
|
+
= nearest_strike + idx
|
34
|
+
.grid-mark{ class: "mark-#{idx}", style: "left: #{idx * u}px" }
|
35
|
+
.label
|
36
|
+
= nearest_strike - idx
|
37
|
+
.Origin{ style: "left: #{ ( nearest_strike - stock.last )* u}px" }
|
38
|
+
.label
|
39
|
+
Last:
|
40
|
+
= stock.last
|
41
|
+
|
42
|
+
- left = "#{ (stock.last - position.inner_strike - position.begin_inner_price) * u}px"
|
43
|
+
- width = "#{ position.begin_inner_price * u}px"
|
44
|
+
.PositionW{ style: "left: #{left}; width: #{width}; " }
|
45
|
+
|
46
|
+
.Position
|
47
|
+
- if position.net_amount >= 0
|
48
|
+
.Net.NetPositive{ style: "width: #{ (position.net_amount / 100) * u }px; right: 0" }
|
49
|
+
.label
|
50
|
+
net
|
51
|
+
= pp_amount position.net_amount
|
52
|
+
- else
|
53
|
+
.Net.NetNegative{ style: "width: #{ (-1 * position.net_amount / 100) * u }px; left: 100%" }
|
54
|
+
.label
|
55
|
+
net
|
56
|
+
= pp_amount position.net_amount
|
57
|
+
|
58
|
+
%br
|
59
|
+
%br
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
- pos = position
|
3
|
+
- stock = pos.stock
|
4
|
+
- nearest_strike = stock.last.round
|
5
|
+
- u = @purse.unit # pixels per dollar
|
6
|
+
|
7
|
+
.collapse-expand.d-flex{ id: "gameui-pos-#{pos.id}" }
|
8
|
+
[<>]
|
9
|
+
.maxwidth= render "/iro/positions/header", pos: pos
|
10
|
+
.a
|
11
|
+
= render "/iro/positions/header_#{pos.strategy.kind}", pos: pos
|
12
|
+
.StockCoordinatesW
|
13
|
+
.StockCoordinates{ style: "height: #{pos.purse.height}px " }
|
14
|
+
= render "/iro/stocks/grid_#{pos.strategy.long_or_short}", stock: stock
|
15
|
+
|
16
|
+
.Origin{ style: "left: #{ ( nearest_strike - stock.last )* u}px" }
|
17
|
+
.label Last: #{pp_amount stock.last}
|
18
|
+
.c
|
19
|
+
|
20
|
+
- left = "#{ ( pos.outer_strike - stock.last ) * u}px"
|
21
|
+
- width = "#{ ( pos.inner_strike - pos.outer_strike ) * u}px"
|
22
|
+
.PositionW{ style: "left: #{left}; width: #{width}; " }
|
23
|
+
.Position
|
24
|
+
.MaxGain{ style: "width: #{pos.max_gain * u}px" }
|
25
|
+
|
26
|
+
- if pos.net_amount >= 0
|
27
|
+
.Net.NetPositive{ style: "width: #{ (pos.net_amount / 100) * u }px; right: 0" }
|
28
|
+
.label
|
29
|
+
net
|
30
|
+
= pp_amount pos.net_amount
|
31
|
+
- else
|
32
|
+
.Net.NetNegative{ style: "width: #{ (-1 * pos.net_amount / 100) * u }px; left: 100%" }
|
33
|
+
.label
|
34
|
+
net
|
35
|
+
= pp_amount pos.net_amount
|
36
|
+
.c
|
37
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
- pos = position
|
3
|
+
- stock = pos.stock
|
4
|
+
- nearest_strike = stock.last.round
|
5
|
+
- u = @unit # pixels per dollar
|
6
|
+
|
7
|
+
|
8
|
+
.collapse-expand.d-flex{ id: "gameui-pos-#{pos.id}" }
|
9
|
+
[<>]
|
10
|
+
.maxwidth= render "/iro/positions/header", pos: pos
|
11
|
+
.a
|
12
|
+
= render "/iro/positions/header_#{pos.strategy.kind}", pos: pos
|
13
|
+
.StockCoordinatesW
|
14
|
+
.StockCoordinates{ style: "height: #{pos.purse.height}px " }
|
15
|
+
= render "/iro/stocks/grid_#{pos.strategy.long_or_short}", stock: stock
|
16
|
+
|
17
|
+
.Origin{ style: "left: #{ ( nearest_strike - stock.last )* u}px" }
|
18
|
+
.label Last: #{pp_amount stock.last}
|
19
|
+
.c
|
20
|
+
|
21
|
+
-## these are different
|
22
|
+
- left = "#{ ( stock.last - pos.outer_strike ) * u}px"
|
23
|
+
- width = "#{ ( pos.outer_strike - pos.inner_strike ) * u}px"
|
24
|
+
.PositionW{ style: "left: #{left}; width: #{width}; " }
|
25
|
+
.Position
|
26
|
+
.MaxGain{ style: "width: #{pos.max_gain * u}px" }
|
27
|
+
|
28
|
+
- if pos.net_amount >= 0
|
29
|
+
.Net.NetPositive{ style: "width: #{ (pos.net_amount / 100) * u }px; right: 0" }
|
30
|
+
.label
|
31
|
+
net
|
32
|
+
= pp_amount pos.net_amount
|
33
|
+
- else
|
34
|
+
.Net.NetNegative{ style: "width: #{ (-1 * pos.net_amount / 100) * u }px; left: 100%" }
|
35
|
+
.label
|
36
|
+
net
|
37
|
+
= pp_amount pos.net_amount
|
38
|
+
.c
|
39
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
-## Same file for the spreads
|
4
|
+
%i.fa.fa-expand.collapse-expand.floaty-collapse{ id: "gameui-pos-detail-#{pos.id}" }
|
5
|
+
.maxwidth
|
6
|
+
%ul.m-0.p-0
|
7
|
+
%li <b>outer,inner_strike:</b> #{pp_amount pos.outer_strike} -> #{pp_amount pos.inner_strike}
|
8
|
+
%li
|
9
|
+
<b>begin_outer,inner_price:</b> #{pp_amount pos.begin_outer_price} -> #{pp_amount pos.begin_inner_price}
|
10
|
+
|
11
|
+
-## yes *100
|
12
|
+
<b>max gain:</b> #{pp_amount pos.max_gain} :: #{pp_amount pos.max_gain * 100 * pos.q}
|
13
|
+
- ## no *100
|
14
|
+
%li <b>Net:</b> #{pp_amount pos.net_amount} :: #{pp_amount pos.net_amount * pos.q} <i>#{pp_percent pos.net_percent}</i>
|