wco_models 3.1.0.265 → 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/models/iro/option.rb +3 -98
- data/app/models/iro/option.rb-bk +62 -0
- data/app/models/iro/position.rb +22 -3
- data/app/models/iro/purse.rb +1 -4
- data/app/models/iro/strategy.rb +0 -3
- data/app/models/tda/order.rb +73 -4
- metadata +3 -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
|
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
|
@@ -17,14 +17,22 @@ class Iro::Position
|
|
|
17
17
|
STATUSES = [ nil, STATUS_CLOSED, STATUS_ACTIVE, STATUS_PREPARE, STATUS_PROPOSED, STATUS_PENDING ]
|
|
18
18
|
field :status
|
|
19
19
|
validates :status, presence: true
|
|
20
|
-
scope :active,
|
|
21
|
-
|
|
20
|
+
scope :active, ->{ where( status: 'active' ) }
|
|
21
|
+
scope :proposed, ->{ where( status: 'proposed' ) }
|
|
22
|
+
|
|
22
23
|
|
|
23
24
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
|
24
25
|
index({ purse_id: 1, ticker: 1 })
|
|
25
26
|
|
|
26
27
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
|
|
27
|
-
|
|
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
|
|
28
36
|
|
|
29
37
|
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
|
30
38
|
delegate :long_or_short, to: :strategy
|
|
@@ -77,6 +85,7 @@ class Iro::Position
|
|
|
77
85
|
field :end_on
|
|
78
86
|
|
|
79
87
|
field :schwab_order_id, type: :integer
|
|
88
|
+
field :schwab_status
|
|
80
89
|
|
|
81
90
|
def begin_delta
|
|
82
91
|
strategy.send("begin_delta_#{strategy.kind}", self)
|
|
@@ -126,6 +135,16 @@ class Iro::Position
|
|
|
126
135
|
print '^'
|
|
127
136
|
end
|
|
128
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
|
+
|
|
129
148
|
def roll_price
|
|
130
149
|
pos = self
|
|
131
150
|
out = pos.autoprev.outer.end_price - pos.autoprev.inner.end_price + pos.inner.begin_price - pos.outer.begin_price
|
data/app/models/iro/purse.rb
CHANGED
|
@@ -28,10 +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
|
|
31
|
+
# validates :available_amount, presence: true
|
|
35
32
|
|
|
36
33
|
def to_s
|
|
37
34
|
slug
|
data/app/models/iro/strategy.rb
CHANGED
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,69 @@ 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
|
+
|
|
36
99
|
## obsolete, I don't do covered calls anymore?
|
|
37
100
|
def self.roll_covered_call_q pos
|
|
38
101
|
roll_price = pos.inner.begin_price - pos.autoprev.inner.end_price
|
|
@@ -117,8 +180,8 @@ class Tda::Order
|
|
|
117
180
|
return query
|
|
118
181
|
end
|
|
119
182
|
|
|
120
|
-
def self.place_order query
|
|
121
|
-
puts! query, '#place_order'
|
|
183
|
+
def self.place_order! query
|
|
184
|
+
# puts! query, '#place_order'
|
|
122
185
|
|
|
123
186
|
profile = Wco::Profile.pi
|
|
124
187
|
results = self.post("/accounts/#{profile.schwab_account_hash}/orders", {
|
|
@@ -129,8 +192,14 @@ class Tda::Order
|
|
|
129
192
|
},
|
|
130
193
|
body: query.to_json,
|
|
131
194
|
})
|
|
195
|
+
puts! results, 'place_order!() results'
|
|
196
|
+
puts! results.code, 'results.code'
|
|
197
|
+
# if 201 != results.code
|
|
198
|
+
# throw results
|
|
199
|
+
# end
|
|
132
200
|
order_id = results.headers['location'].split('/').last
|
|
133
|
-
|
|
201
|
+
# response = JSON.parse results.body
|
|
202
|
+
return { schwab_order_id: order_id, schwab_status: 'WORKING' }
|
|
134
203
|
end
|
|
135
204
|
|
|
136
205
|
end
|
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
|