wco_models 3.1.0.265 → 3.1.0.267
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 +28 -95
- data/app/models/iro/option.rb-bk +62 -0
- data/app/models/iro/position.rb +38 -6
- data/app/models/iro/purse.rb +1 -4
- data/app/models/iro/stock.rb +33 -4
- data/app/models/iro/strategy.rb +0 -3
- data/app/models/tda/order.rb +121 -17
- 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: a1f54c396bd801b8243d24de86159a104d390d1b74ce79bd12c3bde3bda2d682
|
|
4
|
+
data.tar.gz: 137492819d59dc6cffb3a220f74f4352970dd69a3d8a01a585e3f17c7260dd1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b8b9b3d27b172ad9094d5231865e2864881696718bb014a4ae7c73405a5e58eb4df313ef1340a14bbc6109293b1656e060493d0127eb00988bb9f8400ac92d7f
|
|
7
|
+
data.tar.gz: cc019d6173c28f78dd506300a8bd692086d9d84752e2744dfa547d03d61dc3a47513af1be09653e588200ed444d0528bf5c940294d3c65e2b7e64a8d17a3139e
|
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,41 @@ 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
|
|
68
|
+
|
|
93
69
|
=begin
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
70
|
+
symbol = "META 260424P00500000"
|
|
71
|
+
=end
|
|
72
|
+
def self.symbol_to_h symbol
|
|
73
|
+
ticker = symbol[0,6].strip
|
|
74
|
+
date_str = symbol[6,6]
|
|
75
|
+
type = symbol[12] == 'P' ? 'PUT' : 'CALL'
|
|
76
|
+
strike_str = symbol[13,8]
|
|
77
|
+
expires_on = Date.strptime(date_str, "%y%m%d")
|
|
78
|
+
strike = strike_str.to_i / 1000.0
|
|
79
|
+
return {
|
|
80
|
+
ticker: ticker,
|
|
81
|
+
strike: strike,
|
|
82
|
+
put_call: type,
|
|
83
|
+
expires_on: expires_on,
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def matches_h h
|
|
88
|
+
if h[:put_call] == put_call &&
|
|
89
|
+
h[:strike] == strike &&
|
|
90
|
+
h[:expires_on] == expires_on
|
|
91
|
+
return true
|
|
92
|
+
else
|
|
93
|
+
return false
|
|
101
94
|
end
|
|
102
|
-
self[:symbol]
|
|
103
95
|
end
|
|
104
|
-
=end
|
|
105
96
|
|
|
106
97
|
# before_save :sync, if: ->() { !Rails.env.test? } ## do not sync in test
|
|
107
98
|
def sync
|
|
@@ -117,65 +108,7 @@ class Iro::Option
|
|
|
117
108
|
self.save! ## 2026-02-19 this must be present.
|
|
118
109
|
end
|
|
119
110
|
|
|
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
|
|
111
|
+
def to_s
|
|
112
|
+
"#{symbol} :: #{expires_on.strftime('%Y-%m-%d')} #{put_call} #{strike}"
|
|
178
113
|
end
|
|
179
|
-
|
|
180
|
-
|
|
181
114
|
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
|
@@ -7,7 +7,6 @@ class Iro::Position
|
|
|
7
7
|
|
|
8
8
|
field :next_gain_loss_amount, type: :float
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
STATUS_ACTIVE = 'active'
|
|
12
11
|
STATUS_CLOSED = 'closed'
|
|
13
12
|
STATUS_PREPARE = 'prepare'
|
|
@@ -17,14 +16,31 @@ class Iro::Position
|
|
|
17
16
|
STATUSES = [ nil, STATUS_CLOSED, STATUS_ACTIVE, STATUS_PREPARE, STATUS_PROPOSED, STATUS_PENDING ]
|
|
18
17
|
field :status
|
|
19
18
|
validates :status, presence: true
|
|
20
|
-
scope :active,
|
|
21
|
-
|
|
19
|
+
scope :active, ->{ where( status: 'active' ) }
|
|
20
|
+
scope :proposed, ->{ where( status: 'proposed' ) }
|
|
21
|
+
|
|
22
|
+
def status_label st
|
|
23
|
+
labels = {}
|
|
24
|
+
labels[STATUS_PROPOSED] = 'Selected.'
|
|
25
|
+
return labels[st] || st
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
INTENT_CLOSE = 'close.'
|
|
29
|
+
INTENTS = [ nil, INTENT_CLOSE ]
|
|
30
|
+
field :intent
|
|
22
31
|
|
|
23
32
|
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
|
24
33
|
index({ purse_id: 1, ticker: 1 })
|
|
25
34
|
|
|
26
35
|
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :positions
|
|
27
|
-
|
|
36
|
+
field :ticker
|
|
37
|
+
def ticker
|
|
38
|
+
if !self[:ticker]
|
|
39
|
+
self[:ticker] = stock.ticker
|
|
40
|
+
self.save
|
|
41
|
+
end
|
|
42
|
+
self[:ticker]
|
|
43
|
+
end
|
|
28
44
|
|
|
29
45
|
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
|
30
46
|
delegate :long_or_short, to: :strategy
|
|
@@ -77,6 +93,7 @@ class Iro::Position
|
|
|
77
93
|
field :end_on
|
|
78
94
|
|
|
79
95
|
field :schwab_order_id, type: :integer
|
|
96
|
+
field :schwab_status
|
|
80
97
|
|
|
81
98
|
def begin_delta
|
|
82
99
|
strategy.send("begin_delta_#{strategy.kind}", self)
|
|
@@ -126,6 +143,23 @@ class Iro::Position
|
|
|
126
143
|
print '^'
|
|
127
144
|
end
|
|
128
145
|
|
|
146
|
+
|
|
147
|
+
field :pending_price
|
|
148
|
+
|
|
149
|
+
## credit spread only
|
|
150
|
+
def close_price
|
|
151
|
+
pos = self
|
|
152
|
+
out = pos.outer.end_price - pos.inner.end_price
|
|
153
|
+
return out.round(2)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
## place2 = credit-spread
|
|
157
|
+
def place2_price
|
|
158
|
+
pos = self
|
|
159
|
+
out = pos.inner.begin_price - pos.outer.begin_price
|
|
160
|
+
return out.round(2)
|
|
161
|
+
end
|
|
162
|
+
|
|
129
163
|
def roll_price
|
|
130
164
|
pos = self
|
|
131
165
|
out = pos.autoprev.outer.end_price - pos.autoprev.inner.end_price + pos.inner.begin_price - pos.outer.begin_price
|
|
@@ -188,8 +222,6 @@ class Iro::Position
|
|
|
188
222
|
def calc_rollp
|
|
189
223
|
pos = self
|
|
190
224
|
pos.next_reasons = []
|
|
191
|
-
# pos.next_symbol = nil
|
|
192
|
-
# pos.next_delta = nil
|
|
193
225
|
|
|
194
226
|
out = strategy.send("calc_rollp_#{strategy.kind}", pos )
|
|
195
227
|
|
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/stock.rb
CHANGED
|
@@ -73,12 +73,43 @@ class ::Iro::Stock
|
|
|
73
73
|
|
|
74
74
|
=end
|
|
75
75
|
field :volatility, type: :float
|
|
76
|
+
field :volatility_annual
|
|
77
|
+
field :volatility_monthly
|
|
78
|
+
field :volatility_daily
|
|
76
79
|
def volatility duration: 1.year, recompute: false
|
|
77
80
|
if self[:volatility]
|
|
78
81
|
if !recompute
|
|
79
82
|
return self[:volatility]
|
|
80
83
|
end
|
|
81
84
|
end
|
|
85
|
+
stock = self
|
|
86
|
+
begin_on = Time.now - duration
|
|
87
|
+
points = ::Iro::Datapoint.where( kind: 'STOCK', symbol: stock.ticker,
|
|
88
|
+
:date.gte => begin_on,
|
|
89
|
+
).order_by( date: :asc )
|
|
90
|
+
|
|
91
|
+
returns = []
|
|
92
|
+
points.each_cons(2) do |prev, curr|
|
|
93
|
+
returns << Math.log(curr.value / prev.value)
|
|
94
|
+
end
|
|
95
|
+
# puts! returns, 'returns'
|
|
96
|
+
|
|
97
|
+
mean = returns.sum / returns.size
|
|
98
|
+
variance = returns.sum { |r| (r - mean) ** 2 } / (returns.size - 1)
|
|
99
|
+
|
|
100
|
+
daily_vol = Math.sqrt(variance)
|
|
101
|
+
monthly_vol = daily_vol * Math.sqrt(21)
|
|
102
|
+
annual_vol = daily_vol * Math.sqrt(252)
|
|
103
|
+
|
|
104
|
+
self.update(volatility_annual: annual_vol, volatility_monthly: monthly_vol, volatility_daily: daily_vol)
|
|
105
|
+
annual_vol
|
|
106
|
+
end
|
|
107
|
+
def volatility_old duration: 1.year, recompute: false
|
|
108
|
+
if self[:volatility]
|
|
109
|
+
if !recompute
|
|
110
|
+
return self[:volatility]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
82
113
|
|
|
83
114
|
stock = self
|
|
84
115
|
begin_on = Time.now - duration - 1.day
|
|
@@ -107,10 +138,8 @@ class ::Iro::Stock
|
|
|
107
138
|
|
|
108
139
|
# n_periods = begin_on.to_date.business_days_until( Date.today )
|
|
109
140
|
out = Math.sqrt( sum_of_sq )*sqrt( n )
|
|
110
|
-
adjustment = 2.0
|
|
111
|
-
out = out * adjustment
|
|
112
141
|
puts! out, 'volatility (adjusted)'
|
|
113
|
-
self.update volatility: out
|
|
142
|
+
self.update( volatility: out, volatility_annual: out )
|
|
114
143
|
return out
|
|
115
144
|
end
|
|
116
145
|
|
|
@@ -147,7 +176,7 @@ class ::Iro::Stock
|
|
|
147
176
|
date_from ||= Time.now - 1.year - 1.week
|
|
148
177
|
date_to ||= date_from + 180.days
|
|
149
178
|
date_from = date_from.strftime('%Y-%m-%d')
|
|
150
|
-
date_to
|
|
179
|
+
date_to = date_to.strftime('%Y-%m-%d')
|
|
151
180
|
puts! [ticker, date_from, date_to], "ticker,date_from,date_to"
|
|
152
181
|
outs = HTTParty.get("https://api.stockdata.org/v1/data/eod?symbols=#{ticker}&date_from=#{date_from}&date_to=#{date_to}&api_token=#{STOCKDATA_ORG_KEY}")
|
|
153
182
|
outs['data'].each do |datum|
|
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,126 @@ 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
|
+
|
|
70
|
+
def self.close_credit_spread_q pos
|
|
71
|
+
query = {
|
|
72
|
+
orderType: pos.pending_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
73
|
+
session: "NORMAL",
|
|
74
|
+
duration: "DAY",
|
|
75
|
+
price: pos.pending_price.abs,
|
|
76
|
+
orderStrategyType: "SINGLE",
|
|
77
|
+
orderLegCollection: [
|
|
78
|
+
## close
|
|
79
|
+
{
|
|
80
|
+
instruction: "BUY_TO_CLOSE",
|
|
81
|
+
quantity: pos.q,
|
|
82
|
+
instrument: {
|
|
83
|
+
symbol: pos.inner.symbol,
|
|
84
|
+
assetType: "OPTION",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
instruction: "SELL_TO_CLOSE",
|
|
89
|
+
quantity: pos.q,
|
|
90
|
+
instrument: {
|
|
91
|
+
symbol: pos.outer.symbol,
|
|
92
|
+
assetType: "OPTION",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
return query
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
## open credit spread?!
|
|
102
|
+
def self.credit_spread_q pos
|
|
103
|
+
query = {
|
|
104
|
+
orderType: pos.place2_price > 0 ? "NET_CREDIT" : "NET_DEBIT",
|
|
105
|
+
session: "NORMAL",
|
|
106
|
+
duration: "DAY",
|
|
107
|
+
price: pos.pending_price,
|
|
108
|
+
orderStrategyType: "SINGLE",
|
|
109
|
+
orderLegCollection: [
|
|
110
|
+
## open
|
|
111
|
+
{
|
|
112
|
+
instruction: "BUY_TO_OPEN",
|
|
113
|
+
quantity: pos.q,
|
|
114
|
+
instrument: {
|
|
115
|
+
symbol: pos.outer.symbol,
|
|
116
|
+
assetType: "OPTION",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
instruction: "SELL_TO_OPEN",
|
|
121
|
+
quantity: pos.q,
|
|
122
|
+
instrument: {
|
|
123
|
+
symbol: pos.inner.symbol,
|
|
124
|
+
assetType: "OPTION",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
return query
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def self.place_order! query
|
|
134
|
+
# puts! query, '#place_order'
|
|
135
|
+
|
|
136
|
+
profile = Wco::Profile.pi
|
|
137
|
+
results = self.post("/accounts/#{profile.schwab_account_hash}/orders", {
|
|
138
|
+
headers: {
|
|
139
|
+
'content-type' => 'application/json',
|
|
140
|
+
accept: 'application/json',
|
|
141
|
+
Authorization: "Bearer #{profile[:schwab_exec_access_token]}",
|
|
142
|
+
},
|
|
143
|
+
body: query.to_json,
|
|
144
|
+
})
|
|
145
|
+
puts! results, 'place_order!() results'
|
|
146
|
+
puts! results.code, 'results.code'
|
|
147
|
+
# if 201 != results.code
|
|
148
|
+
# throw results
|
|
149
|
+
# end
|
|
150
|
+
order_id = results.headers['location'].split('/').last
|
|
151
|
+
# response = JSON.parse results.body
|
|
152
|
+
return { schwab_order_id: order_id, schwab_status: 'WORKING' }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
|
|
36
156
|
## obsolete, I don't do covered calls anymore?
|
|
37
157
|
def self.roll_covered_call_q pos
|
|
38
158
|
roll_price = pos.inner.begin_price - pos.autoprev.inner.end_price
|
|
@@ -117,20 +237,4 @@ class Tda::Order
|
|
|
117
237
|
return query
|
|
118
238
|
end
|
|
119
239
|
|
|
120
|
-
def self.place_order query
|
|
121
|
-
puts! query, '#place_order'
|
|
122
|
-
|
|
123
|
-
profile = Wco::Profile.pi
|
|
124
|
-
results = self.post("/accounts/#{profile.schwab_account_hash}/orders", {
|
|
125
|
-
headers: {
|
|
126
|
-
'content-type' => 'application/json',
|
|
127
|
-
accept: 'application/json',
|
|
128
|
-
Authorization: "Bearer #{profile[:schwab_exec_access_token]}",
|
|
129
|
-
},
|
|
130
|
-
body: query.to_json,
|
|
131
|
-
})
|
|
132
|
-
order_id = results.headers['location'].split('/').last
|
|
133
|
-
return order_id
|
|
134
|
-
end
|
|
135
|
-
|
|
136
240
|
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.267
|
|
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
|