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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7b7685e4696ff34f93ea5bd1a5f3931e1aa0718cb893de9ccb589234fd539e4
|
4
|
+
data.tar.gz: c11b8fb78c052fee8584ce20e3c9673d74778945b4ba5d469e30f10e7521d7b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a54e555e30a9197f6414c7989b652699a7d5bd087294889a6cc1fd5cf942818c8fb0ca3ab1eb50fa15f83be11f5e310caef40187c7e9097fe77721d3eb3e0fd0
|
7
|
+
data.tar.gz: 1bd3bcef86e4980e6f45c96d3096a9bcbe7975425bac15b16129acaf2f5f4ccf0ccb1c3fab60e6e71b67bf4ab589c4999eb09871cd7190680c25fed1410a32c2
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
.positions--table {
|
3
|
+
|
4
|
+
table {
|
5
|
+
td {
|
6
|
+
text-align: center;
|
7
|
+
}
|
8
|
+
td.actions,
|
9
|
+
td.next_position,
|
10
|
+
td.strategy,
|
11
|
+
._ {
|
12
|
+
text-align: left;
|
13
|
+
}
|
14
|
+
th {
|
15
|
+
border: 1px solid red;
|
16
|
+
|
17
|
+
> * {
|
18
|
+
border: 1px solid yellow;
|
19
|
+
// background: #ddd;
|
20
|
+
|
21
|
+
transform-origin: 0 0;
|
22
|
+
rotate: -40deg;
|
23
|
+
|
24
|
+
padding: .4em 0;
|
25
|
+
width: 120px;
|
26
|
+
|
27
|
+
}
|
28
|
+
|
29
|
+
}
|
30
|
+
|
31
|
+
th.begin_price {
|
32
|
+
> div {
|
33
|
+
background: cyan;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
th.end_delta {
|
38
|
+
width: 50px;
|
39
|
+
}
|
40
|
+
th.next_position {
|
41
|
+
width: 160px;
|
42
|
+
}
|
43
|
+
th.next_symbol {
|
44
|
+
width: 120px;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
/* F */
|
3
|
+
|
4
|
+
.flex-row {
|
5
|
+
display: flex;
|
6
|
+
margin-bottom: 0.4em;
|
7
|
+
}
|
8
|
+
.field {
|
9
|
+
min-width: 100px;
|
10
|
+
margin-right: 1em;
|
11
|
+
}
|
12
|
+
|
13
|
+
/* M */
|
14
|
+
|
15
|
+
.modal-absolute {
|
16
|
+
border: 1px solid green;
|
17
|
+
border-radius: 5px;
|
18
|
+
box-shadow: 2px 2px;
|
19
|
+
|
20
|
+
padding: 0.5em;
|
21
|
+
|
22
|
+
position: absolute;
|
23
|
+
|
24
|
+
min-width: 200px;
|
25
|
+
width: 400px;
|
26
|
+
|
27
|
+
min-height: 200px;
|
28
|
+
height: 300px;
|
29
|
+
/* overflow: auto; */
|
30
|
+
|
31
|
+
background: white;
|
32
|
+
|
33
|
+
z-index: 2;
|
34
|
+
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
/* W */
|
40
|
+
|
41
|
+
.w-min-50px {
|
42
|
+
min-width: 50px;
|
43
|
+
}
|
44
|
+
|
@@ -4,7 +4,8 @@ class Iro::AlertsController < Iro::ApplicationController
|
|
4
4
|
before_action :set_lists
|
5
5
|
|
6
6
|
def create
|
7
|
-
@alert = Iro::Alert.new
|
7
|
+
@alert = Iro::Alert.new params[:alert].permit!
|
8
|
+
authorize! :create, @alert
|
8
9
|
if @alert.save
|
9
10
|
redirect_to action: :index, notice: 'Alert was successfully created.'
|
10
11
|
else
|
@@ -14,17 +15,20 @@ class Iro::AlertsController < Iro::ApplicationController
|
|
14
15
|
|
15
16
|
def destroy
|
16
17
|
@alert = Iro::Alert.find(params[:id])
|
18
|
+
authorize! :destroy, @alert
|
17
19
|
@alert.destroy
|
18
20
|
redirect_to action: :index, notice: 'Alert was successfully destroyed.'
|
19
21
|
end
|
20
22
|
|
21
23
|
def index
|
22
24
|
@alerts = Iro::Alert.all
|
25
|
+
authorize! :index, Iro::Alert
|
23
26
|
end
|
24
27
|
|
25
28
|
def update
|
26
29
|
@alert = Iro::Alert.find(params[:id])
|
27
|
-
|
30
|
+
authorize! :update, @alert
|
31
|
+
if @alert.update params[:alert].permit!
|
28
32
|
redirect_to action: :index, notice: 'Alert was successfully updated.'
|
29
33
|
else
|
30
34
|
render :edit
|
@@ -36,10 +40,6 @@ class Iro::AlertsController < Iro::ApplicationController
|
|
36
40
|
##
|
37
41
|
private
|
38
42
|
|
39
|
-
def alert_params
|
40
|
-
params.require(:alert).permit(:class_name, :kind, :symbol, :direction, :strike, :profile_id)
|
41
|
-
end
|
42
|
-
|
43
43
|
def set_lists
|
44
44
|
# @profiles_list = Wco::Profile.list
|
45
45
|
@stocks_list = [[nil,nil]] + Iro::Stock.active.map { |s| [ s.ticker, s.ticker ] }
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
class Iro::PositionsController < Iro::ApplicationController
|
3
|
+
|
4
|
+
before_action :set_lists
|
5
|
+
|
6
|
+
def new
|
7
|
+
@position = Iro::Position.new purse_id: params[:purse_id]
|
8
|
+
authorize! :new, @posision
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
@position = Iro::Position.new params[:position].permit!
|
13
|
+
authorize! :create, @position
|
14
|
+
|
15
|
+
if @position.save
|
16
|
+
flash_notice @position
|
17
|
+
redirect_to controller: :purses, action: :show, id: @position.purse_id.to_s
|
18
|
+
else
|
19
|
+
flash_alert @position
|
20
|
+
redirect_to request.referrer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy
|
25
|
+
@position = Iro::Position.find params[:id]
|
26
|
+
authorize! :destroy, @position
|
27
|
+
@position.delete
|
28
|
+
flash_notice "Probably ok"
|
29
|
+
redirect_to request.referrer
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit
|
33
|
+
@position = Iro::Position.find params[:id]
|
34
|
+
authorize! :edit, @position
|
35
|
+
|
36
|
+
@strategies_list = Iro::Strategy.for_ticker( @position.ticker ).list
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
@position = Iro::Position.find params[:id]
|
41
|
+
authorize! :update, @position
|
42
|
+
|
43
|
+
if @position.update params[:position].permit!
|
44
|
+
flash_notice @position
|
45
|
+
redirect_to controller: :purses, action: :show, id: @position.purse_id.to_s
|
46
|
+
else
|
47
|
+
flash_alert @position
|
48
|
+
redirect_to request.referrer
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
## private
|
54
|
+
##
|
55
|
+
private
|
56
|
+
|
57
|
+
def set_lists
|
58
|
+
@strategies_list = Iro::Strategy.list
|
59
|
+
@tickers_list = Iro::Stock.tickers_list
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
class Iro::PursesController < Iro::ApplicationController
|
3
|
+
|
4
|
+
before_action :set_lists
|
5
|
+
|
6
|
+
def create
|
7
|
+
@purse = Iro::Purse.new params[:purse].permit!
|
8
|
+
authorize! :create, @purse
|
9
|
+
if @purse.save
|
10
|
+
redirect_to action: :index, notice: 'Purse was successfully created.'
|
11
|
+
else
|
12
|
+
render :new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy
|
17
|
+
@purse = Iro::Purse.find(params[:id])
|
18
|
+
authorize! :destroy, @purse
|
19
|
+
@purse.destroy
|
20
|
+
redirect_to action: :index, notice: 'Purse was successfully destroyed.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def edit
|
24
|
+
@purse = Iro::Purse.find(params[:id])
|
25
|
+
authorize! :edit, @purse
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def index
|
31
|
+
@purses = Iro::Purse.all
|
32
|
+
authorize! :index, Iro::Purse
|
33
|
+
end
|
34
|
+
|
35
|
+
def show
|
36
|
+
@purse = Iro::Purse.find(params[:id])
|
37
|
+
authorize! :show, @purse
|
38
|
+
|
39
|
+
@positions = Iro::Position.all
|
40
|
+
end
|
41
|
+
|
42
|
+
def update
|
43
|
+
@purse = Iro::Purse.find(params[:id])
|
44
|
+
authorize! :update, @purse
|
45
|
+
if @purse.update params[:purse].permit!
|
46
|
+
redirect_to action: :index, notice: 'Purse was successfully updated.'
|
47
|
+
else
|
48
|
+
render :edit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
## private
|
54
|
+
##
|
55
|
+
private
|
56
|
+
|
57
|
+
def set_lists
|
58
|
+
# @profiles_list = Wco::Profile.list
|
59
|
+
@stocks_list = [[nil,nil]] + Iro::Stock.active.map { |s| [ s.ticker, s.ticker ] }
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
|
@@ -12,6 +12,7 @@ class Iro::StocksController < Iro::ApplicationController
|
|
12
12
|
|
13
13
|
def new
|
14
14
|
@stock = Iro::Stock.new
|
15
|
+
authorize! :new, @stock
|
15
16
|
end
|
16
17
|
|
17
18
|
def edit
|
@@ -19,20 +20,25 @@ class Iro::StocksController < Iro::ApplicationController
|
|
19
20
|
|
20
21
|
def create
|
21
22
|
@stock = Iro::Stock.new(stock_params)
|
23
|
+
authorize! :create, @stock
|
22
24
|
|
23
25
|
if @stock.save
|
24
|
-
|
26
|
+
flash_notice @stock
|
25
27
|
else
|
26
|
-
|
28
|
+
flash_alert @stock
|
27
29
|
end
|
30
|
+
redirect_to action: :index
|
28
31
|
end
|
29
32
|
|
30
33
|
def update
|
34
|
+
@stock = Iro::Stock.find params[:id]
|
35
|
+
authorize! :update, @stock
|
31
36
|
if @stock.update(stock_params)
|
32
|
-
|
37
|
+
flash_notice @stock
|
33
38
|
else
|
34
|
-
|
39
|
+
flash_alert @stock
|
35
40
|
end
|
41
|
+
redirect_to request.referrer
|
36
42
|
end
|
37
43
|
|
38
44
|
def destroy
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
class Iro::StrategiesController < Iro::ApplicationController
|
3
|
+
|
4
|
+
before_action :set_lists
|
5
|
+
|
6
|
+
def new
|
7
|
+
@strategy = Iro::Strategy.new
|
8
|
+
authorize! :new, @posision
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
@strategy = Iro::Strategy.new params[:strategy].permit!
|
13
|
+
authorize! :create, @strategy
|
14
|
+
|
15
|
+
if @strategy.save
|
16
|
+
flash_notice @strategy
|
17
|
+
else
|
18
|
+
flash_alert @strategy
|
19
|
+
end
|
20
|
+
|
21
|
+
redirect_to action: :index
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy
|
25
|
+
@strategy = Iro::Strategy.find params[:id]
|
26
|
+
authorize! :destroy, @strategy
|
27
|
+
@strategy.delete
|
28
|
+
flash_notice "Probably ok"
|
29
|
+
redirect_to request.referrer
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit
|
33
|
+
@strategy = Iro::Strategy.find params[:id]
|
34
|
+
authorize! :edit, @strategy
|
35
|
+
end
|
36
|
+
|
37
|
+
def index
|
38
|
+
authorize! :index, Iro::Strategy
|
39
|
+
@strategies = Iro::Strategy.all
|
40
|
+
render '_table'
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
@strategy = Iro::Strategy.find params[:id]
|
45
|
+
authorize! :update, @strategy
|
46
|
+
|
47
|
+
if @strategy.update params[:strategy].permit!
|
48
|
+
flash_notice @strategy
|
49
|
+
else
|
50
|
+
flash_alert @strategy
|
51
|
+
end
|
52
|
+
|
53
|
+
redirect_to action: :index
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
## private
|
58
|
+
##
|
59
|
+
private
|
60
|
+
|
61
|
+
def set_lists
|
62
|
+
@strategies_list = Iro::Strategy.list
|
63
|
+
@tickers_list = Iro::Stock.tickers_list
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/app/models/iro/option.rb
CHANGED
@@ -5,13 +5,6 @@ class Iro::Option
|
|
5
5
|
include Mongoid::Timestamps
|
6
6
|
store_in collection: 'iro_options'
|
7
7
|
|
8
|
-
# STATUS_ACTIVE = 'active'
|
9
|
-
# STATUS_INACTIVE = 'inactive'
|
10
|
-
# STATUSES = [ 'active', 'inactive' ]
|
11
|
-
# def self.active
|
12
|
-
# where( status: STATUS_ACTIVE )
|
13
|
-
# end
|
14
|
-
|
15
8
|
# field :ticker
|
16
9
|
# validates :ticker, uniqueness: true, presence: true
|
17
10
|
|
@@ -0,0 +1,222 @@
|
|
1
|
+
|
2
|
+
class Iro::Position
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps
|
5
|
+
store_in collection: 'iro_positions'
|
6
|
+
|
7
|
+
STATUS_ACTIVE = 'active'
|
8
|
+
STATUS_PROPOSED = 'proposed'
|
9
|
+
STATUSES = [ nil, 'active', 'inactive', 'proposed' ]
|
10
|
+
field :status
|
11
|
+
validates :status, presence: true
|
12
|
+
scope :active, ->{ where( status: 'active' ) }
|
13
|
+
|
14
|
+
belongs_to :purse, class_name: 'Iro::Purse', inverse_of: :positions
|
15
|
+
belongs_to :strategy, class_name: 'Iro::Strategy', inverse_of: :positions
|
16
|
+
|
17
|
+
field :ticker
|
18
|
+
validates :ticker, presence: true
|
19
|
+
index({ purse_id: 1, ticker: 1 })
|
20
|
+
|
21
|
+
KINDS = [ nil, 'covered_call', 'credit_put_spread', 'credit_call_spread' ]
|
22
|
+
field :kind
|
23
|
+
|
24
|
+
field :strike, type: :float
|
25
|
+
validates :strike, presence: true
|
26
|
+
|
27
|
+
field :expires_on
|
28
|
+
validates :expires_on, presence: true
|
29
|
+
|
30
|
+
field :quantity, type: :integer
|
31
|
+
validates :quantity, presence: true
|
32
|
+
|
33
|
+
field :begin_on
|
34
|
+
field :begin_price, type: :float
|
35
|
+
field :begin_delta, type: :float
|
36
|
+
|
37
|
+
field :end_on
|
38
|
+
field :end_price, type: :float
|
39
|
+
field :end_delta, type: :float
|
40
|
+
|
41
|
+
field :net_amount
|
42
|
+
field :net_percent
|
43
|
+
|
44
|
+
def current_underlying_strike
|
45
|
+
Iro::Stock.find_by( ticker: ticker ).last
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh
|
49
|
+
out = Tda::Option.get_quote({
|
50
|
+
contractType: 'CALL',
|
51
|
+
strike: strike,
|
52
|
+
expirationDate: expires_on,
|
53
|
+
ticker: ticker,
|
54
|
+
})
|
55
|
+
update({
|
56
|
+
end_delta: out[:delta],
|
57
|
+
end_price: out[:last],
|
58
|
+
})
|
59
|
+
print '_'
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
field :next_delta, type: :float
|
64
|
+
field :next_outcome, type: :float
|
65
|
+
field :next_symbol
|
66
|
+
field :next_mark
|
67
|
+
field :next_reasons, type: :array, default: []
|
68
|
+
field :should_rollp, type: :float
|
69
|
+
|
70
|
+
def should_roll?
|
71
|
+
puts! 'shold_roll?'
|
72
|
+
|
73
|
+
update({
|
74
|
+
next_reasons: [],
|
75
|
+
next_symbol: nil,
|
76
|
+
next_delta: nil,
|
77
|
+
})
|
78
|
+
|
79
|
+
if must_roll?
|
80
|
+
out = 1.0
|
81
|
+
elsif can_roll?
|
82
|
+
|
83
|
+
if end_delta < strategy.threshold_delta
|
84
|
+
next_reasons.push "delta is lower than threshold"
|
85
|
+
out = 0.91
|
86
|
+
elsif 1 - end_price/begin_price > strategy.threshold_netp
|
87
|
+
next_reasons.push "made enough percent profit (dubious)"
|
88
|
+
out = 0.61
|
89
|
+
else
|
90
|
+
next_reasons.push "neutral"
|
91
|
+
out = 0.33
|
92
|
+
end
|
93
|
+
|
94
|
+
else
|
95
|
+
out = 0.0
|
96
|
+
end
|
97
|
+
|
98
|
+
update({
|
99
|
+
next_delta: next_position[:delta],
|
100
|
+
next_outcome: next_position[:mark] - end_price,
|
101
|
+
next_symbol: next_position[:symbol],
|
102
|
+
next_mark: next_position[:mark],
|
103
|
+
should_rollp: out,
|
104
|
+
# status: Iro::Position::STATE_PROPOSED,
|
105
|
+
})
|
106
|
+
|
107
|
+
puts! next_reasons, 'next_reasons'
|
108
|
+
puts! out, 'out'
|
109
|
+
return out > 0.5
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
## expires_on = cc.expires_on ; nil
|
114
|
+
def can_roll?
|
115
|
+
## only if less than 7 days left
|
116
|
+
( expires_on.to_date - Time.now.to_date ).to_i < 7
|
117
|
+
end
|
118
|
+
|
119
|
+
## If I'm near below water
|
120
|
+
##
|
121
|
+
## expires_on = cc.expires_on ; strategy = cc.strategy ; strike = cc.strike ; nil
|
122
|
+
def must_roll?
|
123
|
+
if ( current_underlying_strike + strategy.buffer_above_water ) > strike
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
## @TODO: This one should not happen, I should log appropriately. _vp_ 2023-03-19
|
127
|
+
if ( expires_on.to_date - Time.now.to_date ).to_i < 1
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
## strike = cc.strike ; strategy = cc.strategy ; nil
|
133
|
+
def near_below_water?
|
134
|
+
strike < current_underlying_strike + strategy.buffer_above_water
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
## 2023-03-18 _vp_ Continue.
|
140
|
+
## 2023-03-19 _vp_ Continue.
|
141
|
+
## 2023-08-05 _vp_ an Important method
|
142
|
+
##
|
143
|
+
## expires_on = cc.expires_on ; strategy = cc.strategy ; ticker = cc.ticker ; end_price = cc.end_price ; next_expires_on = cc.next_expires_on ; nil
|
144
|
+
##
|
145
|
+
## out.map { |p| [ p[:strikePrice], p[:delta] ] }
|
146
|
+
##
|
147
|
+
def next_position
|
148
|
+
return @next_position if @next_position
|
149
|
+
return {} if ![ STATUS_ACTIVE, STATUS_PROPOSED ].include?( status )
|
150
|
+
|
151
|
+
## 7 days ahead - not configurable so far
|
152
|
+
out = Tda::Option.get_quotes({
|
153
|
+
ticker: ticker,
|
154
|
+
expirationDate: next_expires_on,
|
155
|
+
contractType: 'CALL',
|
156
|
+
})
|
157
|
+
|
158
|
+
## above_water
|
159
|
+
if strategy.buffer_above_water.present?
|
160
|
+
out = out.select do |i|
|
161
|
+
i[:strikePrice] > current_underlying_strike + strategy.buffer_above_water
|
162
|
+
end
|
163
|
+
# next_reasons.push "buffer_above_water above #{current_underlying_strike + strategy.buffer_above_water}"
|
164
|
+
end
|
165
|
+
|
166
|
+
if near_below_water?
|
167
|
+
msg = "Panic! climb at a loss. Skip the rest of the calculation."
|
168
|
+
next_reasons.push msg
|
169
|
+
## @TODO: if not enough money in the purse, cannot roll? 2023-03-19
|
170
|
+
|
171
|
+
# byebug
|
172
|
+
|
173
|
+
## Take a small loss here.
|
174
|
+
prev = nil
|
175
|
+
out.each_with_index do |i, idx|
|
176
|
+
next if idx == 0
|
177
|
+
if i[:last] < end_price
|
178
|
+
prev ||= i
|
179
|
+
end
|
180
|
+
end
|
181
|
+
out = [ prev ]
|
182
|
+
|
183
|
+
else
|
184
|
+
## Normal flow, making money.
|
185
|
+
## @TODO: test! _vp_ 2023-03-19
|
186
|
+
|
187
|
+
## next_min_strike
|
188
|
+
if strategy.next_min_strike.present?
|
189
|
+
out = out.select do |i|
|
190
|
+
i[:strikePrice] >= strategy.next_min_strike
|
191
|
+
end
|
192
|
+
# next_reasons.push "next_min_strike above #{strategy.next_min_strike}"
|
193
|
+
end
|
194
|
+
# json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_min_strike'
|
195
|
+
|
196
|
+
## max_delta
|
197
|
+
if strategy.next_max_delta.present?
|
198
|
+
out = out.select do |i|
|
199
|
+
i[:delta] = 0.0 if i[:delta] == "NaN"
|
200
|
+
i[:delta] <= strategy.next_max_delta
|
201
|
+
end
|
202
|
+
# next_reasons.push "next_max_delta below #{strategy.next_max_delta}"
|
203
|
+
end
|
204
|
+
# json_puts! out.map { |p| [p[:delta], p[:symbol]] }, 'next_max_delta'
|
205
|
+
end
|
206
|
+
|
207
|
+
@next_position = out[0] || {}
|
208
|
+
end
|
209
|
+
|
210
|
+
## @TODO: Test this. _vp_ 2023-04-01
|
211
|
+
def next_expires_on
|
212
|
+
out = expires_on.to_time + 7.days
|
213
|
+
while !out.friday?
|
214
|
+
out = out + 1.day
|
215
|
+
end
|
216
|
+
while !out.workday?
|
217
|
+
out = out - 1.day
|
218
|
+
end
|
219
|
+
return out
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
class Iro::Purse
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps
|
5
|
+
store_in collection: 'iro_purses'
|
6
|
+
|
7
|
+
field :slug
|
8
|
+
validates :slug, presence: true, uniqueness: true
|
9
|
+
index({ slug: -1 }, { unique: true })
|
10
|
+
|
11
|
+
has_many :positions, class_name: 'Iro::Position', inverse_of: :purse
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
slug
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/app/models/iro/stock.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
|
2
|
-
|
3
2
|
class Iro::Stock
|
4
3
|
include Mongoid::Document
|
5
4
|
include Mongoid::Timestamps
|
@@ -7,13 +6,24 @@ class Iro::Stock
|
|
7
6
|
|
8
7
|
STATUS_ACTIVE = 'active'
|
9
8
|
STATUS_INACTIVE = 'inactive'
|
10
|
-
STATUSES = [ 'active', 'inactive' ]
|
9
|
+
STATUSES = [ nil, 'active', 'inactive' ]
|
11
10
|
def self.active
|
12
11
|
where( status: STATUS_ACTIVE )
|
13
12
|
end
|
13
|
+
field :status
|
14
14
|
|
15
15
|
field :ticker
|
16
16
|
validates :ticker, uniqueness: true, presence: true
|
17
17
|
|
18
|
+
field :last, type: :float
|
19
|
+
|
20
|
+
def self.list
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.tickers_list
|
24
|
+
[nil] + all.map( &:ticker )
|
25
|
+
end
|
26
|
+
|
27
|
+
# has_many :strategies, class_name: 'Iro::Strategy', inverse_of: :stock
|
18
28
|
|
19
29
|
end
|