wco_models 3.1.0.269 → 3.1.0.271
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/README.txt +10 -12
- data/app/assets/stylesheets/wco/main.scss +24 -0
- data/app/assets/stylesheets/wco/newsvideos.scss +45 -0
- data/app/controllers/wco/api_controller.rb +2 -2
- data/app/controllers/wco/application_controller.rb +45 -0
- data/app/controllers/wco/leads_controller.rb +0 -2
- data/app/controllers/wco/newspartials_controller.rb +1 -1
- data/app/controllers/wco/newsvideos_controller.rb +3 -2
- data/app/controllers/wco/publishers_controller.rb +12 -4
- data/app/controllers/wco/reports_controller.rb +140 -0
- data/app/controllers/wco/tags_controller.rb +18 -5
- data/app/controllers/wco/videos_controller.rb +4 -0
- data/app/mailers/wco_email/application_mailer.rb +1 -1
- data/app/models/ability.rb +3 -0
- data/app/models/iro/option.rb +2 -3
- data/app/models/iro/position.rb +92 -27
- data/app/models/iro/purse.rb +1 -2
- data/app/models/iro/stock.rb +20 -0
- data/app/models/iro/strategy.rb +57 -40
- data/app/models/tda/option.rb +1 -1
- data/app/models/tda/order.rb +4 -4
- data/app/models/wco/lead.rb +6 -5
- data/app/models/wco/newspartial.rb +7 -2
- data/app/models/wco/newsvideo.rb +2 -0
- data/app/models/wco/photo.rb +2 -0
- data/app/models/wco/profile.rb +6 -0
- data/app/models/wco/publisher.rb +3 -3
- data/app/models/wco/report.rb +6 -3
- data/app/models/wco/site.rb +2 -2
- data/app/models/wco/tag.rb +3 -0
- data/app/models/wco/video.rb +28 -3
- data/app/models/wco_email/email_filter.rb +1 -1
- data/app/models/wco_email/email_filter_action.rb +2 -0
- data/app/models/wco_email/email_filter_condition.rb +29 -14
- data/app/models/wco_email/message_stub.rb +2 -1
- data/app/views/wco/_main_footer.haml +4 -3
- data/app/views/wco/_main_header.haml +1 -0
- data/app/views/wco/_sidebar.haml +21 -0
- data/app/views/wco/newsoverlays/_show_in_newsvideo.haml +7 -5
- data/app/views/wco/newspartials/_show_in_newsvideo.haml +35 -28
- data/app/views/wco/newspartials/edit.haml +4 -0
- data/app/views/wco/newsvideos/_form.haml +10 -3
- data/app/views/wco/newsvideos/show.haml +20 -3
- data/app/views/wco/profiles/_form.haml +33 -13
- data/app/views/wco/publishers/_post_with.haml +7 -0
- data/app/views/wco/reports/_form.haml +11 -2
- data/app/views/wco/reports/_to_facebook.html +23 -0
- data/app/views/wco/reports/show.haml +15 -0
- data/app/views/wco/tags/_index_table.haml +4 -1
- data/app/views/wco/tags/_index_tree.haml +2 -1
- data/app/views/wco/tags/new_for_sidebar.haml +9 -0
- data/app/views/wco/tags/show.haml +54 -42
- data/app/views/wco/tags/show.haml-bk +63 -0
- data/config/initializers/08_integrations.rb +0 -15
- data/config/routes.rb +11 -2
- data/lib/wco/facebook_poster.rb +17 -0
- metadata +23 -3
- data/app/views/wco/newspartials/index.haml-trash +0 -6
data/app/models/iro/position.rb
CHANGED
|
@@ -5,7 +5,9 @@ class Iro::Position
|
|
|
5
5
|
include Mongoid::Paranoia
|
|
6
6
|
store_in collection: 'iro_positions'
|
|
7
7
|
|
|
8
|
-
field :next_gain_loss_amount,
|
|
8
|
+
field :next_gain_loss_amount, type: :float
|
|
9
|
+
field :realized_gain_loss_amount, type: :float, default: 0.0 ## for diagonals only
|
|
10
|
+
def realized_gl; realized_gain_loss_amount; end
|
|
9
11
|
|
|
10
12
|
STATUS_ACTIVE = 'active'
|
|
11
13
|
STATUS_CLOSED = 'closed'
|
|
@@ -29,6 +31,12 @@ class Iro::Position
|
|
|
29
31
|
|
|
30
32
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
|
31
33
|
index({ purse_id: 1, ticker: 1 })
|
|
34
|
+
index({ deleted_at: 1,
|
|
35
|
+
status: 1,
|
|
36
|
+
expires_on: 1,
|
|
37
|
+
ticker: 1,
|
|
38
|
+
long_or_short: 1,
|
|
39
|
+
inner_strike: 1 })
|
|
32
40
|
|
|
33
41
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
|
|
34
42
|
field :ticker
|
|
@@ -41,8 +49,13 @@ class Iro::Position
|
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
field :long_or_short, type: String
|
|
53
|
+
before_save do
|
|
54
|
+
self.long_or_short = strategy.long_or_short
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# delegate :credit_or_debit, to: :strategy
|
|
58
|
+
field :credit_or_debit, type: String # , default: 'credit'
|
|
46
59
|
|
|
47
60
|
field :put_call, type: :string
|
|
48
61
|
validates :put_call, presence: true
|
|
@@ -67,9 +80,11 @@ class Iro::Position
|
|
|
67
80
|
|
|
68
81
|
belongs_to :inner, class_name: 'Iro::Option', inverse_of: :pos_of_inner
|
|
69
82
|
validates_associated :inner
|
|
83
|
+
has_many :inners, class_name: 'Iro::Option', inverse_of: :poss_of_inner ## for history and diagonals
|
|
70
84
|
|
|
71
85
|
belongs_to :outer, class_name: 'Iro::Option', inverse_of: :pos_of_outer, optional: true
|
|
72
86
|
validates_associated :outer
|
|
87
|
+
has_many :outers, class_name: 'Iro::Option', inverse_of: :poss_of_outer ## for history and diagonals
|
|
73
88
|
|
|
74
89
|
accepts_nested_attributes_for :inner, :outer
|
|
75
90
|
|
|
@@ -79,8 +94,12 @@ class Iro::Position
|
|
|
79
94
|
field :inner_strike, type: :float
|
|
80
95
|
validates :inner_strike, presence: true ## 2026-02-24 only to make finding easier.
|
|
81
96
|
|
|
82
|
-
field :expires_on
|
|
97
|
+
field :expires_on, type: :string
|
|
83
98
|
validates :expires_on, presence: true
|
|
99
|
+
before_save :trim_expires_on
|
|
100
|
+
def trim_expires_on
|
|
101
|
+
self.expires_on = expires_on.to_s[0, 10] if expires_on.present?
|
|
102
|
+
end
|
|
84
103
|
|
|
85
104
|
field :quantity, type: :integer
|
|
86
105
|
validates :quantity, presence: true
|
|
@@ -93,11 +112,18 @@ class Iro::Position
|
|
|
93
112
|
field :schwab_order_id, type: :integer
|
|
94
113
|
field :schwab_status
|
|
95
114
|
|
|
115
|
+
def diag_weeks
|
|
116
|
+
pos = self
|
|
117
|
+
((pos.outer.expires_on - pos.inner.expires_on)/7).to_i
|
|
118
|
+
end
|
|
119
|
+
|
|
96
120
|
def begin_delta
|
|
97
|
-
strategy.send("begin_delta_#{strategy.kind}", self)
|
|
121
|
+
# strategy.send("begin_delta_#{strategy.kind}", self)
|
|
122
|
+
strategy.begin_delta self
|
|
98
123
|
end
|
|
99
124
|
def end_delta
|
|
100
|
-
strategy.send("end_delta_#{strategy.kind}", self)
|
|
125
|
+
# strategy.send("end_delta_#{strategy.kind}", self)
|
|
126
|
+
strategy.end_delta self
|
|
101
127
|
end
|
|
102
128
|
|
|
103
129
|
def breakeven
|
|
@@ -116,11 +142,23 @@ class Iro::Position
|
|
|
116
142
|
p = self
|
|
117
143
|
p.inner.strike + p.max_gain
|
|
118
144
|
end
|
|
145
|
+
def breakeven_short_debit_put_spread
|
|
146
|
+
p = self
|
|
147
|
+
p.inner.strike - p.inner.begin_price + p.outer.begin_price
|
|
148
|
+
end
|
|
119
149
|
## 2026-02-23
|
|
120
150
|
def breakeven_long_credit_put_spread
|
|
121
151
|
p = self
|
|
122
152
|
p.inner.strike - p.max_gain
|
|
123
153
|
end
|
|
154
|
+
def breakeven_diag_long_call_spread
|
|
155
|
+
p = self
|
|
156
|
+
realized_gl + p.outer.strike + p.outer.begin_price - p.inner.begin_price
|
|
157
|
+
end
|
|
158
|
+
def breakeven_diag_short_put_spread
|
|
159
|
+
p = self
|
|
160
|
+
p.inner.strike + p.max_gain + p.realized_gl
|
|
161
|
+
end
|
|
124
162
|
|
|
125
163
|
|
|
126
164
|
def current_underlying_strike
|
|
@@ -142,7 +180,7 @@ class Iro::Position
|
|
|
142
180
|
end
|
|
143
181
|
|
|
144
182
|
|
|
145
|
-
field :pending_price
|
|
183
|
+
field :pending_price, type: :float
|
|
146
184
|
|
|
147
185
|
## credit spread only
|
|
148
186
|
def close_price
|
|
@@ -151,8 +189,8 @@ class Iro::Position
|
|
|
151
189
|
return out.round(2)
|
|
152
190
|
end
|
|
153
191
|
|
|
154
|
-
##
|
|
155
|
-
def
|
|
192
|
+
## credit-spread
|
|
193
|
+
def open_price
|
|
156
194
|
pos = self
|
|
157
195
|
out = pos.inner.begin_price - pos.outer.begin_price
|
|
158
196
|
return out.round(2)
|
|
@@ -182,6 +220,15 @@ class Iro::Position
|
|
|
182
220
|
def net_amount_short_credit_call_spread
|
|
183
221
|
return net_amount_long_credit_put_spread
|
|
184
222
|
end
|
|
223
|
+
def net_amount_short_debit_put_spread
|
|
224
|
+
inner.end_price - inner.begin_price + outer.begin_price - outer.end_price
|
|
225
|
+
end
|
|
226
|
+
def net_amount_diag_long_call_spread
|
|
227
|
+
inner.begin_price - outer.begin_price + outer.end_price - inner.end_price + realized_gl
|
|
228
|
+
end
|
|
229
|
+
def net_amount_diag_short_put_spread
|
|
230
|
+
net_amount_diag_long_call_spread
|
|
231
|
+
end
|
|
185
232
|
|
|
186
233
|
def max_gain # each
|
|
187
234
|
strategy.send("max_gain_#{strategy.kind}", self)
|
|
@@ -208,6 +255,19 @@ class Iro::Position
|
|
|
208
255
|
outer.sync
|
|
209
256
|
end
|
|
210
257
|
|
|
258
|
+
def schwab_query
|
|
259
|
+
pos = self
|
|
260
|
+
case pos.intent
|
|
261
|
+
when Iro::Strategy::INTENT_OPEN
|
|
262
|
+
the_q = Tda::Order.credit_spread_q pos
|
|
263
|
+
when Iro::Strategy::INTENT_ROLL
|
|
264
|
+
the_q = Tda::Order.roll_credit_call_spread_q pos
|
|
265
|
+
else
|
|
266
|
+
throw "prp - #schwab_query undefined for position #{pos.inspect}"
|
|
267
|
+
end
|
|
268
|
+
return the_q
|
|
269
|
+
end
|
|
270
|
+
|
|
211
271
|
def self.sync_all
|
|
212
272
|
@positions = Iro::Position.where( :status.in => [ 'active', 'pending' ] )
|
|
213
273
|
expiration_dates = @positions.map { |p| p.expires_on.to_s }.sort
|
|
@@ -215,7 +275,7 @@ class Iro::Position
|
|
|
215
275
|
|
|
216
276
|
count = 1
|
|
217
277
|
@positions.each do |pos|
|
|
218
|
-
# puts! pos.to_s, 'pos
|
|
278
|
+
# puts! pos.id.to_s, '#sync_all.pos'
|
|
219
279
|
|
|
220
280
|
quotes_h = Tda::Option.get_quotes_h({
|
|
221
281
|
contractType: 'ALL',
|
|
@@ -224,12 +284,17 @@ class Iro::Position
|
|
|
224
284
|
toDate: expiration_dates.last,
|
|
225
285
|
})
|
|
226
286
|
|
|
227
|
-
pos.inner.end_price = quotes_h[pos.expires_on.to_s][pos.put_call][pos.inner.strike][:price]
|
|
228
|
-
pos.inner.end_delta = quotes_h[pos.expires_on.to_s][pos.put_call][pos.inner.strike][:delta]
|
|
287
|
+
pos.inner.end_price = quotes_h[pos.expires_on.to_date.to_s][pos.put_call][pos.inner.strike][:price]
|
|
288
|
+
pos.inner.end_delta = quotes_h[pos.expires_on.to_date.to_s][pos.put_call][pos.inner.strike][:delta]
|
|
229
289
|
pos.inner.save ? print("#{count}^") : print("#{count}X")
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
290
|
+
|
|
291
|
+
# if [ Iro::Strategy::KIND_LONG_CREDIT_PUT_SPREAD,
|
|
292
|
+
# Iro::Strategy::KIND_SHORT_CREDIT_CALL_SPREAD,
|
|
293
|
+
# Iro::Strategy::KIND_DIAG_LONG_CALL_SPREAD,
|
|
294
|
+
# Iro::Strategy::KIND_DIAG_SHORT_PUT_SPREAD ].include?( pos.strategy.kind )
|
|
295
|
+
if pos.outer
|
|
296
|
+
pos.outer.end_price = quotes_h[pos.expires_on.to_date.to_s][pos.put_call][pos.outer.strike][:price]
|
|
297
|
+
pos.outer.end_delta = quotes_h[pos.expires_on.to_date.to_s][pos.put_call][pos.outer.strike][:delta]
|
|
233
298
|
pos.outer.save ? print('^') : print('X')
|
|
234
299
|
end
|
|
235
300
|
count = count+1
|
|
@@ -274,6 +339,11 @@ class Iro::Position
|
|
|
274
339
|
# puts! outs, 'outs'
|
|
275
340
|
outs_bk = outs.dup
|
|
276
341
|
|
|
342
|
+
## cleanup mid-increments
|
|
343
|
+
outs = outs.select do |out|
|
|
344
|
+
( out[:strikePrice] - pos.inner.strike ) % strategy.stock.options_price_increment == 0
|
|
345
|
+
end
|
|
346
|
+
|
|
277
347
|
outs = outs.select do |out|
|
|
278
348
|
out[:bidSize] + out[:askSize] > 0
|
|
279
349
|
end
|
|
@@ -319,15 +389,8 @@ class Iro::Position
|
|
|
319
389
|
|
|
320
390
|
## next_inner_delta
|
|
321
391
|
outs = outs.select do |out|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
out_delta <= strategy.next_inner_delta
|
|
325
|
-
elsif 'PUT' == pos.put_call
|
|
326
|
-
out_delta = out[:delta] rescue 0
|
|
327
|
-
out_delta <= strategy.next_inner_delta
|
|
328
|
-
else
|
|
329
|
-
raise 'zt5 - this cannot happen'
|
|
330
|
-
end
|
|
392
|
+
out_delta = out[:delta].abs rescue 0
|
|
393
|
+
out_delta <= strategy.next_inner_delta
|
|
331
394
|
end
|
|
332
395
|
# puts! outs[0][:strikePrice], 'after calc next_inner_delta'
|
|
333
396
|
# puts! outs, 'outs'
|
|
@@ -422,15 +485,17 @@ class Iro::Position
|
|
|
422
485
|
|
|
423
486
|
def to_s
|
|
424
487
|
out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.long_or_short} ["
|
|
425
|
-
if Iro::Strategy::
|
|
488
|
+
if Iro::Strategy::KIND_SHORT_DEBIT_PUT_SPREAD == strategy.kind
|
|
489
|
+
out = out + "$#{outer.strike} << $#{inner.strike}"
|
|
490
|
+
elsif Iro::Strategy::LONG == long_or_short
|
|
426
491
|
if outer&.strike
|
|
427
|
-
out = out + "$#{outer.strike}
|
|
492
|
+
out = out + "$#{outer.strike} << "
|
|
428
493
|
end
|
|
429
494
|
out = out + "$#{inner.strike}"
|
|
430
495
|
else
|
|
431
496
|
out = out + "$#{inner.strike}"
|
|
432
497
|
if outer&.strike
|
|
433
|
-
out = out + "
|
|
498
|
+
out = out + " >> $#{outer.strike}"
|
|
434
499
|
end
|
|
435
500
|
end
|
|
436
501
|
out += "] "
|
data/app/models/iro/purse.rb
CHANGED
|
@@ -24,8 +24,7 @@ class Iro::Purse
|
|
|
24
24
|
## with unit 100, .0001
|
|
25
25
|
field :summary_unit, type: :float, default: 0.001
|
|
26
26
|
|
|
27
|
-
##
|
|
28
|
-
field :height, type: :integer, default: 100
|
|
27
|
+
field :height, type: :integer, default: 100 ## px/q, units modal
|
|
29
28
|
|
|
30
29
|
field :mark_every_n_usd, type: :float, default: 1
|
|
31
30
|
field :n_next_positions, type: :integer, default: 5
|
data/app/models/iro/stock.rb
CHANGED
|
@@ -32,6 +32,26 @@ class ::Iro::Stock
|
|
|
32
32
|
|
|
33
33
|
field :descr, type: :string
|
|
34
34
|
|
|
35
|
+
## for charting:
|
|
36
|
+
field :min, type: :integer
|
|
37
|
+
field :max, type: :integer
|
|
38
|
+
field :step, type: :integer, default: 1.0 ## for histograms. I liked 50 bars, that's $1-3 bucket size.
|
|
39
|
+
|
|
40
|
+
validate :step_must_fit_range
|
|
41
|
+
def step_must_fit_range
|
|
42
|
+
return if min.blank? || max.blank? || step.blank?
|
|
43
|
+
|
|
44
|
+
range = max. - min.to_i
|
|
45
|
+
step_val = step.to_i
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if !((max - min) % step).zero?
|
|
49
|
+
errors.add(:step, "must evenly divide (max-min)/step")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
35
55
|
has_many :positions, class_name: '::Iro::Position', inverse_of: :stock
|
|
36
56
|
has_many :strategies, class_name: '::Iro::Strategy', inverse_of: :stock
|
|
37
57
|
# has_many :purses, class_name: '::Iro::Purse', inverse_of: :stock
|
data/app/models/iro/strategy.rb
CHANGED
|
@@ -24,6 +24,8 @@ class Iro::Strategy
|
|
|
24
24
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :strategies
|
|
25
25
|
|
|
26
26
|
KIND_COVERED_CALL = 'covered_call'
|
|
27
|
+
KIND_DIAG_LONG_CALL_SPREAD = 'diag_long_call_spread'
|
|
28
|
+
KIND_DIAG_SHORT_PUT_SPREAD = 'diag_short_put_spread'
|
|
27
29
|
KIND_IRON_CONDOR = 'iron_condor'
|
|
28
30
|
KIND_LONG_CREDIT_PUT_SPREAD = 'long_credit_put_spread'
|
|
29
31
|
KIND_LONG_DEBIT_CALL_SPREAD = 'long_debit_call_spread'
|
|
@@ -36,6 +38,8 @@ class Iro::Strategy
|
|
|
36
38
|
KIND_WHEEL = 'wheel' ## @deprecated, use covered_call
|
|
37
39
|
KINDS = [ nil,
|
|
38
40
|
KIND_COVERED_CALL,
|
|
41
|
+
KIND_DIAG_LONG_CALL_SPREAD,
|
|
42
|
+
KIND_DIAG_SHORT_PUT_SPREAD,
|
|
39
43
|
KIND_IRON_CONDOR,
|
|
40
44
|
KIND_LONG_CREDIT_PUT_SPREAD,
|
|
41
45
|
KIND_LONG_DEBIT_CALL_SPREAD,
|
|
@@ -49,6 +53,12 @@ class Iro::Strategy
|
|
|
49
53
|
|
|
50
54
|
def put_call
|
|
51
55
|
case kind
|
|
56
|
+
when Iro::Strategy::KIND_COVERED_CALL
|
|
57
|
+
put_call = 'CALL'
|
|
58
|
+
when Iro::Strategy::KIND_DIAG_LONG_CALL_SPREAD
|
|
59
|
+
put_call = 'CALL'
|
|
60
|
+
when Iro::Strategy::KIND_DIAG_SHORT_PUT_SPREAD
|
|
61
|
+
put_call = 'PUT'
|
|
52
62
|
when Iro::Strategy::KIND_LONG_CREDIT_PUT_SPREAD
|
|
53
63
|
put_call = 'PUT'
|
|
54
64
|
when Iro::Strategy::KIND_LONG_DEBIT_CALL_SPREAD
|
|
@@ -57,8 +67,7 @@ class Iro::Strategy
|
|
|
57
67
|
put_call = 'CALL'
|
|
58
68
|
when Iro::Strategy::KIND_SHORT_DEBIT_PUT_SPREAD
|
|
59
69
|
put_call = 'PUT'
|
|
60
|
-
|
|
61
|
-
put_call = 'CALL'
|
|
70
|
+
|
|
62
71
|
when Iro::Strategy::KIND_SPREAD
|
|
63
72
|
if credit_or_debit == CREDIT
|
|
64
73
|
if long_or_short == LONG
|
|
@@ -77,11 +86,11 @@ class Iro::Strategy
|
|
|
77
86
|
end
|
|
78
87
|
|
|
79
88
|
|
|
80
|
-
field :threshold_usd_above_mark, type: :float
|
|
89
|
+
field :threshold_usd_above_mark, type: :float, default: 1.00
|
|
81
90
|
validates :threshold_usd_above_mark, presence: true
|
|
82
91
|
|
|
83
92
|
field :threshold_pos_delta, type: :float # offensive: roll b/c markets are going my way
|
|
84
|
-
field :threshold_neg_delta, type: :float # defensive: roll b/c markets are going against me
|
|
93
|
+
field :threshold_neg_delta, type: :float, default: 0.8 # defensive: roll b/c markets are going against me
|
|
85
94
|
field :threshold_netp, type: :float
|
|
86
95
|
field :threshold_dte, type: :integer, default: 1
|
|
87
96
|
|
|
@@ -91,7 +100,7 @@ class Iro::Strategy
|
|
|
91
100
|
field :next_outer_strike, type: :float
|
|
92
101
|
field :next_spread_amount, type: :float # e.g. $20 for a $2000 NVDA spread
|
|
93
102
|
|
|
94
|
-
field :next_usd_above_mark, type: :float
|
|
103
|
+
field :next_usd_above_mark, type: :float, default: 1.00
|
|
95
104
|
validates :next_usd_above_mark, presence: true
|
|
96
105
|
|
|
97
106
|
|
|
@@ -108,12 +117,12 @@ class Iro::Strategy
|
|
|
108
117
|
field :tgt_exposure, type: :float
|
|
109
118
|
|
|
110
119
|
|
|
120
|
+
def begin_delta p
|
|
121
|
+
_begin_delta_spread p
|
|
122
|
+
end
|
|
111
123
|
def begin_delta_covered_call p
|
|
112
124
|
p.inner.begin_delta
|
|
113
125
|
end
|
|
114
|
-
# def begin_delta_wheel p
|
|
115
|
-
# p.inner.begin_delta
|
|
116
|
-
# end
|
|
117
126
|
def _begin_delta_spread p
|
|
118
127
|
p.inner.begin_delta - p.outer.begin_delta
|
|
119
128
|
end
|
|
@@ -123,13 +132,19 @@ class Iro::Strategy
|
|
|
123
132
|
def begin_delta_short_credit_call_spread p
|
|
124
133
|
_begin_delta_spread p
|
|
125
134
|
end
|
|
135
|
+
def begin_delta_diag_long_call_spread p
|
|
136
|
+
_begin_delta_spread p
|
|
137
|
+
end
|
|
138
|
+
def begin_delta_diag_short_put_spread p
|
|
139
|
+
_begin_delta_spread p
|
|
140
|
+
end
|
|
126
141
|
|
|
142
|
+
def end_delta p
|
|
143
|
+
_end_delta_spread p
|
|
144
|
+
end
|
|
127
145
|
def end_delta_covered_call p
|
|
128
146
|
p.inner.end_delta
|
|
129
147
|
end
|
|
130
|
-
# def end_delta_wheel p
|
|
131
|
-
# p.inner.end_delta
|
|
132
|
-
# end
|
|
133
148
|
def _end_delta_spread p
|
|
134
149
|
p.inner.end_delta - p.outer.end_delta
|
|
135
150
|
end
|
|
@@ -139,7 +154,21 @@ class Iro::Strategy
|
|
|
139
154
|
def end_delta_short_credit_call_spread p
|
|
140
155
|
_end_delta_spread p
|
|
141
156
|
end
|
|
157
|
+
def end_delta_diag_long_call_spread p
|
|
158
|
+
_end_delta_spread p
|
|
159
|
+
end
|
|
160
|
+
def end_delta_diag_short_put_spread p
|
|
161
|
+
_end_delta_spread p
|
|
162
|
+
end
|
|
163
|
+
|
|
142
164
|
|
|
165
|
+
def max_gain_diag_long_call_spread p ## each
|
|
166
|
+
# p.outer.strike - p.inner.strike + p.outer.begin_price - p.inner.begin_price
|
|
167
|
+
p.inner.strike - p.outer.strike - p.outer.begin_price + p.inner.begin_price
|
|
168
|
+
end
|
|
169
|
+
def max_gain_diag_short_put_spread p ## each
|
|
170
|
+
p.outer.strike - p.inner.strike - p.outer.begin_price + p.inner.begin_price
|
|
171
|
+
end
|
|
143
172
|
|
|
144
173
|
def max_gain_covered_call p ## each
|
|
145
174
|
p.inner.begin_price # - 0.66
|
|
@@ -168,21 +197,31 @@ class Iro::Strategy
|
|
|
168
197
|
end
|
|
169
198
|
|
|
170
199
|
|
|
200
|
+
def max_loss_diag_long_call_spread p
|
|
201
|
+
# p.inner.strike - p.outer.strike + p.inner.begin_price + p.outer.begin_price
|
|
202
|
+
p.outer.begin_price - p.inner.begin_price - p.realized_gl
|
|
203
|
+
end
|
|
204
|
+
def max_loss_diag_short_put_spread p
|
|
205
|
+
p.outer.begin_price - p.inner.begin_price - p.realized_gl ## same as above, max_loss_diag_long_call_spread
|
|
206
|
+
end
|
|
171
207
|
def max_loss_covered_call p
|
|
172
208
|
p.inner.begin_price*10 # just suppose 10,000%
|
|
173
209
|
end
|
|
174
210
|
def max_loss_long_credit_put_spread p
|
|
175
211
|
out = p.inner.strike - p.outer.strike
|
|
176
212
|
end
|
|
177
|
-
def max_loss_long_debit_call_spread p
|
|
178
|
-
|
|
179
|
-
end
|
|
180
|
-
def max_loss_short_debit_put_spread p # different
|
|
181
|
-
|
|
182
|
-
end
|
|
213
|
+
# def max_loss_long_debit_call_spread p
|
|
214
|
+
# out = p.outer.strike - p.inner.strike
|
|
215
|
+
# end
|
|
216
|
+
# def max_loss_short_debit_put_spread p # different
|
|
217
|
+
# out = p.inner.strike - p.outer.strike
|
|
218
|
+
# end
|
|
183
219
|
def max_loss_short_credit_call_spread p
|
|
184
220
|
out = p.outer.strike - p.inner.strike
|
|
185
221
|
end
|
|
222
|
+
def max_loss_short_debit_put_spread p
|
|
223
|
+
p.inner.begin_price - p.outer.begin_price
|
|
224
|
+
end
|
|
186
225
|
def max_loss_spread p
|
|
187
226
|
( p.outer.strike - p.inner.strike ).abs
|
|
188
227
|
end
|
|
@@ -193,26 +232,9 @@ class Iro::Strategy
|
|
|
193
232
|
|
|
194
233
|
|
|
195
234
|
def net_amount_spread p
|
|
196
|
-
p.inner.begin_price - p.inner.end_price
|
|
235
|
+
p.inner.begin_price - p.inner.end_price - p.outer.begin_price + p.outer.end_price
|
|
197
236
|
end
|
|
198
|
-
# def net_amount_long_credit_put_spread p
|
|
199
|
-
# p.inner.begin_price - p.inner.end_price
|
|
200
|
-
# end
|
|
201
|
-
|
|
202
237
|
|
|
203
|
-
## 2024-05-09 _TODO
|
|
204
|
-
## 2025-10-11 _TODO
|
|
205
|
-
## 2026-02-23 trash, makes no sense.
|
|
206
|
-
=begin
|
|
207
|
-
def next_inner_strike_on expires_on
|
|
208
|
-
outs = ::Tda::Option.get_quotes({
|
|
209
|
-
contractType: put_call,
|
|
210
|
-
expirationDate: expires_on,
|
|
211
|
-
ticker: stock.ticker,
|
|
212
|
-
})
|
|
213
|
-
puts! outs, 'next_inner_strike_on -> outs'
|
|
214
|
-
end
|
|
215
|
-
=end
|
|
216
238
|
|
|
217
239
|
|
|
218
240
|
##
|
|
@@ -277,11 +299,6 @@ class Iro::Strategy
|
|
|
277
299
|
def calc_rollp_long_credit_put_spread p
|
|
278
300
|
stock.reload
|
|
279
301
|
|
|
280
|
-
# puts! p, '#calc_rollp_long_credit_put_spread'
|
|
281
|
-
# puts! p.inner, 'p.inner'
|
|
282
|
-
# puts! stock, 'stock'
|
|
283
|
-
# puts! attributes, 'strategy attributes'
|
|
284
|
-
|
|
285
302
|
if ( p.expires_on.to_date - Time.now.to_date ).to_i < 1
|
|
286
303
|
return [ 0.99, '0 DTE, must exit' ]
|
|
287
304
|
end
|
data/app/models/tda/option.rb
CHANGED
|
@@ -178,7 +178,7 @@ class Tda::Option
|
|
|
178
178
|
|
|
179
179
|
## 2026-02-23 use this instead.
|
|
180
180
|
def self.get_quotes_h params
|
|
181
|
-
# puts! params, 'Tda::Option#get_quotes_h ...'
|
|
181
|
+
# puts! params, 'Tda::Option#get_quotes_h params ...'
|
|
182
182
|
|
|
183
183
|
profile = Wco::Profile.find_by email: 'piousbox@gmail.com'
|
|
184
184
|
opts = {}
|
data/app/models/tda/order.rb
CHANGED
|
@@ -98,10 +98,10 @@ class Tda::Order
|
|
|
98
98
|
return query
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
## open credit spread
|
|
101
|
+
## open credit spread
|
|
102
102
|
def self.credit_spread_q pos
|
|
103
103
|
query = {
|
|
104
|
-
orderType: pos.
|
|
104
|
+
orderType: pos.pending_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
105
105
|
session: "NORMAL",
|
|
106
106
|
duration: "DAY",
|
|
107
107
|
price: pos.pending_price,
|
|
@@ -190,10 +190,10 @@ class Tda::Order
|
|
|
190
190
|
|
|
191
191
|
def self.roll_credit_call_spread_q pos
|
|
192
192
|
query = {
|
|
193
|
-
orderType: pos.
|
|
193
|
+
orderType: pos.pending_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
194
194
|
session: "NORMAL",
|
|
195
195
|
duration: "DAY",
|
|
196
|
-
price: pos.
|
|
196
|
+
price: pos.pending_price.abs.to_s,
|
|
197
197
|
orderStrategyType: "SINGLE",
|
|
198
198
|
orderLegCollection: [
|
|
199
199
|
## close
|
data/app/models/wco/lead.rb
CHANGED
|
@@ -28,11 +28,6 @@ class Wco::Lead
|
|
|
28
28
|
field :memory, type: Hash, default: {}
|
|
29
29
|
|
|
30
30
|
belongs_to :leadset, class_name: 'Wco::Leadset'
|
|
31
|
-
before_validation :set_leadset, on: :create
|
|
32
|
-
def set_leadset
|
|
33
|
-
domain = email.split('@')[1]
|
|
34
|
-
self.leadset ||= Wco::Leadset.find_or_create_by({ company_url: domain })
|
|
35
|
-
end
|
|
36
31
|
before_validation :normalize_email, on: :create
|
|
37
32
|
def normalize_email
|
|
38
33
|
self[:email] = email.downcase
|
|
@@ -49,6 +44,12 @@ class Wco::Lead
|
|
|
49
44
|
end
|
|
50
45
|
return a
|
|
51
46
|
end
|
|
47
|
+
before_validation :set_leadset, on: :create
|
|
48
|
+
def set_leadset
|
|
49
|
+
domain = email.split('@')[1]
|
|
50
|
+
self.leadset ||= Wco::Leadset.find_or_create_by({ company_url: domain })
|
|
51
|
+
end
|
|
52
|
+
|
|
52
53
|
def self.find_or_create_by_email email
|
|
53
54
|
email = self.normalize_email email
|
|
54
55
|
out = where( email: email ).first
|
|
@@ -92,12 +92,17 @@ class Wco::Newspartial
|
|
|
92
92
|
return out
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
##
|
|
96
|
+
## Can I make do without puppet driver? Probably not: I need the audio worklet.
|
|
97
|
+
##
|
|
95
98
|
def generate_video
|
|
96
99
|
cmd = "cd #{ISHLIB3JS_ROOT} ;
|
|
97
100
|
node ./src/talking_head/example_puppeteer_wired.js \
|
|
98
|
-
--api_key=#{
|
|
99
|
-
--api_secret=#{
|
|
101
|
+
--api_key=#{WCO_SIMPLE_API_KEY} \
|
|
102
|
+
--api_secret=#{WCO_SIMPLE_API_SECRET} \
|
|
100
103
|
--wco_origin=#{WCO_ORIGIN} \
|
|
104
|
+
--w_px=#{newsvideo.w_px} \
|
|
105
|
+
--h_px=#{newsvideo.h_px} \
|
|
101
106
|
--newspartial_id=#{self[:id]} ";
|
|
102
107
|
|
|
103
108
|
puts "+++ cmd:"
|
data/app/models/wco/newsvideo.rb
CHANGED
data/app/models/wco/photo.rb
CHANGED
|
@@ -15,6 +15,8 @@ class Wco::Photo
|
|
|
15
15
|
belongs_to :gallery, class_name: 'Wco::Gallery', optional: true
|
|
16
16
|
belongs_to :lead, class_name: 'Wco::Lead', optional: true
|
|
17
17
|
|
|
18
|
+
belongs_to :report, class_name: 'Wco::Report', optional: true
|
|
19
|
+
|
|
18
20
|
has_many :email_templates, class_name: 'WcoEmail::EmailTemplate'
|
|
19
21
|
# belongs_to :newsitem, :optional => true
|
|
20
22
|
|
data/app/models/wco/profile.rb
CHANGED
|
@@ -10,6 +10,7 @@ class Wco::Profile
|
|
|
10
10
|
validates :email, presence: true, uniqueness: true
|
|
11
11
|
|
|
12
12
|
field :name
|
|
13
|
+
field :descr
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
field :per_page, type: :integer, default: 25
|
|
@@ -31,10 +32,15 @@ class Wco::Profile
|
|
|
31
32
|
field :smtp_password
|
|
32
33
|
field :smtp_port
|
|
33
34
|
|
|
35
|
+
field :linkedin_client_id
|
|
36
|
+
field :linkedin_client_secret
|
|
37
|
+
field :linkedin_access_token
|
|
38
|
+
|
|
34
39
|
|
|
35
40
|
has_many :newsvideos, class_name: 'Wco::Newsvideo'
|
|
36
41
|
has_many :reports, class_name: 'Wco::Report'
|
|
37
42
|
has_many :stocks, class_name: 'Iro::Stock'
|
|
43
|
+
has_many :sidebar_tags, class_name: 'Wco::Tag', inverse_of: :sidebar_profile
|
|
38
44
|
|
|
39
45
|
belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :profile
|
|
40
46
|
has_many :newsitems, class_name: 'Wco::Newsitem'
|
data/app/models/wco/publisher.rb
CHANGED
|
@@ -28,13 +28,13 @@ class Wco::Publisher
|
|
|
28
28
|
@headers = {}
|
|
29
29
|
@ctx = OpenStruct.new
|
|
30
30
|
|
|
31
|
-
puts! context_eval, 'context_eval'
|
|
31
|
+
# puts! context_eval, 'context_eval'
|
|
32
32
|
eval( context_eval )
|
|
33
|
-
puts! @ctx, '@ctx'
|
|
33
|
+
# puts! @ctx, '@ctx'
|
|
34
34
|
|
|
35
35
|
tmpl = ERB.new post_body_tmpl
|
|
36
36
|
body = JSON.parse tmpl.result(binding)
|
|
37
|
-
puts! body, 'body'
|
|
37
|
+
# puts! body, 'body'
|
|
38
38
|
|
|
39
39
|
out = Wco::HTTParty.post( "#{@site.origin}#{post_path}", {
|
|
40
40
|
body: body.to_json,
|
data/app/models/wco/report.rb
CHANGED
|
@@ -13,7 +13,7 @@ class Wco::Report
|
|
|
13
13
|
|
|
14
14
|
field :title
|
|
15
15
|
validates :title, presence: true # , uniqueness: true
|
|
16
|
-
index({ title: 1 }
|
|
16
|
+
index({ title: 1 })
|
|
17
17
|
def name ; title ; end
|
|
18
18
|
|
|
19
19
|
field :subtitle
|
|
@@ -25,6 +25,9 @@ class Wco::Report
|
|
|
25
25
|
before_validation :set_slug, on: :create
|
|
26
26
|
|
|
27
27
|
field :body
|
|
28
|
+
def body_json
|
|
29
|
+
body.gsub(/\r/, '').gsub(/\n\n+/, '<br /><br />').to_json
|
|
30
|
+
end
|
|
28
31
|
|
|
29
32
|
field :x, :type => Float
|
|
30
33
|
field :y, :type => Float
|
|
@@ -32,8 +35,8 @@ class Wco::Report
|
|
|
32
35
|
|
|
33
36
|
belongs_to :author, class_name: 'Wco::Profile'
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
has_one :image_thumb, class_name: 'Wco::Photo', inverse_of: :report, dependent: :destroy
|
|
39
|
+
accepts_nested_attributes_for :image_thumb
|
|
37
40
|
|
|
38
41
|
has_and_belongs_to_many :tags
|
|
39
42
|
|
data/app/models/wco/site.rb
CHANGED
|
@@ -25,7 +25,7 @@ class Wco::Site
|
|
|
25
25
|
validates :slug, presence: true, uniqueness: true
|
|
26
26
|
|
|
27
27
|
field :origin # http://pi.local
|
|
28
|
-
validates :origin, presence: true, uniqueness: true
|
|
28
|
+
# validates :origin, presence: true, uniqueness: true
|
|
29
29
|
|
|
30
30
|
field :post_path # /node?_format=hal_json
|
|
31
31
|
field :username
|
|
@@ -39,7 +39,7 @@ class Wco::Site
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def self.list
|
|
42
|
-
[[nil,nil]] + all.map { |s| [ s.
|
|
42
|
+
[[nil,nil]] + all.map { |s| [ s.slug, s.id ] }
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def body
|
data/app/models/wco/tag.rb
CHANGED
|
@@ -9,10 +9,13 @@ class Wco::Tag
|
|
|
9
9
|
validates :slug, presence: true, uniqueness: true
|
|
10
10
|
index({ slug: -1 })
|
|
11
11
|
|
|
12
|
+
field :weight, type: :string, default: 'jjj'
|
|
13
|
+
|
|
12
14
|
belongs_to :parent, class_name: '::Wco::Tag', inverse_of: :sons, optional: true
|
|
13
15
|
has_many :sons, class_name: '::Wco::Tag', inverse_of: :parent
|
|
14
16
|
|
|
15
17
|
belongs_to :site, class_name: '::Wco::Site', optional: true
|
|
18
|
+
belongs_to :sidebar_profile, class_name: 'Wco::Profile', optional: true
|
|
16
19
|
has_many :email_filters, class_name: '::WcoEmail::EmailFilter', inverse_of: :tag
|
|
17
20
|
has_many :email_templates, class_name: '::WcoEmail::EmailTemplate', inverse_of: :tag
|
|
18
21
|
has_many :ajects, class_name: '::WcoEmail::EmailFilterAction', inverse_of: :aject
|