wco_models 3.1.0.270 → 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 +67 -13
- 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/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
|
|
@@ -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)
|
|
@@ -228,7 +275,7 @@ class Iro::Position
|
|
|
228
275
|
|
|
229
276
|
count = 1
|
|
230
277
|
@positions.each do |pos|
|
|
231
|
-
# puts! pos.to_s, 'pos
|
|
278
|
+
# puts! pos.id.to_s, '#sync_all.pos'
|
|
232
279
|
|
|
233
280
|
quotes_h = Tda::Option.get_quotes_h({
|
|
234
281
|
contractType: 'ALL',
|
|
@@ -237,12 +284,17 @@ class Iro::Position
|
|
|
237
284
|
toDate: expiration_dates.last,
|
|
238
285
|
})
|
|
239
286
|
|
|
240
|
-
pos.inner.end_price = quotes_h[pos.expires_on.to_s][pos.put_call][pos.inner.strike][:price]
|
|
241
|
-
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]
|
|
242
289
|
pos.inner.save ? print("#{count}^") : print("#{count}X")
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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]
|
|
246
298
|
pos.outer.save ? print('^') : print('X')
|
|
247
299
|
end
|
|
248
300
|
count = count+1
|
|
@@ -433,7 +485,9 @@ class Iro::Position
|
|
|
433
485
|
|
|
434
486
|
def to_s
|
|
435
487
|
out = "#{stock} (#{q}) #{expires_on.to_datetime.strftime('%b %d')} #{strategy.long_or_short} ["
|
|
436
|
-
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
|
|
437
491
|
if outer&.strike
|
|
438
492
|
out = out + "$#{outer.strike} << "
|
|
439
493
|
end
|
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/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
|
data/app/models/wco/video.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
require 'mongoid_paperclip'
|
|
3
|
+
require 'streamio-ffmpeg'
|
|
3
4
|
|
|
4
5
|
class Wco::Video
|
|
5
6
|
include Mongoid::Document
|
|
@@ -33,9 +34,6 @@ class Wco::Video
|
|
|
33
34
|
|
|
34
35
|
field :duration_ms, type: :integer
|
|
35
36
|
|
|
36
|
-
# belongs_to :user_profile, :class_name => 'Ish::UserProfile', :inverse_of => :videos
|
|
37
|
-
# has_and_belongs_to_many :shared_profiles, :class_name => 'Ish::UserProfile', :inverse_of => :shared_videos
|
|
38
|
-
|
|
39
37
|
belongs_to :lead, optional: true
|
|
40
38
|
belongs_to :newspartial, optional: true
|
|
41
39
|
belongs_to :newsvideo, optional: true
|
|
@@ -77,10 +75,37 @@ class Wco::Video
|
|
|
77
75
|
%w| name descr |
|
|
78
76
|
end
|
|
79
77
|
|
|
78
|
+
before_create :set_duration_ms
|
|
79
|
+
def set_duration_ms
|
|
80
|
+
return unless video.queued_for_write[:original]
|
|
81
|
+
path = video.queued_for_write[:original].path
|
|
82
|
+
movie = ::FFMPEG::Movie.new(path)
|
|
83
|
+
self.duration_ms = (movie.duration * 1000).to_i if movie.duration
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
before_create :set_title
|
|
87
|
+
def set_title
|
|
88
|
+
return unless video.present?
|
|
89
|
+
filename = video_file_name # Paperclip metadata
|
|
90
|
+
return unless filename
|
|
91
|
+
self.name = File.basename(filename, ".*") if self.name.blank?
|
|
92
|
+
end
|
|
93
|
+
|
|
80
94
|
def self.list
|
|
81
95
|
[['', nil]] + self.unscoped.order_by( :created_at => :desc ).map do |item|
|
|
82
96
|
[ "#{item.created_at.strftime('%Y%m%d')} #{item.name}", item.id ]
|
|
83
97
|
end
|
|
84
98
|
end
|
|
85
99
|
|
|
100
|
+
def generate_thumbnail
|
|
101
|
+
return unless video.queued_for_write[:original]
|
|
102
|
+
|
|
103
|
+
input_path = video.queued_for_write[:original].path
|
|
104
|
+
output_path = Rails.root.join('tmp', "thumb_#{SecureRandom.hex}.jpg")
|
|
105
|
+
movie = ::FFMPEG::Movie.new(input_path)
|
|
106
|
+
movie.screenshot(output_path.to_s, seek_time: 1) ## at time 00:00:01 seconds
|
|
107
|
+
self.thumb = File.open(output_path)
|
|
108
|
+
File.delete(output_path) if File.exist?(output_path)
|
|
109
|
+
end
|
|
110
|
+
|
|
86
111
|
end
|
|
@@ -28,7 +28,7 @@ class WcoEmail::EmailFilter
|
|
|
28
28
|
|
|
29
29
|
has_many :actions, class_name: '::WcoEmail::EmailFilterAction', inverse_of: :email_filter
|
|
30
30
|
accepts_nested_attributes_for :actions, allow_destroy: true, reject_if: :all_blank
|
|
31
|
-
|
|
31
|
+
validate :validate_actions
|
|
32
32
|
def validate_actions
|
|
33
33
|
if actions.length == 0
|
|
34
34
|
errors.add(:actions, 'must be present')
|
|
@@ -22,6 +22,7 @@ class WcoEmail::EmailFilterAction
|
|
|
22
22
|
field :value
|
|
23
23
|
|
|
24
24
|
belongs_to :aject, polymorphic: true # , optional: true # eg tag, EAT, OAT
|
|
25
|
+
accepts_nested_attributes_for :aject, allow_destroy: true, reject_if: :all_blank
|
|
25
26
|
# validates :aject_id, presence: true
|
|
26
27
|
|
|
27
28
|
## 2026-04-02 not anymore.
|
|
@@ -46,3 +47,4 @@ class WcoEmail::EmailFilterAction
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
end
|
|
50
|
+
EFA = WcoEmail::EmailFilterAction
|