iron_warbler 2.0.7.8 → 2.0.7.10
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/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
|