wco_models 3.1.0.264 → 3.1.0.266
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/wco/application.scss +0 -3
- data/app/assets/stylesheets/wco/main.scss +31 -10
- data/app/helpers/wco/application_helper.rb +2 -2
- data/app/models/iro/option.rb +3 -98
- data/app/models/iro/option.rb-bk +62 -0
- data/app/models/iro/position.rb +42 -25
- data/app/models/iro/purse.rb +1 -33
- data/app/models/iro/strategy.rb +36 -32
- data/app/models/tda/order.rb +77 -7
- data/app/models/wco_email/config.rb +15 -0
- data/app/views/wco/profiles/_form.haml +5 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d52ff07afe73ec8c7240fbfb85a78a1cd4bf66c9db7c49fd69d5e7c3c56114aa
|
|
4
|
+
data.tar.gz: 33592a3dd04ab62d82c36d77070323735112bdb5966f8ba3009ff182c593f51e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88fc66fef4da9de8509ff2a46088229c73ea4041aa2aee67c1cd2771dbec93bafa164b6a6d39b7e312e3f86000b36cfc554fb33f9677b495fde357df60ed1739
|
|
7
|
+
data.tar.gz: 816df1b774e34f02dea1d1f23176a1ba5e0c00f84a1c04afb40084cd79af2671d1e28194b70ce8f90db6c239b672cc352ae8d27a00b9ac49e5f60977d49fad06
|
|
@@ -70,16 +70,6 @@ table.bordered {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
table.sticky thead,
|
|
74
|
-
table thead.sticky {
|
|
75
|
-
background: #ccc;
|
|
76
|
-
|
|
77
|
-
th {
|
|
78
|
-
position: sticky;
|
|
79
|
-
top: 0;
|
|
80
|
-
z-index: 10;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
73
|
|
|
84
74
|
/* C */
|
|
85
75
|
|
|
@@ -93,6 +83,11 @@ textarea.monospace {
|
|
|
93
83
|
padding: 0.6em;
|
|
94
84
|
}
|
|
95
85
|
|
|
86
|
+
.crossed,
|
|
87
|
+
.crossout {
|
|
88
|
+
text-decoration: line-through;
|
|
89
|
+
}
|
|
90
|
+
|
|
96
91
|
/* D */
|
|
97
92
|
|
|
98
93
|
.d-flex {
|
|
@@ -250,10 +245,36 @@ label.required {
|
|
|
250
245
|
|
|
251
246
|
|
|
252
247
|
/* S */
|
|
248
|
+
|
|
253
249
|
.spacer-small {
|
|
254
250
|
height: 20px;
|
|
255
251
|
}
|
|
256
252
|
|
|
253
|
+
div.sticky,
|
|
254
|
+
h2.sticky,
|
|
255
|
+
h3.sticky,
|
|
256
|
+
h4.sticky,
|
|
257
|
+
h5.sticky {
|
|
258
|
+
position: sticky;
|
|
259
|
+
top: 0.5em;
|
|
260
|
+
z-index: 10;
|
|
261
|
+
|
|
262
|
+
border-radius: 0.5em;
|
|
263
|
+
padding: 0.5em;
|
|
264
|
+
background: rgba(255,255,255, 0.5);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
table.sticky thead,
|
|
268
|
+
table thead.sticky {
|
|
269
|
+
background: #ccc;
|
|
270
|
+
|
|
271
|
+
th {
|
|
272
|
+
position: sticky;
|
|
273
|
+
top: 0;
|
|
274
|
+
z-index: 10;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
257
278
|
/* T */
|
|
258
279
|
|
|
259
280
|
.title {
|
|
@@ -34,8 +34,8 @@ module Wco::ApplicationHelper
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def pp_amount a, config = { precision: 2 }
|
|
37
|
-
return '
|
|
38
|
-
return '
|
|
37
|
+
return '___' if !a
|
|
38
|
+
return '___' if a.class == String
|
|
39
39
|
return number_to_currency a, precision: config[:precision]
|
|
40
40
|
# "$#{'%.2f' % a}"
|
|
41
41
|
end
|
data/app/models/iro/option.rb
CHANGED
|
@@ -10,17 +10,10 @@ class Iro::Option
|
|
|
10
10
|
|
|
11
11
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
|
|
12
12
|
def ticker; stock.ticker; end
|
|
13
|
-
# field :ticker
|
|
14
|
-
# validates :ticker, presence: true
|
|
15
13
|
|
|
16
14
|
CALL = 'CALL'
|
|
17
15
|
PUT = 'PUT'
|
|
18
16
|
|
|
19
|
-
## for now, recompute every time
|
|
20
|
-
# field :symbol
|
|
21
|
-
## each option can be a leg in a position, no uniqueness
|
|
22
|
-
# validates :symbol, uniqueness: true, presence: true
|
|
23
|
-
|
|
24
17
|
field :put_call, type: :string # 'PUT' or 'CALL'
|
|
25
18
|
validates :put_call, presence: true
|
|
26
19
|
|
|
@@ -29,11 +22,6 @@ class Iro::Option
|
|
|
29
22
|
field :strike, type: :float
|
|
30
23
|
validates :strike, presence: true
|
|
31
24
|
|
|
32
|
-
def to_s
|
|
33
|
-
"#{symbol} :: #{expires_on.strftime('%Y-%m-%d')} #{put_call} #{strike}"
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
|
|
37
25
|
field :expires_on, type: :date
|
|
38
26
|
validates :expires_on, presence: true
|
|
39
27
|
def self.expirations_list full: false, n: 5
|
|
@@ -70,38 +58,13 @@ class Iro::Option
|
|
|
70
58
|
|
|
71
59
|
field :last, type: :float
|
|
72
60
|
|
|
73
|
-
## for
|
|
74
|
-
## "COST_030626C1030"
|
|
75
|
-
def symbol_old
|
|
76
|
-
if !self[:symbol]
|
|
77
|
-
p_c_ = put_call == 'PUT' ? 'P' : 'C'
|
|
78
|
-
strike_ = strike.to_i == strike ? strike.to_i : strike
|
|
79
|
-
sym = "#{stock.ticker}_#{expires_on.strftime("%m%d%y")}#{p_c_}#{strike_}" # XYZ_011819P45
|
|
80
|
-
self[:symbol] = sym
|
|
81
|
-
save
|
|
82
|
-
end
|
|
83
|
-
self[:symbol]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
## for schwab
|
|
61
|
+
## for schwab, eg:
|
|
87
62
|
## "COST 260306C01030000"
|
|
88
63
|
def symbol
|
|
89
64
|
p_c_ = put_call == 'PUT' ? 'P' : 'C'
|
|
90
65
|
strike_ = format("%08d", (strike.to_f * 1000).round)
|
|
91
66
|
sym = "#{stock.ticker.ljust(6)}#{expires_on.strftime("%y%m%d")}#{p_c_}#{strike_}"
|
|
92
67
|
end
|
|
93
|
-
=begin
|
|
94
|
-
def symbol_trash ## it persists - which I dont do right now
|
|
95
|
-
if !self[:symbol]
|
|
96
|
-
p_c_ = put_call == 'PUT' ? 'P' : 'C'
|
|
97
|
-
strike_ = format("%08d", (strike.to_f * 1000).round)
|
|
98
|
-
sym = "#{stock.ticker.ljust(6)}#{expires_on.strftime("%y%m%d")}#{p_c_}#{strike_}"
|
|
99
|
-
self[:symbol] = sym
|
|
100
|
-
save
|
|
101
|
-
end
|
|
102
|
-
self[:symbol]
|
|
103
|
-
end
|
|
104
|
-
=end
|
|
105
68
|
|
|
106
69
|
# before_save :sync, if: ->() { !Rails.env.test? } ## do not sync in test
|
|
107
70
|
def sync
|
|
@@ -117,65 +80,7 @@ class Iro::Option
|
|
|
117
80
|
self.save! ## 2026-02-19 this must be present.
|
|
118
81
|
end
|
|
119
82
|
|
|
120
|
-
def
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
%w| put call |.each do |contractType|
|
|
124
|
-
dates = hash["#{contractType}ExpDateMap"]
|
|
125
|
-
dates.each do |_date, strikes| ## _date="2023-02-10:5"
|
|
126
|
-
date = _date.split(':')[0].to_date.to_s
|
|
127
|
-
outs[date] ||= {
|
|
128
|
-
'all' => {},
|
|
129
|
-
'call' => {},
|
|
130
|
-
'put' => {},
|
|
131
|
-
'summary' => {},
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
strikes.each do |_strike, _v| ## _strike="18.5"
|
|
135
|
-
strike = _strike.to_f
|
|
136
|
-
|
|
137
|
-
## calls
|
|
138
|
-
mem_c = 0
|
|
139
|
-
strikes.keys.reverse.each do |_key|
|
|
140
|
-
if _key == _strike
|
|
141
|
-
break
|
|
142
|
-
end
|
|
143
|
-
key = _key.to_f
|
|
144
|
-
tmp = hash["callExpDateMap"][_date][_key][0]['openInterest'] * ( key - strike )
|
|
145
|
-
mem_c += tmp
|
|
146
|
-
end
|
|
147
|
-
outs[date]['call'][_strike] = mem_c
|
|
148
|
-
|
|
149
|
-
## puts
|
|
150
|
-
mem_p = 0
|
|
151
|
-
strikes.keys.each do |_key|
|
|
152
|
-
if _key == _strike
|
|
153
|
-
break
|
|
154
|
-
end
|
|
155
|
-
key = _key.to_f
|
|
156
|
-
tmp = hash["putExpDateMap"][_date][_key][0]['openInterest'] * ( strike - key )
|
|
157
|
-
mem_p += tmp
|
|
158
|
-
end
|
|
159
|
-
outs[date]['put'][_strike] = mem_p
|
|
160
|
-
outs[date]['all'][_strike] = mem_c + mem_p
|
|
161
|
-
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
## compute summary
|
|
167
|
-
outs.each do |date, types|
|
|
168
|
-
all = types['all']
|
|
169
|
-
outs[date]['summary'] = { 'value' => all.keys[0] }
|
|
170
|
-
all.each do |strike, amount|
|
|
171
|
-
if amount < all[ outs[date]['summary']['value'] ]
|
|
172
|
-
outs[date]['summary']['value'] = strike
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
return outs
|
|
83
|
+
def to_s
|
|
84
|
+
"#{symbol} :: #{expires_on.strftime('%Y-%m-%d')} #{put_call} #{strike}"
|
|
178
85
|
end
|
|
179
|
-
|
|
180
|
-
|
|
181
86
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
def self.max_pain hash
|
|
4
|
+
outs = {}
|
|
5
|
+
|
|
6
|
+
%w| put call |.each do |contractType|
|
|
7
|
+
dates = hash["#{contractType}ExpDateMap"]
|
|
8
|
+
dates.each do |_date, strikes| ## _date="2023-02-10:5"
|
|
9
|
+
date = _date.split(':')[0].to_date.to_s
|
|
10
|
+
outs[date] ||= {
|
|
11
|
+
'all' => {},
|
|
12
|
+
'call' => {},
|
|
13
|
+
'put' => {},
|
|
14
|
+
'summary' => {},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
strikes.each do |_strike, _v| ## _strike="18.5"
|
|
18
|
+
strike = _strike.to_f
|
|
19
|
+
|
|
20
|
+
## calls
|
|
21
|
+
mem_c = 0
|
|
22
|
+
strikes.keys.reverse.each do |_key|
|
|
23
|
+
if _key == _strike
|
|
24
|
+
break
|
|
25
|
+
end
|
|
26
|
+
key = _key.to_f
|
|
27
|
+
tmp = hash["callExpDateMap"][_date][_key][0]['openInterest'] * ( key - strike )
|
|
28
|
+
mem_c += tmp
|
|
29
|
+
end
|
|
30
|
+
outs[date]['call'][_strike] = mem_c
|
|
31
|
+
|
|
32
|
+
## puts
|
|
33
|
+
mem_p = 0
|
|
34
|
+
strikes.keys.each do |_key|
|
|
35
|
+
if _key == _strike
|
|
36
|
+
break
|
|
37
|
+
end
|
|
38
|
+
key = _key.to_f
|
|
39
|
+
tmp = hash["putExpDateMap"][_date][_key][0]['openInterest'] * ( strike - key )
|
|
40
|
+
mem_p += tmp
|
|
41
|
+
end
|
|
42
|
+
outs[date]['put'][_strike] = mem_p
|
|
43
|
+
outs[date]['all'][_strike] = mem_c + mem_p
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
## compute summary
|
|
50
|
+
outs.each do |date, types|
|
|
51
|
+
all = types['all']
|
|
52
|
+
outs[date]['summary'] = { 'value' => all.keys[0] }
|
|
53
|
+
all.each do |strike, amount|
|
|
54
|
+
if amount < all[ outs[date]['summary']['value'] ]
|
|
55
|
+
outs[date]['summary']['value'] = strike
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
return outs
|
|
61
|
+
end
|
|
62
|
+
|
data/app/models/iro/position.rb
CHANGED
|
@@ -5,12 +5,6 @@ class Iro::Position
|
|
|
5
5
|
include Mongoid::Paranoia
|
|
6
6
|
store_in collection: 'iro_positions'
|
|
7
7
|
|
|
8
|
-
## @trash, use next_gain_loss_amount instead
|
|
9
|
-
# field :prev_gain_loss_amount, type: :float
|
|
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
8
|
field :next_gain_loss_amount, type: :float
|
|
15
9
|
|
|
16
10
|
|
|
@@ -23,14 +17,22 @@ class Iro::Position
|
|
|
23
17
|
STATUSES = [ nil, STATUS_CLOSED, STATUS_ACTIVE, STATUS_PREPARE, STATUS_PROPOSED, STATUS_PENDING ]
|
|
24
18
|
field :status
|
|
25
19
|
validates :status, presence: true
|
|
26
|
-
scope :active,
|
|
27
|
-
|
|
20
|
+
scope :active, ->{ where( status: 'active' ) }
|
|
21
|
+
scope :proposed, ->{ where( status: 'proposed' ) }
|
|
22
|
+
|
|
28
23
|
|
|
29
24
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
|
30
25
|
index({ purse_id: 1, ticker: 1 })
|
|
31
26
|
|
|
32
27
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
|
|
33
|
-
|
|
28
|
+
field :ticker
|
|
29
|
+
def ticker
|
|
30
|
+
if !self[:ticker]
|
|
31
|
+
self[:ticker] = stock.ticker
|
|
32
|
+
self.save
|
|
33
|
+
end
|
|
34
|
+
self[:ticker]
|
|
35
|
+
end
|
|
34
36
|
|
|
35
37
|
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
|
36
38
|
delegate :long_or_short, to: :strategy
|
|
@@ -83,6 +85,7 @@ class Iro::Position
|
|
|
83
85
|
field :end_on
|
|
84
86
|
|
|
85
87
|
field :schwab_order_id, type: :integer
|
|
88
|
+
field :schwab_status
|
|
86
89
|
|
|
87
90
|
def begin_delta
|
|
88
91
|
strategy.send("begin_delta_#{strategy.kind}", self)
|
|
@@ -132,6 +135,16 @@ class Iro::Position
|
|
|
132
135
|
print '^'
|
|
133
136
|
end
|
|
134
137
|
|
|
138
|
+
|
|
139
|
+
field :pending_price
|
|
140
|
+
|
|
141
|
+
## place2 = credit-spread
|
|
142
|
+
def place2_price
|
|
143
|
+
pos = self
|
|
144
|
+
out = pos.inner.begin_price - pos.outer.begin_price
|
|
145
|
+
return out.round(2)
|
|
146
|
+
end
|
|
147
|
+
|
|
135
148
|
def roll_price
|
|
136
149
|
pos = self
|
|
137
150
|
out = pos.autoprev.outer.end_price - pos.autoprev.inner.end_price + pos.inner.begin_price - pos.outer.begin_price
|
|
@@ -231,33 +244,35 @@ class Iro::Position
|
|
|
231
244
|
# puts! outs, '#calc_nxt.outs -> 2'
|
|
232
245
|
|
|
233
246
|
## next_inner_strike
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if Iro::Strategy::
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
if strategy.next_inner_strike.present?
|
|
248
|
+
outs = outs.select do |out|
|
|
249
|
+
if Iro::Strategy::CREDIT == pos.credit_or_debit
|
|
250
|
+
if Iro::Strategy::SHORT == pos.long_or_short
|
|
251
|
+
## short credit call
|
|
252
|
+
out[:strikePrice] >= strategy.next_inner_strike
|
|
253
|
+
elsif Iro::Strategy::LONG == pos.long_or_short
|
|
254
|
+
## long credit put
|
|
255
|
+
out[:strikePrice] <= strategy.next_inner_strike
|
|
256
|
+
end
|
|
257
|
+
else
|
|
258
|
+
raise 'zt3 - @TODO: implement, debit spreads'
|
|
242
259
|
end
|
|
243
|
-
else
|
|
244
|
-
raise 'zt3 - @TODO: implement, debit spreads'
|
|
245
260
|
end
|
|
261
|
+
puts! outs[0][:strikePrice], 'after calc next_inner_strike'
|
|
262
|
+
# puts! outs, 'outs'
|
|
246
263
|
end
|
|
247
|
-
puts! outs[0][:strikePrice], 'after calc next_inner_strike'
|
|
248
|
-
# puts! outs, 'outs'
|
|
249
264
|
|
|
250
|
-
##
|
|
265
|
+
## next_usd_above_mark
|
|
251
266
|
outs = outs.select do |out|
|
|
252
267
|
if Iro::Strategy::SHORT == pos.long_or_short
|
|
253
|
-
out[:strikePrice] > strategy.
|
|
268
|
+
out[:strikePrice] > strategy.next_usd_above_mark + strategy.stock.last
|
|
254
269
|
elsif Iro::Strategy::LONG == pos.long_or_short
|
|
255
|
-
out[:strikePrice] < strategy.stock.last - strategy.
|
|
270
|
+
out[:strikePrice] < strategy.stock.last - strategy.next_usd_above_mark
|
|
256
271
|
else
|
|
257
272
|
raise 'zt4 - this cannot happen'
|
|
258
273
|
end
|
|
259
274
|
end
|
|
260
|
-
puts! outs[0][:strikePrice], 'after calc
|
|
275
|
+
puts! outs[0][:strikePrice], 'after calc next_usd_above_mark'
|
|
261
276
|
puts! outs, 'outs'
|
|
262
277
|
|
|
263
278
|
## next_inner_delta
|
|
@@ -310,7 +325,9 @@ class Iro::Position
|
|
|
310
325
|
status: 'proposed',
|
|
311
326
|
stock: strategy.stock,
|
|
312
327
|
inner_strike: inner_attrs[:strike],
|
|
328
|
+
inner_attributes: inner_attrs,
|
|
313
329
|
outer_strike: outer_attrs[:strike],
|
|
330
|
+
outer_attributes: outer_attrs,
|
|
314
331
|
begin_on: Time.now.to_date,
|
|
315
332
|
expires_on: next_expires_on,
|
|
316
333
|
purse: purse,
|
data/app/models/iro/purse.rb
CHANGED
|
@@ -28,39 +28,7 @@ class Iro::Purse
|
|
|
28
28
|
field :n_next_positions, type: :integer, default: 5
|
|
29
29
|
|
|
30
30
|
field :available_amount, type: :float
|
|
31
|
-
validates :available_amount, presence: true
|
|
32
|
-
def available
|
|
33
|
-
available_amount
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# def balance
|
|
37
|
-
# 0.01
|
|
38
|
-
# end
|
|
39
|
-
|
|
40
|
-
def delta_wt_avg( begin_end, long_short, inner_outer )
|
|
41
|
-
max_loss_total = 0
|
|
42
|
-
|
|
43
|
-
out = positions.send( long_short ).map do |pos|
|
|
44
|
-
max_loss_total += pos.max_loss * pos.q
|
|
45
|
-
pos.max_loss * pos.q * pos.send( inner_outer ).send( "#{begin_end}_delta" )
|
|
46
|
-
end
|
|
47
|
-
# puts! out, 'delta_wt_avg 1'
|
|
48
|
-
out = out.reduce( &:+ ) / max_loss_total rescue 0
|
|
49
|
-
# puts! out, 'delta_wt_avg 2'
|
|
50
|
-
return out
|
|
51
|
-
end
|
|
52
|
-
## delta to plot percentage
|
|
53
|
-
## convert to normal between 0 and 3 std
|
|
54
|
-
def delta_to_plot_p( *args )
|
|
55
|
-
x = delta_wt_avg( *args ).abs
|
|
56
|
-
if x < 0.5
|
|
57
|
-
y = 1
|
|
58
|
-
else
|
|
59
|
-
y = 2 - 1/( 1.5 - x )
|
|
60
|
-
end
|
|
61
|
-
y_ = "#{ (y*100) .to_i}%"
|
|
62
|
-
return y_
|
|
63
|
-
end
|
|
31
|
+
# validates :available_amount, presence: true
|
|
64
32
|
|
|
65
33
|
def to_s
|
|
66
34
|
slug
|
data/app/models/iro/strategy.rb
CHANGED
|
@@ -79,7 +79,6 @@ class Iro::Strategy
|
|
|
79
79
|
|
|
80
80
|
field :threshold_usd_above_mark, type: :float
|
|
81
81
|
validates :threshold_usd_above_mark, presence: true
|
|
82
|
-
def buffer_above_water; threshold_usd_above_mark; end
|
|
83
82
|
|
|
84
83
|
field :threshold_pos_delta, type: :float # offensive: roll b/c markets are going my way
|
|
85
84
|
field :threshold_neg_delta, type: :float # defensive: roll b/c markets are going against me
|
|
@@ -206,6 +205,7 @@ class Iro::Strategy
|
|
|
206
205
|
## decisions
|
|
207
206
|
##
|
|
208
207
|
|
|
208
|
+
## do not use!
|
|
209
209
|
def calc_rollp_covered_call p
|
|
210
210
|
stock.reload
|
|
211
211
|
|
|
@@ -213,10 +213,10 @@ class Iro::Strategy
|
|
|
213
213
|
return [ 0.99, '0 DTE, must exit' ]
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
-
if ( stock.last -
|
|
216
|
+
if ( stock.last - threshold_usd_above_mark ) < p.inner.strike
|
|
217
217
|
return [ 0.98, "Last #{'%.2f' % stock.last} is " +
|
|
218
|
-
"#{'%.2f' % [p.inner.strike +
|
|
219
|
-
"below #{'%.2f' % [p.inner.strike +
|
|
218
|
+
"#{'%.2f' % [p.inner.strike + threshold_usd_above_mark - stock.last]} " +
|
|
219
|
+
"below #{'%.2f' % [p.inner.strike + threshold_usd_above_mark]} water" ]
|
|
220
220
|
end
|
|
221
221
|
|
|
222
222
|
if p.inner.end_delta < threshold_pos_delta
|
|
@@ -230,7 +230,7 @@ class Iro::Strategy
|
|
|
230
230
|
return [ 0.33, '-' ]
|
|
231
231
|
end
|
|
232
232
|
|
|
233
|
-
##
|
|
233
|
+
## do not use!
|
|
234
234
|
def calc_rollp_long_debit_call_spread p
|
|
235
235
|
stock.reload
|
|
236
236
|
|
|
@@ -241,10 +241,10 @@ class Iro::Strategy
|
|
|
241
241
|
return [ 0.99, '1 DTE, must exit' ]
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
-
if ( stock.last -
|
|
244
|
+
if ( stock.last - threshold_usd_above_mark ) < p.inner.strike
|
|
245
245
|
return [ 0.95, "Last #{'%.2f' % stock.last} is " +
|
|
246
|
-
"#{'%.2f' % [stock.last - p.inner.strike -
|
|
247
|
-
"below #{'%.2f' % [p.inner.strike +
|
|
246
|
+
"#{'%.2f' % [stock.last - p.inner.strike - threshold_usd_above_mark]} " +
|
|
247
|
+
"below #{'%.2f' % [p.inner.strike + threshold_usd_above_mark]} water" ]
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
if p.inner.end_delta < threshold_pos_delta
|
|
@@ -258,14 +258,15 @@ class Iro::Strategy
|
|
|
258
258
|
return [ 0.33, '-' ]
|
|
259
259
|
end
|
|
260
260
|
|
|
261
|
-
## 2025-10-12
|
|
261
|
+
## 2025-10-12 continue
|
|
262
|
+
## 2026-04-05 continue
|
|
262
263
|
def calc_rollp_long_credit_put_spread p
|
|
263
264
|
stock.reload
|
|
264
265
|
|
|
265
266
|
# puts! p, '#calc_rollp_long_credit_put_spread'
|
|
266
267
|
# puts! p.inner, 'p.inner'
|
|
267
|
-
puts! stock, 'stock'
|
|
268
|
-
puts! attributes, 'strategy attributes'
|
|
268
|
+
# puts! stock, 'stock'
|
|
269
|
+
# puts! attributes, 'strategy attributes'
|
|
269
270
|
|
|
270
271
|
if ( p.expires_on.to_date - Time.now.to_date ).to_i < 1
|
|
271
272
|
return [ 0.99, '0 DTE, must exit' ]
|
|
@@ -274,24 +275,28 @@ class Iro::Strategy
|
|
|
274
275
|
return [ 0.99, '1 DTE, must exit' ]
|
|
275
276
|
end
|
|
276
277
|
|
|
277
|
-
if ( stock.last -
|
|
278
|
-
return [ 0.95, "Last
|
|
279
|
-
|
|
280
|
-
"below #{'%.2f' % [p.inner.strike + buffer_above_water]} water" ]
|
|
278
|
+
if ( stock.last - threshold_usd_above_mark ) < p.inner.strike
|
|
279
|
+
return [ 0.95, ":threshold_usd_above_mark <br />Last $#{'%.2f' % stock.last} is " +
|
|
280
|
+
"#{'%.2f' % [stock.last - p.inner.strike]} near #{p.inner.strike} but should be greater than #{threshold_usd_above_mark} ." ]
|
|
281
281
|
end
|
|
282
282
|
|
|
283
|
-
if p.inner.end_delta < threshold_pos_delta
|
|
284
|
-
return [ 0.79, "
|
|
283
|
+
if p.inner.end_delta.abs < threshold_pos_delta
|
|
284
|
+
return [ 0.79, ":threshold_pos_delta <br />Offensive roll: delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} ." ]
|
|
285
|
+
end
|
|
286
|
+
if p.inner.end_delta.abs > threshold_neg_delta
|
|
287
|
+
return [ 0.79, ":threshold_neg_delta <br />Defensive roll: delta #{p.inner.end_delta} is higher than #{threshold_neg_delta} ." ]
|
|
285
288
|
end
|
|
286
289
|
|
|
287
|
-
if
|
|
288
|
-
|
|
290
|
+
if threshold_netp.present?
|
|
291
|
+
if 1 - p.inner.end_price/p.inner.begin_price > threshold_netp
|
|
292
|
+
return [ 0.51, ":threshold_netp <br />made enough #{'%.02f' % [(1.0 - p.inner.end_price/p.inner.begin_price )*100]}% profit^" ]
|
|
293
|
+
end
|
|
289
294
|
end
|
|
290
295
|
|
|
291
296
|
return [ 0.33, '-' ]
|
|
292
297
|
end
|
|
293
298
|
|
|
294
|
-
##
|
|
299
|
+
## do not use!
|
|
295
300
|
def calc_rollp_short_debit_put_spread p
|
|
296
301
|
stock.reload
|
|
297
302
|
|
|
@@ -299,10 +304,10 @@ class Iro::Strategy
|
|
|
299
304
|
return [ 0.99, "< #{threshold_dte}DTE, must exit" ]
|
|
300
305
|
end
|
|
301
306
|
|
|
302
|
-
if stock.last +
|
|
307
|
+
if stock.last + threshold_usd_above_mark > p.inner.strike
|
|
303
308
|
return [ 0.98, "Last #{'%.2f' % stock.last} is " +
|
|
304
|
-
"#{'%.2f' % [stock.last +
|
|
305
|
-
"above #{'%.2f' % [p.inner.strike -
|
|
309
|
+
"#{'%.2f' % [stock.last + threshold_usd_above_mark - p.inner.strike]} " +
|
|
310
|
+
"above #{'%.2f' % [p.inner.strike - threshold_usd_above_mark]} water" ]
|
|
306
311
|
end
|
|
307
312
|
|
|
308
313
|
if p.inner.end_delta.abs < threshold_pos_delta.abs
|
|
@@ -317,6 +322,7 @@ class Iro::Strategy
|
|
|
317
322
|
end
|
|
318
323
|
|
|
319
324
|
## 2026-02-21 ok
|
|
325
|
+
## 2026-04-05 ok
|
|
320
326
|
def calc_rollp_short_credit_call_spread p
|
|
321
327
|
puts! p, 'calc_rollp_short_credit_call_spread...'
|
|
322
328
|
stock.reload
|
|
@@ -325,10 +331,9 @@ class Iro::Strategy
|
|
|
325
331
|
return [ 0.99, "< #{threshold_dte}DTE, must exit" ]
|
|
326
332
|
end
|
|
327
333
|
|
|
328
|
-
if stock.last +
|
|
329
|
-
return [ 0.95, "Last
|
|
330
|
-
"#{'%.2f' % [stock.last
|
|
331
|
-
"above #{'%.2f' % [p.inner.strike - buffer_above_water]} water" ]
|
|
334
|
+
if stock.last + threshold_usd_above_mark > p.inner.strike
|
|
335
|
+
return [ 0.95, ":threshold_usd_above_mark <br />Last $#{'%.2f' % stock.last} is " +
|
|
336
|
+
"#{'%.2f' % [p.inner.strike - stock.last]} near #{p.inner.strike} but should be greater than #{threshold_usd_above_mark} ." ]
|
|
332
337
|
end
|
|
333
338
|
|
|
334
339
|
## defensive
|
|
@@ -342,8 +347,10 @@ class Iro::Strategy
|
|
|
342
347
|
return [ 0.69, "Delta #{p.inner.end_delta} is lower than #{threshold_pos_delta} offensive threshold." ]
|
|
343
348
|
end
|
|
344
349
|
|
|
345
|
-
if
|
|
346
|
-
|
|
350
|
+
if threshold_netp.present?
|
|
351
|
+
if p.net_percent > threshold_netp
|
|
352
|
+
return [ 0.51, "made enough #{'%.0f' % [p.net_percent*100]}% > #{"%.2f" % [threshold_netp*100]}% profit," ]
|
|
353
|
+
end
|
|
347
354
|
end
|
|
348
355
|
|
|
349
356
|
return [ 0.33, '-' ]
|
|
@@ -357,9 +364,6 @@ class Iro::Strategy
|
|
|
357
364
|
end
|
|
358
365
|
|
|
359
366
|
|
|
360
|
-
# def slug
|
|
361
|
-
# "#{kind} #{stock}"
|
|
362
|
-
# end
|
|
363
367
|
def to_s
|
|
364
368
|
"#{kind} #{stock} #{descr}"
|
|
365
369
|
end
|
data/app/models/tda/order.rb
CHANGED
|
@@ -18,7 +18,7 @@ class Tda::Order
|
|
|
18
18
|
},
|
|
19
19
|
})
|
|
20
20
|
puts! results, 'results'
|
|
21
|
-
return results
|
|
21
|
+
return results.deep_symbolize_keys
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
## not used - the hash is stored
|
|
@@ -33,6 +33,70 @@ class Tda::Order
|
|
|
33
33
|
puts! results, 'results'
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def self.get_orders
|
|
37
|
+
profile = Wco::Profile.pi
|
|
38
|
+
today = Time.now.strftime("%Y-%m-%d")
|
|
39
|
+
results = self.get("/accounts/#{profile.schwab_account_hash}/orders", {
|
|
40
|
+
headers: {
|
|
41
|
+
accept: 'application/json',
|
|
42
|
+
Authorization: "Bearer #{profile[:schwab_exec_access_token]}",
|
|
43
|
+
},
|
|
44
|
+
query: {
|
|
45
|
+
fromEnteredTime: "#{today}T00:00:00Z",
|
|
46
|
+
toEnteredTime: "#{today}T23:59:59Z",
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
puts! results, 'get_orders() results'
|
|
50
|
+
puts! results.code, 'results.code'
|
|
51
|
+
return results
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.cancel_order!( id )
|
|
55
|
+
profile = Wco::Profile.pi
|
|
56
|
+
results = self.delete("/accounts/#{profile.schwab_account_hash}/orders/#{id}", {
|
|
57
|
+
headers: {
|
|
58
|
+
accept: 'application/json',
|
|
59
|
+
Authorization: "Bearer #{profile[:schwab_exec_access_token]}",
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
puts! results, 'cancel_order!() results'
|
|
63
|
+
puts! results.code, 'results.code'
|
|
64
|
+
# if !results.code == '200'
|
|
65
|
+
# throw 'could not cancel order'
|
|
66
|
+
# end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.credit_spread_q pos
|
|
70
|
+
query = {
|
|
71
|
+
orderType: pos.place2_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
72
|
+
session: "NORMAL",
|
|
73
|
+
duration: "DAY",
|
|
74
|
+
price: pos.pending_price,
|
|
75
|
+
orderStrategyType: "SINGLE",
|
|
76
|
+
orderLegCollection: [
|
|
77
|
+
## open
|
|
78
|
+
{
|
|
79
|
+
instruction: "BUY_TO_OPEN",
|
|
80
|
+
quantity: pos.q,
|
|
81
|
+
instrument: {
|
|
82
|
+
symbol: pos.outer.symbol,
|
|
83
|
+
assetType: "OPTION",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
instruction: "SELL_TO_OPEN",
|
|
88
|
+
quantity: pos.q,
|
|
89
|
+
instrument: {
|
|
90
|
+
symbol: pos.inner.symbol,
|
|
91
|
+
assetType: "OPTION",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
}
|
|
96
|
+
return query
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
## obsolete, I don't do covered calls anymore?
|
|
36
100
|
def self.roll_covered_call_q pos
|
|
37
101
|
roll_price = pos.inner.begin_price - pos.autoprev.inner.end_price
|
|
38
102
|
query = {
|
|
@@ -67,12 +131,12 @@ class Tda::Order
|
|
|
67
131
|
return query
|
|
68
132
|
end
|
|
69
133
|
|
|
70
|
-
def self.
|
|
134
|
+
def self.roll_credit_call_spread_q pos
|
|
71
135
|
query = {
|
|
72
|
-
orderType:
|
|
136
|
+
orderType: pos.roll_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
73
137
|
session: "NORMAL",
|
|
74
138
|
duration: "DAY",
|
|
75
|
-
price:
|
|
139
|
+
price: pos.roll_price.abs.to_s,
|
|
76
140
|
orderStrategyType: "SINGLE",
|
|
77
141
|
orderLegCollection: [
|
|
78
142
|
## close
|
|
@@ -116,8 +180,8 @@ class Tda::Order
|
|
|
116
180
|
return query
|
|
117
181
|
end
|
|
118
182
|
|
|
119
|
-
def self.place_order query
|
|
120
|
-
puts! query, '#place_order'
|
|
183
|
+
def self.place_order! query
|
|
184
|
+
# puts! query, '#place_order'
|
|
121
185
|
|
|
122
186
|
profile = Wco::Profile.pi
|
|
123
187
|
results = self.post("/accounts/#{profile.schwab_account_hash}/orders", {
|
|
@@ -128,8 +192,14 @@ class Tda::Order
|
|
|
128
192
|
},
|
|
129
193
|
body: query.to_json,
|
|
130
194
|
})
|
|
195
|
+
puts! results, 'place_order!() results'
|
|
196
|
+
puts! results.code, 'results.code'
|
|
197
|
+
# if 201 != results.code
|
|
198
|
+
# throw results
|
|
199
|
+
# end
|
|
131
200
|
order_id = results.headers['location'].split('/').last
|
|
132
|
-
|
|
201
|
+
# response = JSON.parse results.body
|
|
202
|
+
return { schwab_order_id: order_id, schwab_status: 'WORKING' }
|
|
133
203
|
end
|
|
134
204
|
|
|
135
205
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
class WcoEmail::Config
|
|
3
|
+
include Mongoid::Document
|
|
4
|
+
include Mongoid::Timestamps
|
|
5
|
+
store_in collection: 'wco_email_config'
|
|
6
|
+
|
|
7
|
+
field :key
|
|
8
|
+
validates :key, { presence: true, uniqueness: true }
|
|
9
|
+
|
|
10
|
+
field :value
|
|
11
|
+
validates :value, { presence: true }
|
|
12
|
+
|
|
13
|
+
field :descr
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
%hr
|
|
24
24
|
.row
|
|
25
25
|
.col-6
|
|
26
|
+
.field
|
|
27
|
+
%label schwab_account_hash
|
|
28
|
+
= f.text_field :schwab_account_hash
|
|
26
29
|
.field
|
|
27
30
|
%label schwab_access_token
|
|
28
31
|
= f.text_field :schwab_access_token
|
|
@@ -33,6 +36,8 @@
|
|
|
33
36
|
%label schwab_id_token
|
|
34
37
|
= f.text_field :schwab_id_token
|
|
35
38
|
.col-6
|
|
39
|
+
.field
|
|
40
|
+
%label
|
|
36
41
|
.field
|
|
37
42
|
%label schwab_exec_access_token
|
|
38
43
|
= f.text_field :schwab_exec_access_token
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wco_models
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1.0.
|
|
4
|
+
version: 3.1.0.266
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Victor Pudeyev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ahoy_matey
|
|
@@ -477,6 +477,7 @@ files:
|
|
|
477
477
|
- app/models/iro/datapoint.rb
|
|
478
478
|
- app/models/iro/date.rb
|
|
479
479
|
- app/models/iro/option.rb
|
|
480
|
+
- app/models/iro/option.rb-bk
|
|
480
481
|
- app/models/iro/option_black_scholes.rb-bk
|
|
481
482
|
- app/models/iro/position.rb
|
|
482
483
|
- app/models/iro/priceitem.rb
|
|
@@ -516,6 +517,7 @@ files:
|
|
|
516
517
|
- app/models/wco/utils.rb
|
|
517
518
|
- app/models/wco/video.rb
|
|
518
519
|
- app/models/wco_email/campaign.rb
|
|
520
|
+
- app/models/wco_email/config.rb
|
|
519
521
|
- app/models/wco_email/context.rb
|
|
520
522
|
- app/models/wco_email/conversation.rb
|
|
521
523
|
- app/models/wco_email/email_action.rb
|