iron_warbler 2.0.7.8 → 2.0.7.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/iron_warbler/positions.scss +47 -0
- data/app/assets/stylesheets/iron_warbler/strategies.scss +0 -0
- data/app/assets/stylesheets/iron_warbler/utils.css +44 -0
- data/app/controllers/iro/alerts_controller.rb +6 -6
- data/app/controllers/iro/datapoints_controller.rb +0 -2
- data/app/controllers/iro/positions_controller.rb +62 -0
- data/app/controllers/iro/profiles_controller.rb +0 -2
- data/app/controllers/iro/purses_controller.rb +65 -0
- data/app/controllers/iro/stocks_controller.rb +10 -4
- data/app/controllers/iro/strategies_controller.rb +66 -0
- data/app/models/iro/option.rb +0 -7
- data/app/models/iro/position.rb +222 -0
- data/app/models/iro/purse.rb +17 -0
- data/app/models/iro/stock.rb +12 -2
- data/app/models/iro/strategy.rb +38 -0
- data/app/models/tda/option.rb +137 -0
- data/app/models/tda/{api.rb → stock.rb} +1 -1
- data/app/views/iro/_main_header.haml +16 -6
- data/app/views/iro/alerts/index.haml +1 -1
- data/app/views/iro/positions/_form.haml +59 -0
- data/app/views/iro/positions/_reasons.haml +4 -0
- data/app/views/iro/positions/_table.haml +137 -0
- data/app/views/iro/positions/edit.haml +4 -0
- data/app/views/iro/positions/new.haml +4 -0
- data/app/views/iro/purses/_form.haml +9 -0
- data/app/views/iro/purses/edit.haml +3 -0
- data/app/views/iro/purses/index.haml +11 -0
- data/app/views/iro/purses/show.haml +10 -0
- data/app/views/iro/stocks/_form.haml +4 -0
- data/app/views/iro/strategies/_form.haml +37 -0
- data/app/views/iro/strategies/_header.haml +8 -0
- data/app/views/iro/strategies/_show.haml +18 -0
- data/app/views/iro/strategies/_table.haml +18 -0
- data/app/views/iro/strategies/edit.haml +3 -0
- data/app/views/iro/strategies/new.haml +3 -0
- data/config/routes.rb +6 -0
- data/lib/iron_warbler.rb +4 -0
- data/lib/tasks/iro_tasks.rake +35 -0
- metadata +42 -13
- data/app/assets/stylesheets/iron_warbler/main.css +0 -13
- data/app/assets/stylesheets/scaffold.css +0 -80
- data/app/views/iro/alerts/edit.html.erb +0 -6
- data/app/views/iro/alerts/new.html.erb +0 -5
- data/app/views/iro/alerts/show.html.erb +0 -34
- data/app/views/iro/profiles/_form.html.erb +0 -32
- data/app/views/iro/profiles/edit.html.erb +0 -6
- data/app/views/iro/profiles/index.html.erb +0 -31
- data/app/views/iro/profiles/new.html.erb +0 -5
- data/app/views/iro/profiles/show.html.erb +0 -19
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
class Iro::Strategy
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps
|
5
|
+
store_in collection: 'iro_strategies'
|
6
|
+
|
7
|
+
field :slug
|
8
|
+
validates :slug, presence: true, uniqueness: true
|
9
|
+
|
10
|
+
has_many :positions, class_name: 'Iro::Position', inverse_of: :strategy
|
11
|
+
|
12
|
+
## multiple strategies per ticker
|
13
|
+
field :ticker
|
14
|
+
validates :ticker, presence: true
|
15
|
+
index({ ticker: 1 })
|
16
|
+
# belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
|
17
|
+
|
18
|
+
|
19
|
+
field :buffer_above_water, type: :float
|
20
|
+
field :next_max_delta, type: :float
|
21
|
+
field :next_min_strike, type: :float
|
22
|
+
field :threshold_delta, type: :float
|
23
|
+
field :threshold_netp, type: :float
|
24
|
+
|
25
|
+
def self.for_ticker ticker
|
26
|
+
where( ticker: ticker )
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
slug
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.list
|
35
|
+
[[nil,nil]] + all.map { |ttt| [ ttt.slug, ttt.id ] }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
class Tda::Option
|
5
|
+
|
6
|
+
include ::HTTParty
|
7
|
+
base_uri 'https://api.tdameritrade.com'
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
## 2023-02-05 _vp_ :: Gets the entire chain
|
12
|
+
##
|
13
|
+
def self.get_chain params
|
14
|
+
opts = { symbol: params[:ticker] } ## use 'GME' as symbol here even though a symbol is eg 'GME_021023P2.5'
|
15
|
+
query = { apikey: ::TD_AMERITRADE[:apiKey] }.merge opts
|
16
|
+
# puts! query, 'input opts'
|
17
|
+
|
18
|
+
path = "/v1/marketdata/chains"
|
19
|
+
out = self.get path, { query: query }
|
20
|
+
timestamp = DateTime.parse out.headers['date']
|
21
|
+
out = out.parsed_response.deep_symbolize_keys
|
22
|
+
|
23
|
+
|
24
|
+
outs = []
|
25
|
+
%w| put call |.each do |contractType|
|
26
|
+
tmp_sym = "#{contractType}ExpDateMap".to_sym
|
27
|
+
_out = out[tmp_sym]
|
28
|
+
_out.each do |date, vs| ## date="2023-02-10:5"
|
29
|
+
vs.each do |strike, _v| ## strike="18.5"
|
30
|
+
v = _v[0] ## v={} many attrs
|
31
|
+
v = v.except( :lastSize, :optionDeliverablesList, :settlementType,
|
32
|
+
:deliverableNote, :pennyPilot, :mini )
|
33
|
+
v.each do |k, i|
|
34
|
+
if i == 'NaN'
|
35
|
+
v[k] = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
v[:timestamp] = timestamp
|
40
|
+
v[:ticker] = params[:ticker]
|
41
|
+
outs.push( v )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
outs.each do |x|
|
47
|
+
opi = ::Iro::OptionPriceItem.create( x )
|
48
|
+
if !opi.persisted?
|
49
|
+
puts! opi.errors.full_messages, "Cannot create OptionPriceItem"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
## 2023-03-18 _vp_ This is what I should be using to check if a position should be rolled.
|
56
|
+
##
|
57
|
+
def self.get_quote params
|
58
|
+
::Tda::Option.get_quotes(params)[0]
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
## params: contractType, strike, expirationDate, ticker
|
63
|
+
##
|
64
|
+
## ow = { contractType: 'PUT', ticker: 'GME', date: '2022-12-09' }
|
65
|
+
## query = {:apikey=>"<>", :toDate=>"2022-12-09", :fromDate=>"2022-12-09", :symbol=>"GME"}
|
66
|
+
##
|
67
|
+
## 2023-02-04 _vp_ :: Too specific, but I want the entire chain, every 1-min
|
68
|
+
## 2023-02-06 _vp_ :: Continue.
|
69
|
+
##
|
70
|
+
def self.get_quotes params
|
71
|
+
puts! params, 'Tda::Option#get_quotes'
|
72
|
+
opts = {}
|
73
|
+
|
74
|
+
#
|
75
|
+
# Validate input ???
|
76
|
+
#
|
77
|
+
validOpts = %i| contractType |
|
78
|
+
validOpts.each do |s|
|
79
|
+
if params[s]
|
80
|
+
opts[s] = params[s]
|
81
|
+
else
|
82
|
+
raise Iwa::InputError.new("Invalid input, missing '#{s}'.")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
if params[:expirationDate]
|
86
|
+
opts[:fromDate] = opts[:toDate] = params[:expirationDate]
|
87
|
+
else
|
88
|
+
raise Iwa::InputError.new("Invalid input, missing 'date'.")
|
89
|
+
end
|
90
|
+
if params[:ticker]
|
91
|
+
opts[:symbol] = params[:ticker].upcase
|
92
|
+
else
|
93
|
+
raise Iwa::InputError.new("Invalid input, missing 'ticker'.")
|
94
|
+
end
|
95
|
+
|
96
|
+
if params[:strike]
|
97
|
+
opts[:strike] = params[:strike]
|
98
|
+
end
|
99
|
+
|
100
|
+
query = { apikey: ::TD_AMERITRADE[:apiKey] }.merge opts
|
101
|
+
# puts! query, 'input opts'
|
102
|
+
|
103
|
+
path = "/v1/marketdata/chains"
|
104
|
+
out = self.get path, { query: query }
|
105
|
+
timestamp = DateTime.parse out.headers['date']
|
106
|
+
## out = HTTParty.get "https://api.tdameritrade.com#{path}", { query: query }
|
107
|
+
out = out.parsed_response.deep_symbolize_keys
|
108
|
+
|
109
|
+
|
110
|
+
tmp_sym = "#{opts[:contractType].to_s.downcase}ExpDateMap".to_sym
|
111
|
+
outs = []
|
112
|
+
out = out[tmp_sym]
|
113
|
+
out.each do |date, vs|
|
114
|
+
vs.each do |strike, _v|
|
115
|
+
v = _v[0]
|
116
|
+
v = v.except( :lastSize, :optionDeliverablesList, :settlementType,
|
117
|
+
:deliverableNote, :pennyPilot, :mini )
|
118
|
+
v[:timestamp] = timestamp
|
119
|
+
outs.push( v )
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# puts! outs, 'outs'
|
124
|
+
return outs
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
=begin
|
131
|
+
|
132
|
+
outs = Tda::Option.get_quotes({
|
133
|
+
contractType: 'CALL', strike: 20.0, expirationDate: '2024-01-12',
|
134
|
+
ticker: 'GME',
|
135
|
+
})
|
136
|
+
|
137
|
+
=end
|
@@ -1,13 +1,23 @@
|
|
1
1
|
|
2
|
+
.main-header.maxwidth
|
2
3
|
|
4
|
+
%i.fa.fa-compress.collapse-expand#collapseHeaderEmail
|
5
|
+
Iron Warbler
|
6
|
+
|
7
|
+
-# .header.collapse-expand#IroMenu
|
8
|
+
-# %h5.title Iron Warbler
|
3
9
|
|
4
|
-
.main-menu
|
5
|
-
%h5 Main Menu
|
6
10
|
%ul
|
7
|
-
%li= link_to '
|
8
|
-
%li
|
11
|
+
%li= link_to 'ROOT', root_path
|
12
|
+
%li
|
13
|
+
= link_to "Stocks (#{Iro::Stock.all.length})", stocks_path
|
9
14
|
-# %li= link_to 'Options', options_path
|
10
|
-
%li Max Pain
|
11
|
-
%li
|
15
|
+
-# %li Max Pain
|
16
|
+
%li
|
17
|
+
= link_to "Alerts (#{Iro::Alert.all.length})", alerts_path
|
18
|
+
%li
|
19
|
+
= link_to "Purses (#{Iro::Purse.all.length})", purses_path
|
20
|
+
%li
|
21
|
+
= render '/iro/strategies/header'
|
12
22
|
|
13
23
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
.positions--form
|
3
|
+
= form_for position do |f|
|
4
|
+
.actions
|
5
|
+
= f.submit
|
6
|
+
|
7
|
+
.field
|
8
|
+
%label Purse
|
9
|
+
-# = f.select :purse_id, options_for_select( @
|
10
|
+
= f.text_field :purse_id
|
11
|
+
|
12
|
+
%label Status
|
13
|
+
= f.select :status, options_for_select( Iro::Position::STATUSES, selected: position.status )
|
14
|
+
|
15
|
+
%br
|
16
|
+
%br
|
17
|
+
|
18
|
+
.field
|
19
|
+
%label Ticker
|
20
|
+
= f.select :ticker, options_for_select( @tickers_list, selected: position.ticker )
|
21
|
+
|
22
|
+
|
23
|
+
%label Kind
|
24
|
+
= f.select :kind, options_for_select( Iro::Position::KINDS, selected: position.kind )
|
25
|
+
%label Strategy
|
26
|
+
= f.select :strategy_id, options_for_select( @strategies_list, selected: position.strategy_id )
|
27
|
+
|
28
|
+
|
29
|
+
%label Strike
|
30
|
+
= f.text_field :strike
|
31
|
+
|
32
|
+
.field
|
33
|
+
%label Expires on
|
34
|
+
= f.text_field :expires_on
|
35
|
+
|
36
|
+
|
37
|
+
%label Quantity
|
38
|
+
= f.text_field :quantity
|
39
|
+
|
40
|
+
%br
|
41
|
+
%br
|
42
|
+
|
43
|
+
.flex-row
|
44
|
+
%label Begin on
|
45
|
+
= f.text_field :begin_on
|
46
|
+
%label Begin price
|
47
|
+
= f.text_field :begin_price
|
48
|
+
%label Begin delta
|
49
|
+
= f.text_field :begin_delta
|
50
|
+
.flex-row
|
51
|
+
%label End on
|
52
|
+
= f.text_field :end_on
|
53
|
+
%label End price
|
54
|
+
= f.text_field :end_price
|
55
|
+
%label End delta
|
56
|
+
= f.text_field :end_delta
|
57
|
+
|
58
|
+
.actions
|
59
|
+
= f.submit
|
@@ -0,0 +1,137 @@
|
|
1
|
+
|
2
|
+
.positions--table
|
3
|
+
|
4
|
+
%h5 Positions
|
5
|
+
%table.bordered
|
6
|
+
%tr
|
7
|
+
%th
|
8
|
+
%th
|
9
|
+
.status
|
10
|
+
Status
|
11
|
+
%br
|
12
|
+
Strategy
|
13
|
+
%th.ticker
|
14
|
+
.a Ticker
|
15
|
+
%th
|
16
|
+
.kind
|
17
|
+
Kind
|
18
|
+
-# covered call (mounted elephant),
|
19
|
+
-# reckless defence (credit put spread) (spearman)
|
20
|
+
-# reckless re-defence (soaring eagle)
|
21
|
+
%th.strike
|
22
|
+
.a Strike
|
23
|
+
%th
|
24
|
+
.expires_on Expires On
|
25
|
+
%th
|
26
|
+
.end_n_days End N Days
|
27
|
+
%th
|
28
|
+
.quantity Q
|
29
|
+
|
30
|
+
%th
|
31
|
+
.begin_on begin_on
|
32
|
+
%th.begin_price
|
33
|
+
.a Begin price
|
34
|
+
%th.to_open
|
35
|
+
.a To open
|
36
|
+
%th
|
37
|
+
.begin_delta begin_delta
|
38
|
+
|
39
|
+
%th
|
40
|
+
.end_price End Price
|
41
|
+
%th
|
42
|
+
.net_amount Net Amount
|
43
|
+
%th
|
44
|
+
.net_percent Net %
|
45
|
+
%th.end_delta
|
46
|
+
.a end_delta
|
47
|
+
-# %th
|
48
|
+
-# .end_n_days End n days
|
49
|
+
-# %th
|
50
|
+
-# .end_on End on
|
51
|
+
%th.should_rollp
|
52
|
+
.a should_rollp
|
53
|
+
%th.next_position
|
54
|
+
.a
|
55
|
+
next_symbol
|
56
|
+
%br
|
57
|
+
next_delta
|
58
|
+
%br
|
59
|
+
next_outcome
|
60
|
+
|
61
|
+
- positions.each_with_index do |position, idx|
|
62
|
+
- if idx > 0
|
63
|
+
- prev_position = positions[idx-1]
|
64
|
+
- if prev_position && position[:expires_on] != prev_position[:expires_on]
|
65
|
+
%tr
|
66
|
+
%td{ colspan: 18 }
|
67
|
+
%hr
|
68
|
+
|
69
|
+
%tr
|
70
|
+
%td.actions
|
71
|
+
.flex-row
|
72
|
+
= button_to 'x', position_path(position), method: :delete, data: { confirm: 'Are you sure?' }
|
73
|
+
= link_to '[~]', edit_position_path(position)
|
74
|
+
- if position.next_reasons.present? && position.should_rollp && position.should_rollp > 0.5
|
75
|
+
= button_to 'Roll', roll_position_path(position)
|
76
|
+
- if position.next_reasons.present?
|
77
|
+
.collapse-expand{ id: "ce-p-#{position.id}" } [Reasons]
|
78
|
+
= render '/iro/positions/reasons', reasons: position.next_reasons
|
79
|
+
|
80
|
+
|
81
|
+
%td.status.strategy
|
82
|
+
= position.status
|
83
|
+
-# %br
|
84
|
+
-# = link_to "[~ #{position.strategy}]", edit_strategy_path(position.strategy)
|
85
|
+
.collapse-expand{ id: "ce-pos-reasons-#{position.id}" }
|
86
|
+
[#{position.strategy}]
|
87
|
+
= render '/iro/strategies/show', strategy: position.strategy
|
88
|
+
|
89
|
+
%td.ticker
|
90
|
+
= position.ticker
|
91
|
+
%br
|
92
|
+
= pp_amount position.current_underlying_strike
|
93
|
+
%td.kind.mini
|
94
|
+
= position.kind
|
95
|
+
%td.strike
|
96
|
+
= pp_amount position.strike
|
97
|
+
%td.expires_on.mini
|
98
|
+
= position.expires_on rescue nil
|
99
|
+
%td.end_n_days
|
100
|
+
-# = ( position.expires_on.to_date - position.begin_on.to_date ).to_i
|
101
|
+
%b= ( position.expires_on.to_date - Time.now.to_date ).to_i
|
102
|
+
%td= position.quantity
|
103
|
+
|
104
|
+
%td.begin_on.mini
|
105
|
+
= position.begin_on rescue nil
|
106
|
+
%td.begin_price
|
107
|
+
= pp_amount position.begin_price
|
108
|
+
%td.to_open
|
109
|
+
= pp_amount( position.begin_price * 100 * position.quantity )
|
110
|
+
%td= position.begin_delta
|
111
|
+
|
112
|
+
%td.end_price
|
113
|
+
= pp_amount position.end_price
|
114
|
+
%td.net_amount
|
115
|
+
= pp_amount ( position.begin_price - position.end_price ) * 100 * position.quantity rescue nil
|
116
|
+
%td.net_percent
|
117
|
+
= pp_percent( ( position.begin_price - position.end_price ) / position.begin_price ) rescue nil
|
118
|
+
%td.end_delta= position.end_delta
|
119
|
+
-# %td.end_n_days
|
120
|
+
-# %td.end_on
|
121
|
+
|
122
|
+
%td.should_rollp
|
123
|
+
= pp_percent position.should_rollp rescue nil
|
124
|
+
%td.next_position.mini
|
125
|
+
.a= position.next_symbol
|
126
|
+
.a= position.next_delta
|
127
|
+
.a
|
128
|
+
= pp_amount position.next_mark
|
129
|
+
\=>
|
130
|
+
= pp_amount position.next_outcome * 100 * position.quantity rescue nil
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
.purses-show.padded
|
3
|
+
|
4
|
+
.maxwidth
|
5
|
+
.header
|
6
|
+
%h5.title Purse `#{@purse.slug}` (#{@purse.positions.length})
|
7
|
+
%ul.menu-secondary
|
8
|
+
%li= link_to '[+position]', new_position_path({ purse_id: @purse.id })
|
9
|
+
|
10
|
+
= render '/iro/positions/table', positions: @purse.positions
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
.strategies--form
|
3
|
+
= form_for strategy do |f|
|
4
|
+
.actions
|
5
|
+
= f.submit
|
6
|
+
|
7
|
+
.field
|
8
|
+
%label Ticker
|
9
|
+
= f.select :ticker, options_for_select( @tickers_list, selected: strategy.ticker )
|
10
|
+
|
11
|
+
.field
|
12
|
+
%label slug
|
13
|
+
= f.text_field :slug
|
14
|
+
|
15
|
+
.field
|
16
|
+
= f.label :buffer_above_water
|
17
|
+
= f.number_field :buffer_above_water, placeholder: "0.49", step: 0.01
|
18
|
+
.field
|
19
|
+
= f.label :next_max_delta
|
20
|
+
= f.number_field :next_max_delta, placeholder: "0.25", step: 0.01
|
21
|
+
.field
|
22
|
+
= f.label :next_min_strike
|
23
|
+
= f.number_field :next_min_strike, placeholder: "20.0", step: 0.01
|
24
|
+
.field
|
25
|
+
= f.label :threshold_delta
|
26
|
+
= f.number_field :threshold_delta, placeholder: "0.14", step: 0.01
|
27
|
+
.field
|
28
|
+
= f.label :threshold_netp
|
29
|
+
= f.number_field :threshold_netp, placeholder: "0.69", step: 0.01
|
30
|
+
-# .field
|
31
|
+
-# = f.label :current_underlying_strike
|
32
|
+
-# = f.number_field :current_underlying_strike, placeholder: "16.66", step: 0.01
|
33
|
+
|
34
|
+
.actions
|
35
|
+
= f.submit
|
36
|
+
|
37
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
.strategies--show.modal-absolute.hide
|
3
|
+
.header
|
4
|
+
Strategy
|
5
|
+
= link_to '[~]', edit_strategy_path(strategy)
|
6
|
+
= strategy
|
7
|
+
%ul
|
8
|
+
%li <b>ticker:</b> #{strategy.ticker}
|
9
|
+
%li <b>slug:</b> #{strategy.slug}
|
10
|
+
|
11
|
+
%li <b>buffer_above_water:</b> #{strategy.buffer_above_water}
|
12
|
+
|
13
|
+
%li <b>next_max_delta:</b> #{strategy.next_max_delta}
|
14
|
+
%li <b>next_min_strike:</b> #{strategy.next_min_strike}
|
15
|
+
|
16
|
+
%li <b>threshold_delta:</b> #{strategy.threshold_delta}
|
17
|
+
%li <b>threshold_netp:</b> #{strategy.threshold_netp}
|
18
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
- strategies ||= @strategies
|
3
|
+
|
4
|
+
.strategies--table
|
5
|
+
|
6
|
+
%table.bordered
|
7
|
+
%tr
|
8
|
+
%th
|
9
|
+
%th ticker
|
10
|
+
%th slug
|
11
|
+
%th next_max_delta
|
12
|
+
- strategies.each do |sss|
|
13
|
+
%tr
|
14
|
+
%td= link_to '[~]', edit_strategy_path(sss)
|
15
|
+
%td= sss.ticker
|
16
|
+
%td= sss.slug
|
17
|
+
%td= sss.next_max_delta
|
18
|
+
|
data/config/routes.rb
CHANGED
@@ -8,7 +8,13 @@ Iro::Engine.routes.draw do
|
|
8
8
|
get 'datapoints', to: '/iro/datapoints#index'
|
9
9
|
|
10
10
|
resources :option_watches
|
11
|
+
|
12
|
+
post 'positions/:id/roll', to: 'positions#roll', as: :roll_position
|
13
|
+
resources :positions
|
11
14
|
resources :profiles
|
15
|
+
resources :purses
|
16
|
+
|
12
17
|
resources :stocks
|
18
|
+
resources :strategies
|
13
19
|
|
14
20
|
end
|
data/lib/iron_warbler.rb
CHANGED
data/lib/tasks/iro_tasks.rake
CHANGED
@@ -1,6 +1,41 @@
|
|
1
1
|
|
2
2
|
namespace :iro do
|
3
3
|
|
4
|
+
desc 'recommend position actions'
|
5
|
+
task recommend_position_actions: :environment do
|
6
|
+
Iro::Position.active.where({ kind: 'covered_call' }).map &:should_roll?
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'refresh positions'
|
10
|
+
task refresh_positions: :environment do
|
11
|
+
Iro::Position.active.where({ kind: 'covered_call' }).map &:refresh
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'watch positions'
|
15
|
+
task watch_positions: :environment do
|
16
|
+
while true
|
17
|
+
if in_business
|
18
|
+
|
19
|
+
positions = Iro::Position.active.where({ kind: 'covered_call' })
|
20
|
+
positions.each do |position|
|
21
|
+
out = Tda::Option.get_quote({
|
22
|
+
contractType: 'CALL',
|
23
|
+
strike: position.strike,
|
24
|
+
expirationDate: position.expires_on,
|
25
|
+
ticker: position.ticker,
|
26
|
+
})
|
27
|
+
position.update({
|
28
|
+
end_delta: out[:delta],
|
29
|
+
end_price: out[:last],
|
30
|
+
})
|
31
|
+
end
|
32
|
+
|
33
|
+
print '.'
|
34
|
+
end
|
35
|
+
sleep 60 # seconds
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
4
39
|
desc 'watch stocks'
|
5
40
|
task watch_stocks: :environment do
|
6
41
|
while true
|