iron_warbler 2.0.7.41 → 2.0.7.42
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/controllers/iro/api/stocks_controller.rb +26 -25
- data/app/controllers/iro/application_controller.rb +1 -33
- data/app/controllers/iro/stocks_controller.rb +2 -9
- data/app/models/iro/option.rb +13 -1
- data/app/models/tda/option.rb +58 -50
- data/app/views/iro/api/stocks/index.json.jbuilder +4 -2
- data/app/views/iro/api/stocks/max_pain.json.jbuilder +6 -0
- data/lib/iron_warbler.rb +35 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63cc89c7ffba2eebf079f6470e3d7346d7fae502a5789e3822210ddb747fc3e1
|
4
|
+
data.tar.gz: faf3977f0f75b27653d9112c6ee993627a8a465aacb904866d5e047253989aa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7a17720e26f3226831d9381f91aa6202ae19be883d28c4b98210280c9afbda233550a0217d60212091d15b53ab708d636e62a953d21d04b5acc0c3dc31d0229
|
7
|
+
data.tar.gz: 9e9ce2229a98c994619f8b3a6f3134babf1acbf9c709d8a9a19d21c60c8d19da96e893aa7c571b074019402b3479bf903fbbd89c25ede452b17d51794b374fff
|
@@ -2,6 +2,23 @@
|
|
2
2
|
class Iro::Api::StocksController < Iro::ApiController
|
3
3
|
before_action :set_stock, only: [:destroy, :edit, :max_pain, :show, :update ]
|
4
4
|
|
5
|
+
def create
|
6
|
+
@stock = Iro::Stock.new(stock_params)
|
7
|
+
authorize! :create, @stock
|
8
|
+
|
9
|
+
if @stock.save
|
10
|
+
flash_notice @stock
|
11
|
+
else
|
12
|
+
flash_alert @stock
|
13
|
+
end
|
14
|
+
redirect_to action: :index
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy
|
18
|
+
@stock.destroy
|
19
|
+
redirect_to stocks_url, notice: 'Stock was successfully destroyed.'
|
20
|
+
end
|
21
|
+
|
5
22
|
def index
|
6
23
|
@stocks = Iro::Stock.active
|
7
24
|
authorize! :index, Iro::Stock
|
@@ -14,8 +31,9 @@ class Iro::Api::StocksController < Iro::ApiController
|
|
14
31
|
|
15
32
|
def max_pain
|
16
33
|
authorize! :max_pain, @stock
|
34
|
+
Iro::Iro.schwab_sync
|
17
35
|
|
18
|
-
hash = Tda::Option.get_chains({ ticker: @stock.ticker })
|
36
|
+
hash = Tda::Option.get_chains({ ticker: @stock.ticker, force: false })
|
19
37
|
# hash = JSON.parse File.read './trash.json'
|
20
38
|
@max_pain = Iro::Option.max_pain hash
|
21
39
|
|
@@ -27,6 +45,12 @@ class Iro::Api::StocksController < Iro::ApiController
|
|
27
45
|
end
|
28
46
|
end
|
29
47
|
|
48
|
+
def new
|
49
|
+
@stock = Iro::Stock.new
|
50
|
+
authorize! :new, @stock
|
51
|
+
end
|
52
|
+
|
53
|
+
|
30
54
|
def show
|
31
55
|
authorize! :show, @stock
|
32
56
|
end_on = Time.now.to_date.in_time_zone('UTC')
|
@@ -56,26 +80,6 @@ class Iro::Api::StocksController < Iro::ApiController
|
|
56
80
|
}).order_by({ quote_at: :asc })
|
57
81
|
end
|
58
82
|
|
59
|
-
def new
|
60
|
-
@stock = Iro::Stock.new
|
61
|
-
authorize! :new, @stock
|
62
|
-
end
|
63
|
-
|
64
|
-
def edit
|
65
|
-
end
|
66
|
-
|
67
|
-
def create
|
68
|
-
@stock = Iro::Stock.new(stock_params)
|
69
|
-
authorize! :create, @stock
|
70
|
-
|
71
|
-
if @stock.save
|
72
|
-
flash_notice @stock
|
73
|
-
else
|
74
|
-
flash_alert @stock
|
75
|
-
end
|
76
|
-
redirect_to action: :index
|
77
|
-
end
|
78
|
-
|
79
83
|
def update
|
80
84
|
@stock = Iro::Stock.find params[:id]
|
81
85
|
authorize! :update, @stock
|
@@ -87,10 +91,7 @@ class Iro::Api::StocksController < Iro::ApiController
|
|
87
91
|
redirect_to request.referrer
|
88
92
|
end
|
89
93
|
|
90
|
-
|
91
|
-
@stock.destroy
|
92
|
-
redirect_to stocks_url, notice: 'Stock was successfully destroyed.'
|
93
|
-
end
|
94
|
+
|
94
95
|
|
95
96
|
##
|
96
97
|
## private
|
@@ -5,11 +5,6 @@ Po ||= Iro::Position
|
|
5
5
|
O ||= Iro::Option
|
6
6
|
Sto ||= Iro::Stock
|
7
7
|
|
8
|
-
class Schwab
|
9
|
-
include HTTParty
|
10
|
-
debug_output $stdout
|
11
|
-
end
|
12
|
-
|
13
8
|
class Iro::ApplicationController < Wco::ApplicationController
|
14
9
|
layout 'iro/application'
|
15
10
|
|
@@ -21,34 +16,7 @@ class Iro::ApplicationController < Wco::ApplicationController
|
|
21
16
|
|
22
17
|
def schwab_sync
|
23
18
|
authorize! :shwab_sync, Iro
|
24
|
-
|
25
|
-
|
26
|
-
out = Schwab.post( "https://api.schwabapi.com/v1/oauth/token", {
|
27
|
-
headers: {
|
28
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
29
|
-
},
|
30
|
-
basic_auth: { username: SCHWAB_DATA[:key], password: SCHWAB_DATA[:secret] },
|
31
|
-
body: {
|
32
|
-
grant_type: 'refresh_token',
|
33
|
-
refresh_token: profile.schwab_refresh_token
|
34
|
-
},
|
35
|
-
})
|
36
|
-
out = out.parsed_response
|
37
|
-
# puts! out, 'out'
|
38
|
-
|
39
|
-
attrs = {
|
40
|
-
schwab_access_token: out['access_token'],
|
41
|
-
schwab_refresh_token: out['refresh_token'],
|
42
|
-
schwab_id_token: out['id_token'],
|
43
|
-
}
|
44
|
-
# puts! attrs, 'attrs'
|
45
|
-
|
46
|
-
if attrs[:schwab_refresh_token]
|
47
|
-
profile.update(attrs)
|
48
|
-
profile.save!
|
49
|
-
end
|
50
|
-
|
51
|
-
render json: { attrs: attrs, out: out }
|
19
|
+
render json: Iro::Iro.schwab_sync
|
52
20
|
end
|
53
21
|
|
54
22
|
##
|
@@ -38,8 +38,6 @@ class Iro::StocksController < Iro::ApplicationController
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
41
|
def new
|
44
42
|
@stock = Iro::Stock.new
|
45
43
|
authorize! :new, @stock
|
@@ -66,13 +64,8 @@ class Iro::StocksController < Iro::ApplicationController
|
|
66
64
|
symbol: @stock.ticker,
|
67
65
|
}).order_by({ date: :desc }).limit(100)
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
hash = JSON.parse File.read filename
|
72
|
-
else
|
73
|
-
hash = Tda::Option.get_chains({ ticker: @stock.ticker })
|
74
|
-
File.write filename, hash.to_json
|
75
|
-
end
|
67
|
+
## @deprecated, use api/stocks_controller#max_pain
|
68
|
+
hash = Tda::Option.get_chains({ ticker: @stock.ticker, force: false })
|
76
69
|
@max_pain = Iro::Option.max_pain hash
|
77
70
|
@max_pain_summary = {}
|
78
71
|
@max_pain.each do |date, types|
|
data/app/models/iro/option.rb
CHANGED
@@ -96,8 +96,9 @@ class Iro::Option
|
|
96
96
|
date = _date.split(':')[0].to_date.to_s
|
97
97
|
outs[date] ||= {
|
98
98
|
'all' => {},
|
99
|
-
'put' => {},
|
100
99
|
'call' => {},
|
100
|
+
'put' => {},
|
101
|
+
'summary' => {},
|
101
102
|
}
|
102
103
|
|
103
104
|
strikes.each do |_strike, _v| ## _strike="18.5"
|
@@ -132,6 +133,17 @@ class Iro::Option
|
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
136
|
+
## compute summary
|
137
|
+
outs.each do |date, types|
|
138
|
+
all = types['all']
|
139
|
+
outs[date]['summary'] = { 'value' => all.keys[0] }
|
140
|
+
all.each do |strike, amount|
|
141
|
+
if amount < all[ outs[date]['summary']['value'] ]
|
142
|
+
outs[date]['summary']['value'] = strike
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
135
147
|
return outs
|
136
148
|
end
|
137
149
|
|
data/app/models/tda/option.rb
CHANGED
@@ -19,69 +19,77 @@ class Tda::Option
|
|
19
19
|
|
20
20
|
##
|
21
21
|
## Get entire chains for a ticker
|
22
|
-
## params: { ticker, }
|
22
|
+
## params: { ticker, force }
|
23
23
|
##
|
24
24
|
## 2024-08-09 :: Continue
|
25
|
+
## 2024-08-21 :: Continue : )
|
25
26
|
##
|
26
27
|
def self.get_chains params
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
puts! query, 'query'
|
31
|
-
|
32
|
-
headers = {
|
33
|
-
accept: 'application/json',
|
34
|
-
Authorization: "Bearer #{profile[:schwab_access_token]}",
|
35
|
-
}
|
36
|
-
path = "/chains"
|
37
|
-
out = self.get path, {
|
38
|
-
headers: headers,
|
39
|
-
query: query }
|
40
|
-
timestamp = DateTime.parse out.headers['date']
|
41
|
-
out = out.parsed_response
|
28
|
+
filename = "./data/schwab/#{Time.now.to_date.to_s}-#{params[:ticker]}-chains.json"
|
29
|
+
if !params[:force] && File.exists?( filename)
|
30
|
+
return JSON.parse File.read filename
|
42
31
|
|
43
|
-
|
32
|
+
else
|
33
|
+
profile = Wco::Profile.find_by email: 'piousbox@gmail.com'
|
34
|
+
query = { symbol: params[:ticker] } ## use 'GME' as symbol here even though a symbol is eg 'GME_021023P2.5'
|
35
|
+
# puts! query, 'query'
|
44
36
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
37
|
+
headers = {
|
38
|
+
accept: 'application/json',
|
39
|
+
Authorization: "Bearer #{profile[:schwab_access_token]}",
|
40
|
+
}
|
41
|
+
path = "/chains"
|
42
|
+
out = self.get path, {
|
43
|
+
headers: headers,
|
44
|
+
query: query }
|
45
|
+
timestamp = DateTime.parse out.headers['date']
|
46
|
+
out = out.parsed_response
|
47
|
+
puts! out, 'outs'
|
48
|
+
|
49
|
+
# byebug
|
50
|
+
|
51
|
+
outs = []
|
52
|
+
%w| put call |.each do |contractType|
|
53
|
+
_out = out["#{contractType}ExpDateMap"]
|
54
|
+
_out.each do |date, vs| ## date="2023-02-10:5"
|
55
|
+
vs.each do |strike, _v| ## strike="18.5"
|
56
|
+
_v = _v[0] ## weird, keep
|
57
|
+
# puts! _v, '_v'
|
58
|
+
|
59
|
+
v = {
|
60
|
+
putCall: _v['putCall'],
|
61
|
+
symbol: _v['symbol'],
|
62
|
+
bid: _v['bid'],
|
63
|
+
ask: _v['ask'],
|
64
|
+
last: _v['last'],
|
65
|
+
totalVolume: _v['totalVolume'],
|
66
|
+
openInterest: _v['openInterest'],
|
67
|
+
strikePrice: _v['strikePrice'],
|
68
|
+
expirationDate: _v['expirationDate'],
|
69
|
+
}
|
70
|
+
v.each do |k, i|
|
71
|
+
if i == 'NaN'
|
72
|
+
v[k] = nil
|
73
|
+
end
|
67
74
|
end
|
68
|
-
end
|
69
75
|
|
70
|
-
|
71
|
-
|
72
|
-
|
76
|
+
v[:timestamp] = timestamp
|
77
|
+
v[:ticker] = params[:ticker]
|
78
|
+
outs.push( v )
|
79
|
+
end
|
73
80
|
end
|
74
81
|
end
|
75
|
-
end
|
76
82
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
83
|
+
outs.each do |out|
|
84
|
+
opi = ::Iro::Priceitem.create( out )
|
85
|
+
if !opi.persisted?
|
86
|
+
puts! opi.errors.full_messages, "Cannot create PriceItem"
|
87
|
+
end
|
81
88
|
end
|
82
|
-
end
|
83
89
|
|
84
|
-
|
90
|
+
File.write filename, out.to_json
|
91
|
+
return out
|
92
|
+
end
|
85
93
|
end
|
86
94
|
|
87
95
|
##
|
data/lib/iron_warbler.rb
CHANGED
@@ -5,6 +5,11 @@ require 'mongoid'
|
|
5
5
|
|
6
6
|
require "iro/engine"
|
7
7
|
|
8
|
+
class Schwab
|
9
|
+
include HTTParty
|
10
|
+
debug_output $stdout
|
11
|
+
end
|
12
|
+
|
8
13
|
class Iro::Iro
|
9
14
|
|
10
15
|
def self.get_coins
|
@@ -162,4 +167,34 @@ class Iro::Iro
|
|
162
167
|
end
|
163
168
|
end
|
164
169
|
|
170
|
+
def self.schwab_sync
|
171
|
+
profile = Wco::Profile.find_by email: 'piousbox@gmail.com'
|
172
|
+
out = Schwab.post( "https://api.schwabapi.com/v1/oauth/token", {
|
173
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
174
|
+
basic_auth: { username: SCHWAB_DATA[:key], password: SCHWAB_DATA[:secret] },
|
175
|
+
body: {
|
176
|
+
grant_type: 'refresh_token',
|
177
|
+
refresh_token: profile.schwab_refresh_token
|
178
|
+
},
|
179
|
+
})
|
180
|
+
out = out.parsed_response
|
181
|
+
puts! out, 'out'
|
182
|
+
|
183
|
+
attrs = {
|
184
|
+
schwab_access_token: out['access_token'],
|
185
|
+
schwab_refresh_token: out['refresh_token'],
|
186
|
+
schwab_id_token: out['id_token'],
|
187
|
+
}
|
188
|
+
# puts! attrs, 'attrs'
|
189
|
+
|
190
|
+
if attrs[:schwab_refresh_token]
|
191
|
+
profile.update(attrs)
|
192
|
+
profile.save!
|
193
|
+
return attrs
|
194
|
+
else
|
195
|
+
return false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
165
200
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iron_warbler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.7.
|
4
|
+
version: 2.0.7.42
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Pudeyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: business_time
|