iron_warbler 2.0.7.24 → 2.0.7.26
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/Card.scss +6 -0
- data/app/assets/stylesheets/iron_warbler/positions.scss +1 -1
- data/app/assets/stylesheets/iron_warbler/positions_gameui.scss +8 -0
- data/app/assets/stylesheets/iron_warbler/purses_gameui.scss +2 -4
- data/app/assets/stylesheets/iron_warbler/purses_summary.scss +31 -19
- data/app/controllers/iro/application_controller.rb +6 -0
- data/app/controllers/iro/positions_controller.rb +186 -137
- data/app/controllers/iro/purses_controller.rb +3 -1
- data/app/controllers/iro/stocks_controller.rb +2 -2
- data/app/models/iro/option.rb +41 -148
- data/app/models/iro/option_black_scholes.rb +149 -0
- data/app/models/iro/position.rb +156 -205
- data/app/models/iro/purse.rb +34 -4
- data/app/models/iro/strategy.rb +49 -47
- data/app/views/iro/_main_header.haml +4 -2
- data/app/views/iro/options/_show_mini.haml +8 -0
- data/app/views/iro/positions/_form.haml +8 -3
- data/app/views/iro/positions/_formpart_4data.haml +41 -38
- data/app/views/iro/positions/_gameui_covered_call.haml +1 -1
- data/app/views/iro/positions/_gameui_long_debit_call_spread.haml +5 -5
- data/app/views/iro/positions/_gameui_long_debit_call_spread.haml-trash +42 -0
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +1 -0
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml-trash +40 -0
- data/app/views/iro/positions/_header.haml +2 -0
- data/app/views/iro/positions/_header_long_debit_call_spread.haml +43 -25
- data/app/views/iro/positions/_prepare_long_debit_call_spread.haml +6 -5
- data/app/views/iro/positions/_prepare_short_debit_put_spread.haml +2 -1
- data/app/views/iro/positions/_table.haml +25 -26
- data/app/views/iro/positions/prepare.haml +6 -4
- data/app/views/iro/positions/prepare2.haml +22 -0
- data/app/views/iro/purses/_form_extra_fields.haml +8 -4
- data/app/views/iro/purses/_header.haml +19 -5
- data/app/views/iro/purses/_summary.haml +69 -62
- data/app/views/iro/purses/show.haml +1 -1
- data/app/views/iro/strategies/_form.haml +26 -19
- data/app/views/iro/strategies/_show.haml +6 -4
- data/config/routes.rb +10 -7
- metadata +8 -2
- data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +0 -40
data/app/models/iro/option.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
require 'distribution'
|
2
|
-
N = Distribution::Normal
|
3
|
-
# include Math
|
4
|
-
# require 'business_time'
|
5
1
|
|
6
2
|
class Iro::Option
|
7
3
|
include Mongoid::Document
|
8
4
|
include Mongoid::Timestamps
|
9
5
|
include Mongoid::Paranoia
|
6
|
+
include Iro::OptionBlackScholes
|
10
7
|
store_in collection: 'iro_options'
|
11
8
|
|
12
9
|
attr_accessor :recompute
|
13
10
|
|
14
|
-
|
15
|
-
|
11
|
+
belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
|
12
|
+
def ticker; stock.ticker; end
|
13
|
+
# field :ticker
|
14
|
+
# validates :ticker, presence: true
|
16
15
|
|
17
16
|
field :symbol
|
18
|
-
|
17
|
+
## each option can be a leg in a position, no uniqueness
|
18
|
+
# validates :symbol, uniqueness: true, presence: true
|
19
19
|
|
20
|
-
field :put_call, type: :string
|
20
|
+
field :put_call, type: :string # 'PUT' or 'CALL'
|
21
21
|
validates :put_call, presence: true
|
22
22
|
|
23
23
|
field :delta, type: :float
|
@@ -27,9 +27,29 @@ class Iro::Option
|
|
27
27
|
|
28
28
|
field :expires_on, type: :date
|
29
29
|
validates :expires_on, presence: true
|
30
|
+
def self.expirations_list full: false
|
31
|
+
[
|
32
|
+
[ nil, nil ],
|
33
|
+
[ 'Mar 22', '2024-03-22'.to_date ],
|
34
|
+
[ 'Mar 28', '2024-03-28'.to_date ],
|
35
|
+
[ 'Apr 5', '2024-04-05'.to_date ],
|
36
|
+
[ 'Mar 12', '2024-03-12'.to_date ],
|
37
|
+
[ 'Mar 19', '2024-03-19'.to_date ],
|
38
|
+
]
|
39
|
+
end
|
30
40
|
|
31
|
-
|
41
|
+
field :begin_price, type: :float
|
42
|
+
field :begin_delta, type: :float
|
43
|
+
field :end_price, type: :float
|
44
|
+
field :end_delta, type: :float
|
45
|
+
|
46
|
+
|
47
|
+
has_one :outer, class_name: 'Iro::Position', inverse_of: :outer
|
48
|
+
has_one :inner, class_name: 'Iro::Position', inverse_of: :inner
|
49
|
+
|
50
|
+
field :last, type: :float
|
32
51
|
|
52
|
+
## for TDA
|
33
53
|
def symbol
|
34
54
|
if !self[:symbol]
|
35
55
|
p_c_ = put_call == 'PUT' ? 'P' : 'C'
|
@@ -41,146 +61,19 @@ class Iro::Option
|
|
41
61
|
self[:symbol]
|
42
62
|
end
|
43
63
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
##
|
57
|
-
##
|
58
|
-
##
|
59
|
-
From: https://www.investopedia.com/articles/optioninvestor/07/options_beat_market.asp
|
60
|
-
|
61
|
-
K :: strike price
|
62
|
-
S_t :: last
|
63
|
-
r :: risk-free rate
|
64
|
-
t :: time to maturity
|
65
|
-
|
66
|
-
C = S_t N( d1 ) - K e^-rt N( d2 )
|
67
|
-
|
68
|
-
d1 = ln( St / K ) + (r + theta**2 / 2 )t
|
69
|
-
/{ theta_s * sqrt( t ) }
|
70
|
-
|
71
|
-
d2 = d1 - theta_s sqrt( t )
|
72
|
-
|
73
|
-
##
|
74
|
-
## From: https://en.wikipedia.org/wiki/Black%E2%80%93Scholes_model
|
75
|
-
##
|
76
|
-
|
77
|
-
D :: e^(rt) # discount factor
|
78
|
-
F :: e^(rt) S # forward price of underlying
|
79
|
-
|
80
|
-
C(F,t) = D[ N(d1)F - N(d2)K ]
|
81
|
-
|
82
|
-
d1 = ln(F/K) + stdev**2 t / 2
|
83
|
-
/{ stdev sqrt(t) }
|
84
|
-
d2 = d1 - stdev sqrt(t)
|
85
|
-
|
86
|
-
##
|
87
|
-
## From: https://www.daytrading.com/options-pricing-models
|
88
|
-
##
|
89
|
-
C0 = S0N(d1) – Xe-rtN(d2)
|
90
|
-
|
91
|
-
C0 = current call premium
|
92
|
-
S0 = current stock price
|
93
|
-
N(d1) = the probability that a value in a normal distribution will be less than d
|
94
|
-
N(d2) = the probability that the option will be in the money by expiration
|
95
|
-
X = strike price of the option
|
96
|
-
T = time until expiration (expressed in years)
|
97
|
-
r = risk-free interest rate
|
98
|
-
e = 2.71828, the base of the natural logarithm
|
99
|
-
ln = natural logarithm function
|
100
|
-
σ = standard deviation of the stock’s annualized rate of return (compounded continuously)
|
101
|
-
d1 = ln(S0/X) + (r + σ2/2)Tσ√T
|
102
|
-
|
103
|
-
d2 = d1 – σ√T
|
104
|
-
|
105
|
-
Note that:
|
106
|
-
|
107
|
-
Xe-rt = X/ert = the present value of the strike price using a continuously compounded interest rate
|
108
|
-
|
109
|
-
##
|
110
|
-
## From: https://www.wallstreetmojo.com/black-scholes-model/
|
111
|
-
##
|
112
|
-
|
113
|
-
|
114
|
-
## init
|
115
|
-
require 'distribution'
|
116
|
-
N = Distribution::Normal
|
117
|
-
stock = Iro::Stock.find_by ticker: 'NVDA'
|
118
|
-
strike = 910.0
|
119
|
-
r = Iro::Option.rate_daily
|
120
|
-
stdev = 91.0
|
121
|
-
t = 7.0
|
122
|
-
expires_on = '2024-03-22'
|
123
|
-
|
124
|
-
=end
|
125
|
-
def d1
|
126
|
-
last = stock.last
|
127
|
-
r = self.class.rate_annual
|
128
|
-
|
129
|
-
out = Math.log( last / strike ) + ( r + stdev**2 / 2 ) * t
|
130
|
-
out = out /( stdev * Math.sqrt(t) )
|
131
|
-
return out
|
132
|
-
end
|
133
|
-
def d2
|
134
|
-
last = stock.last
|
135
|
-
r = self.class.rate_annual
|
136
|
-
|
137
|
-
out = d1 - stdev * Math.sqrt( t )
|
138
|
-
return out
|
139
|
-
end
|
140
|
-
def t
|
141
|
-
# t = 1.0 / 365 * Date.today.business_days_until( expires_on )
|
142
|
-
t = 1.0 / 365 * (expires_on - Date.today).to_i
|
143
|
-
end
|
144
|
-
def stdev
|
145
|
-
recompute = nil
|
146
|
-
stock.stdev( recompute: recompute )
|
147
|
-
end
|
148
|
-
def call_price
|
149
|
-
last = stock.last
|
150
|
-
r = self.class.rate_annual
|
151
|
-
|
152
|
-
out = N.cdf( d1 ) * last - N.cdf( d2 ) * strike * Math::E**( -1 * r * t )
|
153
|
-
return out
|
64
|
+
def sync
|
65
|
+
out = Tda::Option.get_quote({
|
66
|
+
contractType: put_call,
|
67
|
+
strike: strike,
|
68
|
+
expirationDate: expires_on,
|
69
|
+
ticker: ticker,
|
70
|
+
})
|
71
|
+
puts! out, 'option sync'
|
72
|
+
self.end_price = ( out.bid + out.ask ) / 2 rescue 0
|
73
|
+
self.end_delta = out.delta
|
74
|
+
# self.save
|
154
75
|
end
|
155
76
|
|
156
|
-
|
157
|
-
last = stock.last
|
158
|
-
r = self.class.rate_annual
|
159
|
-
|
160
|
-
out = N.cdf(-d2) * strike * exp(-r*t) - N.cdf(-d1) * last
|
161
|
-
return out
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
def self.rate_annual
|
166
|
-
0.05
|
167
|
-
end
|
168
|
-
|
169
|
-
=begin
|
170
|
-
# test
|
171
|
-
|
172
|
-
inn = 100
|
173
|
-
n.times { inn = inn*(1.0+out) }
|
174
|
-
inn
|
175
|
-
|
176
|
-
=end
|
177
|
-
def self.rate_daily
|
178
|
-
n = 250.0 # days
|
179
|
-
# n = 12 # months
|
180
|
-
|
181
|
-
out = (1.0+self.rate_annual)**(1.0/n) - 1.0
|
182
|
-
puts! out, 'rate_daily'
|
183
|
-
return out
|
184
|
-
end
|
77
|
+
before_save :sync
|
185
78
|
|
186
79
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
|
2
|
+
require 'distribution'
|
3
|
+
N = Distribution::Normal
|
4
|
+
|
5
|
+
module Iro::OptionBlackScholes
|
6
|
+
|
7
|
+
##
|
8
|
+
## black-scholes pricing
|
9
|
+
##
|
10
|
+
|
11
|
+
=begin
|
12
|
+
##
|
13
|
+
##
|
14
|
+
##
|
15
|
+
annual to daily:
|
16
|
+
|
17
|
+
AR = ((DR + 1)^365 – 1) x 100
|
18
|
+
|
19
|
+
##
|
20
|
+
##
|
21
|
+
##
|
22
|
+
From: https://www.investopedia.com/articles/optioninvestor/07/options_beat_market.asp
|
23
|
+
|
24
|
+
K :: strike price
|
25
|
+
S_t :: last
|
26
|
+
r :: risk-free rate
|
27
|
+
t :: time to maturity
|
28
|
+
|
29
|
+
C = S_t N( d1 ) - K e^-rt N( d2 )
|
30
|
+
|
31
|
+
d1 = ln( St / K ) + (r + theta**2 / 2 )t
|
32
|
+
/{ theta_s * sqrt( t ) }
|
33
|
+
|
34
|
+
d2 = d1 - theta_s sqrt( t )
|
35
|
+
|
36
|
+
##
|
37
|
+
## From: https://en.wikipedia.org/wiki/Black%E2%80%93Scholes_model
|
38
|
+
##
|
39
|
+
|
40
|
+
D :: e^(rt) # discount factor
|
41
|
+
F :: e^(rt) S # forward price of underlying
|
42
|
+
|
43
|
+
C(F,t) = D[ N(d1)F - N(d2)K ]
|
44
|
+
|
45
|
+
d1 = ln(F/K) + stdev**2 t / 2
|
46
|
+
/{ stdev sqrt(t) }
|
47
|
+
d2 = d1 - stdev sqrt(t)
|
48
|
+
|
49
|
+
##
|
50
|
+
## From: https://www.daytrading.com/options-pricing-models
|
51
|
+
##
|
52
|
+
C0 = S0N(d1) – Xe-rtN(d2)
|
53
|
+
|
54
|
+
C0 = current call premium
|
55
|
+
S0 = current stock price
|
56
|
+
N(d1) = the probability that a value in a normal distribution will be less than d
|
57
|
+
N(d2) = the probability that the option will be in the money by expiration
|
58
|
+
X = strike price of the option
|
59
|
+
T = time until expiration (expressed in years)
|
60
|
+
r = risk-free interest rate
|
61
|
+
e = 2.71828, the base of the natural logarithm
|
62
|
+
ln = natural logarithm function
|
63
|
+
σ = standard deviation of the stock’s annualized rate of return (compounded continuously)
|
64
|
+
d1 = ln(S0/X) + (r + σ2/2)Tσ√T
|
65
|
+
|
66
|
+
d2 = d1 – σ√T
|
67
|
+
|
68
|
+
Note that:
|
69
|
+
|
70
|
+
Xe-rt = X/ert = the present value of the strike price using a continuously compounded interest rate
|
71
|
+
|
72
|
+
##
|
73
|
+
## From: https://www.wallstreetmojo.com/black-scholes-model/
|
74
|
+
##
|
75
|
+
|
76
|
+
|
77
|
+
## init
|
78
|
+
require 'distribution'
|
79
|
+
N = Distribution::Normal
|
80
|
+
stock = Iro::Stock.find_by ticker: 'NVDA'
|
81
|
+
strike = 910.0
|
82
|
+
r = Iro::Option.rate_daily
|
83
|
+
stdev = 91.0
|
84
|
+
t = 7.0
|
85
|
+
expires_on = '2024-03-22'
|
86
|
+
|
87
|
+
=end
|
88
|
+
def d1
|
89
|
+
last = stock.last
|
90
|
+
r = self.class.rate_annual
|
91
|
+
|
92
|
+
out = Math.log( last / strike ) + ( r + stdev**2 / 2 ) * t
|
93
|
+
out = out /( stdev * Math.sqrt(t) )
|
94
|
+
return out
|
95
|
+
end
|
96
|
+
def d2
|
97
|
+
last = stock.last
|
98
|
+
r = self.class.rate_annual
|
99
|
+
|
100
|
+
out = d1 - stdev * Math.sqrt( t )
|
101
|
+
return out
|
102
|
+
end
|
103
|
+
def t
|
104
|
+
# t = 1.0 / 365 * Date.today.business_days_until( expires_on )
|
105
|
+
t = 1.0 / 365 * (expires_on - Date.today).to_i
|
106
|
+
end
|
107
|
+
def stdev
|
108
|
+
recompute = nil
|
109
|
+
stock.stdev( recompute: recompute )
|
110
|
+
end
|
111
|
+
def call_price
|
112
|
+
last = stock.last
|
113
|
+
r = self.class.rate_annual
|
114
|
+
|
115
|
+
out = N.cdf( d1 ) * last - N.cdf( d2 ) * strike * Math::E**( -1 * r * t )
|
116
|
+
return out
|
117
|
+
end
|
118
|
+
|
119
|
+
def put_price
|
120
|
+
last = stock.last
|
121
|
+
r = self.class.rate_annual
|
122
|
+
|
123
|
+
out = N.cdf(-d2) * strike * exp(-r*t) - N.cdf(-d1) * last
|
124
|
+
return out
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def self.rate_annual
|
129
|
+
0.05
|
130
|
+
end
|
131
|
+
|
132
|
+
=begin
|
133
|
+
# test
|
134
|
+
|
135
|
+
inn = 100
|
136
|
+
n.times { inn = inn*(1.0+out) }
|
137
|
+
inn
|
138
|
+
|
139
|
+
=end
|
140
|
+
def self.rate_daily
|
141
|
+
n = 250.0 # days
|
142
|
+
# n = 12 # months
|
143
|
+
|
144
|
+
out = (1.0+self.rate_annual)**(1.0/n) - 1.0
|
145
|
+
puts! out, 'rate_daily'
|
146
|
+
return out
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|