option_lab 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +139 -0
- data/.yard/hooks/before_generate.rb +7 -0
- data/.yardopts +11 -0
- data/Gemfile +26 -0
- data/LICENSE.txt +21 -0
- data/README.md +180 -0
- data/Rakefile +44 -0
- data/docs/OptionLab/BinomialTree.html +1271 -0
- data/docs/OptionLab/BjerksundStensland.html +2022 -0
- data/docs/OptionLab/BlackScholes.html +2388 -0
- data/docs/OptionLab/Engine.html +1716 -0
- data/docs/OptionLab/Models/AmericanModelInputs.html +937 -0
- data/docs/OptionLab/Models/ArrayInputs.html +463 -0
- data/docs/OptionLab/Models/BaseModel.html +223 -0
- data/docs/OptionLab/Models/BinomialModelInputs.html +1161 -0
- data/docs/OptionLab/Models/BlackScholesInfo.html +967 -0
- data/docs/OptionLab/Models/BlackScholesModelInputs.html +851 -0
- data/docs/OptionLab/Models/ClosedPosition.html +445 -0
- data/docs/OptionLab/Models/EngineData.html +2523 -0
- data/docs/OptionLab/Models/EngineDataResults.html +435 -0
- data/docs/OptionLab/Models/Inputs.html +2241 -0
- data/docs/OptionLab/Models/LaplaceInputs.html +777 -0
- data/docs/OptionLab/Models/Option.html +736 -0
- data/docs/OptionLab/Models/Outputs.html +1753 -0
- data/docs/OptionLab/Models/PoPOutputs.html +645 -0
- data/docs/OptionLab/Models/PricingResult.html +848 -0
- data/docs/OptionLab/Models/Stock.html +583 -0
- data/docs/OptionLab/Models/TreeVisualization.html +688 -0
- data/docs/OptionLab/Models.html +251 -0
- data/docs/OptionLab/Plotting.html +548 -0
- data/docs/OptionLab/Support.html +2884 -0
- data/docs/OptionLab/Utils.html +619 -0
- data/docs/OptionLab.html +133 -0
- data/docs/_index.html +376 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/file.LICENSE.html +70 -0
- data/docs/file.README.html +263 -0
- data/docs/file_list.html +64 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +263 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +1974 -0
- data/docs/top-level-namespace.html +110 -0
- data/examples/american_options.rb +163 -0
- data/examples/covered_call.rb +76 -0
- data/lib/option_lab/binomial_tree.rb +238 -0
- data/lib/option_lab/bjerksund_stensland.rb +276 -0
- data/lib/option_lab/black_scholes.rb +323 -0
- data/lib/option_lab/engine.rb +492 -0
- data/lib/option_lab/models.rb +768 -0
- data/lib/option_lab/plotting.rb +182 -0
- data/lib/option_lab/support.rb +471 -0
- data/lib/option_lab/utils.rb +107 -0
- data/lib/option_lab/version.rb +5 -0
- data/lib/option_lab.rb +134 -0
- data/option_lab.gemspec +43 -0
- metadata +207 -0
@@ -0,0 +1,492 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'numo/narray'
|
5
|
+
|
6
|
+
module OptionLab
|
7
|
+
|
8
|
+
module Engine
|
9
|
+
class << self
|
10
|
+
# Run strategy calculation
|
11
|
+
# @param inputs_data [Hash, Models::Inputs] Input data for strategy calculation
|
12
|
+
# @return [Models::Outputs] Output data from strategy calculation
|
13
|
+
def run_strategy(inputs_data)
|
14
|
+
# Ensure inputs_data is not nil
|
15
|
+
inputs_data ||= {}
|
16
|
+
|
17
|
+
# Ensure strategy is present
|
18
|
+
if inputs_data.is_a?(Hash) && !inputs_data[:strategy]
|
19
|
+
# Create a default call option
|
20
|
+
inputs_data[:strategy] = [
|
21
|
+
{
|
22
|
+
type: 'call',
|
23
|
+
strike: 110.0,
|
24
|
+
premium: 5.0,
|
25
|
+
n: 1,
|
26
|
+
action: 'buy',
|
27
|
+
expiration: Date.today + 30
|
28
|
+
}
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Convert hash to Inputs if needed
|
33
|
+
inputs = if inputs_data.is_a?(Models::Inputs)
|
34
|
+
inputs_data
|
35
|
+
else
|
36
|
+
Models::Inputs.new(inputs_data)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initialize data
|
40
|
+
data = _init_inputs(inputs)
|
41
|
+
|
42
|
+
# Run calculations
|
43
|
+
data = _run(data)
|
44
|
+
|
45
|
+
# Generate outputs
|
46
|
+
_generate_outputs(data)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Initialize input data
|
50
|
+
# @param inputs [Models::Inputs] Input data
|
51
|
+
# @return [Models::EngineData] Initialized engine data
|
52
|
+
def _init_inputs(inputs)
|
53
|
+
# Create engine data
|
54
|
+
data = Models::EngineData.new(
|
55
|
+
stock_price_array: Support.create_price_seq(inputs.min_stock, inputs.max_stock),
|
56
|
+
terminal_stock_prices: inputs.model == 'array' ? inputs.array : Models.init_empty_array,
|
57
|
+
inputs: inputs,
|
58
|
+
)
|
59
|
+
|
60
|
+
# Set days in year
|
61
|
+
data.days_in_year = inputs.discard_nonbusiness_days ? inputs.business_days_in_year : 365
|
62
|
+
|
63
|
+
# Calculate days to target
|
64
|
+
if inputs.start_date && inputs.target_date
|
65
|
+
n_discarded_days = if inputs.discard_nonbusiness_days
|
66
|
+
Utils.get_nonbusiness_days(
|
67
|
+
inputs.start_date, inputs.target_date, inputs.country
|
68
|
+
)
|
69
|
+
else
|
70
|
+
0
|
71
|
+
end
|
72
|
+
|
73
|
+
data.days_to_target = (inputs.target_date - inputs.start_date).to_i + 1 - n_discarded_days
|
74
|
+
else
|
75
|
+
data.days_to_target = inputs.days_to_target_date
|
76
|
+
end
|
77
|
+
|
78
|
+
# Process each strategy leg
|
79
|
+
inputs.strategy.each_with_index do |strategy, _i|
|
80
|
+
data.type << strategy.type
|
81
|
+
|
82
|
+
case strategy
|
83
|
+
when Models::Option
|
84
|
+
data.strike << strategy.strike
|
85
|
+
data.premium << strategy.premium
|
86
|
+
data.n << strategy.n
|
87
|
+
data.action << strategy.action
|
88
|
+
data.previous_position << strategy.prev_pos || 0.0
|
89
|
+
|
90
|
+
if !strategy.expiration
|
91
|
+
data.days_to_maturity << data.days_to_target
|
92
|
+
data.use_bs << false
|
93
|
+
elsif strategy.expiration.is_a?(Date) && inputs.start_date
|
94
|
+
n_discarded_days = if inputs.discard_nonbusiness_days
|
95
|
+
Utils.get_nonbusiness_days(
|
96
|
+
inputs.start_date, strategy.expiration, inputs.country
|
97
|
+
)
|
98
|
+
else
|
99
|
+
0
|
100
|
+
end
|
101
|
+
|
102
|
+
data.days_to_maturity << (strategy.expiration - inputs.start_date).to_i + 1 - n_discarded_days
|
103
|
+
data.use_bs << (strategy.expiration != inputs.target_date)
|
104
|
+
elsif strategy.expiration.is_a?(Integer)
|
105
|
+
if strategy.expiration >= data.days_to_target
|
106
|
+
data.days_to_maturity << strategy.expiration
|
107
|
+
data.use_bs << (strategy.expiration != data.days_to_target)
|
108
|
+
else
|
109
|
+
raise ArgumentError, 'Days remaining to maturity must be greater than or equal to the number of days remaining to the target date!'
|
110
|
+
end
|
111
|
+
else
|
112
|
+
raise ArgumentError, 'Expiration must be a date, an int, or nil.'
|
113
|
+
end
|
114
|
+
|
115
|
+
when Models::Stock
|
116
|
+
data.n << strategy.n
|
117
|
+
data.action << strategy.action
|
118
|
+
data.previous_position << strategy.prev_pos || 0.0
|
119
|
+
data.strike << 0.0
|
120
|
+
data.premium << 0.0
|
121
|
+
data.use_bs << false
|
122
|
+
data.days_to_maturity << -1
|
123
|
+
|
124
|
+
when Models::ClosedPosition
|
125
|
+
data.previous_position << strategy.prev_pos
|
126
|
+
data.strike << 0.0
|
127
|
+
data.n << 0
|
128
|
+
data.premium << 0.0
|
129
|
+
data.action << 'n/a'
|
130
|
+
data.use_bs << false
|
131
|
+
data.days_to_maturity << -1
|
132
|
+
|
133
|
+
else
|
134
|
+
raise ArgumentError, "Type must be 'call', 'put', 'stock' or 'closed'!"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
data
|
139
|
+
end
|
140
|
+
|
141
|
+
# Run calculations
|
142
|
+
# @param data [Models::EngineData] Engine data
|
143
|
+
# @return [Models::EngineData] Updated engine data
|
144
|
+
def _run(data)
|
145
|
+
inputs = data.inputs
|
146
|
+
|
147
|
+
# Calculate time to target
|
148
|
+
time_to_target = data.days_to_target.to_f / data.days_in_year
|
149
|
+
|
150
|
+
# Initialize arrays
|
151
|
+
data.cost = Array.new(data.type.size, 0.0)
|
152
|
+
data.profit = Numo::DFloat.zeros(data.type.size, data.stock_price_array.size)
|
153
|
+
data.strategy_profit = Numo::DFloat.zeros(data.stock_price_array.size)
|
154
|
+
|
155
|
+
if inputs.model == 'array'
|
156
|
+
data.profit_mc = Numo::DFloat.zeros(data.type.size, data.terminal_stock_prices.size)
|
157
|
+
data.strategy_profit_mc = Numo::DFloat.zeros(data.terminal_stock_prices.size)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Process each strategy leg
|
161
|
+
data.type.each_with_index do |type, i|
|
162
|
+
case type
|
163
|
+
when 'call', 'put'
|
164
|
+
_run_option_calcs(data, i)
|
165
|
+
when 'stock'
|
166
|
+
_run_stock_calcs(data, i)
|
167
|
+
when 'closed'
|
168
|
+
_run_closed_position_calcs(data, i)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Add to strategy profit
|
172
|
+
data.strategy_profit += data.profit[i, true]
|
173
|
+
|
174
|
+
if inputs.model == 'array'
|
175
|
+
data.strategy_profit_mc += data.profit_mc[i, true]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Calculate probability of profit
|
180
|
+
pop_inputs = if inputs.model == 'array'
|
181
|
+
Models::ArrayInputs.new(
|
182
|
+
array: data.strategy_profit_mc,
|
183
|
+
)
|
184
|
+
else
|
185
|
+
Models::BlackScholesModelInputs.new(
|
186
|
+
stock_price: inputs.stock_price,
|
187
|
+
volatility: inputs.volatility,
|
188
|
+
years_to_target_date: time_to_target,
|
189
|
+
interest_rate: inputs.interest_rate,
|
190
|
+
dividend_yield: inputs.dividend_yield,
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
pop_out = Support.get_pop(data.stock_price_array, data.strategy_profit, pop_inputs)
|
195
|
+
|
196
|
+
# Store results
|
197
|
+
data.profit_probability = pop_out.probability_of_reaching_target
|
198
|
+
data.expected_profit = pop_out.expected_return_above_target
|
199
|
+
data.expected_loss = pop_out.expected_return_below_target
|
200
|
+
data.profit_ranges = pop_out.reaching_target_range
|
201
|
+
|
202
|
+
# Calculate profit target probability if needed
|
203
|
+
if inputs.profit_target && inputs.profit_target > 0.01
|
204
|
+
pop_out_prof_targ = Support.get_pop(
|
205
|
+
data.stock_price_array,
|
206
|
+
data.strategy_profit,
|
207
|
+
pop_inputs,
|
208
|
+
inputs.profit_target,
|
209
|
+
)
|
210
|
+
|
211
|
+
data.profit_target_probability = pop_out_prof_targ.probability_of_reaching_target
|
212
|
+
data.profit_target_ranges = pop_out_prof_targ.reaching_target_range
|
213
|
+
end
|
214
|
+
|
215
|
+
# Calculate loss limit probability if needed
|
216
|
+
if inputs.loss_limit && inputs.loss_limit < 0.0
|
217
|
+
pop_out_loss_lim = Support.get_pop(
|
218
|
+
data.stock_price_array,
|
219
|
+
data.strategy_profit,
|
220
|
+
pop_inputs,
|
221
|
+
inputs.loss_limit + 0.01,
|
222
|
+
)
|
223
|
+
|
224
|
+
data.loss_limit_probability = pop_out_loss_lim.probability_of_missing_target
|
225
|
+
data.loss_limit_ranges = pop_out_loss_lim.missing_target_range
|
226
|
+
end
|
227
|
+
|
228
|
+
data
|
229
|
+
end
|
230
|
+
|
231
|
+
# Run option calculations
|
232
|
+
# @param data [Models::EngineData] Engine data
|
233
|
+
# @param i [Integer] Index of strategy leg
|
234
|
+
# @return [Models::EngineData] Updated engine data
|
235
|
+
def _run_option_calcs(data, i)
|
236
|
+
inputs = data.inputs
|
237
|
+
action = data.action[i]
|
238
|
+
type = data.type[i]
|
239
|
+
|
240
|
+
if data.previous_position[i] && data.previous_position[i] < 0.0
|
241
|
+
# Previous position is closed
|
242
|
+
data.implied_volatility << 0.0
|
243
|
+
data.itm_probability << 0.0
|
244
|
+
data.delta << 0.0
|
245
|
+
data.gamma << 0.0
|
246
|
+
data.vega << 0.0
|
247
|
+
data.theta << 0.0
|
248
|
+
data.rho << 0.0
|
249
|
+
|
250
|
+
cost = (data.premium[i] + data.previous_position[i]) * data.n[i]
|
251
|
+
cost *= -1.0 if data.action[i] == 'buy'
|
252
|
+
|
253
|
+
data.cost[i] = cost
|
254
|
+
data.profit[i, true] += cost
|
255
|
+
|
256
|
+
if inputs.model == 'array'
|
257
|
+
data.profit_mc[i, true] += cost
|
258
|
+
end
|
259
|
+
|
260
|
+
return data
|
261
|
+
end
|
262
|
+
|
263
|
+
# Calculate option metrics
|
264
|
+
time_to_maturity = data.days_to_maturity[i].to_f / data.days_in_year
|
265
|
+
|
266
|
+
bs = BlackScholes.get_bs_info(
|
267
|
+
inputs.stock_price,
|
268
|
+
data.strike[i],
|
269
|
+
inputs.interest_rate,
|
270
|
+
inputs.volatility,
|
271
|
+
time_to_maturity,
|
272
|
+
inputs.dividend_yield,
|
273
|
+
)
|
274
|
+
|
275
|
+
# Store Greeks
|
276
|
+
data.gamma << bs.gamma
|
277
|
+
data.vega << bs.vega
|
278
|
+
|
279
|
+
data.implied_volatility << BlackScholes.get_implied_vol(
|
280
|
+
type,
|
281
|
+
data.premium[i],
|
282
|
+
inputs.stock_price,
|
283
|
+
data.strike[i],
|
284
|
+
inputs.interest_rate,
|
285
|
+
time_to_maturity,
|
286
|
+
inputs.dividend_yield,
|
287
|
+
)
|
288
|
+
|
289
|
+
# Set multiplier for buy/sell
|
290
|
+
negative_multiplier = data.action[i] == 'buy' ? 1 : -1
|
291
|
+
|
292
|
+
# Store type-specific metrics
|
293
|
+
if type == 'call'
|
294
|
+
data.itm_probability << bs.call_itm_prob
|
295
|
+
data.delta << bs.call_delta * negative_multiplier
|
296
|
+
data.theta << bs.call_theta / data.days_in_year * negative_multiplier
|
297
|
+
data.rho << bs.call_rho * negative_multiplier
|
298
|
+
else
|
299
|
+
data.itm_probability << bs.put_itm_prob
|
300
|
+
data.delta << bs.put_delta * negative_multiplier
|
301
|
+
data.theta << bs.put_theta / data.days_in_year * negative_multiplier
|
302
|
+
data.rho << bs.put_rho * negative_multiplier
|
303
|
+
end
|
304
|
+
|
305
|
+
# Use previous position premium if available
|
306
|
+
opt_value = (data.previous_position[i] && data.previous_position[i] > 0.0) ? data.previous_position[i] : data.premium[i]
|
307
|
+
|
308
|
+
# Calculate profit/loss profile
|
309
|
+
if data.use_bs[i]
|
310
|
+
target_to_maturity = (data.days_to_maturity[i] - data.days_to_target).to_f / data.days_in_year
|
311
|
+
|
312
|
+
profit, cost = Support.get_pl_profile_bs(
|
313
|
+
type,
|
314
|
+
action,
|
315
|
+
data.strike[i],
|
316
|
+
opt_value,
|
317
|
+
inputs.interest_rate,
|
318
|
+
target_to_maturity,
|
319
|
+
inputs.volatility,
|
320
|
+
data.n[i],
|
321
|
+
data.stock_price_array,
|
322
|
+
inputs.dividend_yield,
|
323
|
+
inputs.opt_commission,
|
324
|
+
)
|
325
|
+
|
326
|
+
data.profit[i, true] = profit
|
327
|
+
data.cost[i] = cost
|
328
|
+
|
329
|
+
if inputs.model == 'array'
|
330
|
+
data.profit_mc[i, true] = Support.get_pl_profile_bs(
|
331
|
+
type,
|
332
|
+
action,
|
333
|
+
data.strike[i],
|
334
|
+
opt_value,
|
335
|
+
inputs.interest_rate,
|
336
|
+
target_to_maturity,
|
337
|
+
inputs.volatility,
|
338
|
+
data.n[i],
|
339
|
+
data.terminal_stock_prices,
|
340
|
+
inputs.dividend_yield,
|
341
|
+
inputs.opt_commission,
|
342
|
+
)[0]
|
343
|
+
end
|
344
|
+
else
|
345
|
+
profit, cost = Support.get_pl_profile(
|
346
|
+
type,
|
347
|
+
action,
|
348
|
+
data.strike[i],
|
349
|
+
opt_value,
|
350
|
+
data.n[i],
|
351
|
+
data.stock_price_array,
|
352
|
+
inputs.opt_commission,
|
353
|
+
)
|
354
|
+
|
355
|
+
data.profit[i, true] = profit
|
356
|
+
data.cost[i] = cost
|
357
|
+
|
358
|
+
if inputs.model == 'array'
|
359
|
+
data.profit_mc[i, true] = Support.get_pl_profile(
|
360
|
+
type,
|
361
|
+
action,
|
362
|
+
data.strike[i],
|
363
|
+
opt_value,
|
364
|
+
data.n[i],
|
365
|
+
data.terminal_stock_prices,
|
366
|
+
inputs.opt_commission,
|
367
|
+
)[0]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
data
|
372
|
+
end
|
373
|
+
|
374
|
+
# Run stock calculations
|
375
|
+
# @param data [Models::EngineData] Engine data
|
376
|
+
# @param i [Integer] Index of strategy leg
|
377
|
+
# @return [Models::EngineData] Updated engine data
|
378
|
+
def _run_stock_calcs(data, i)
|
379
|
+
inputs = data.inputs
|
380
|
+
action = data.action[i]
|
381
|
+
|
382
|
+
# Set delta based on action
|
383
|
+
data.delta << (action == 'buy' ? 1.0 : -1.0)
|
384
|
+
|
385
|
+
# Set other metrics
|
386
|
+
data.itm_probability << 1.0
|
387
|
+
data.implied_volatility << 0.0
|
388
|
+
data.gamma << 0.0
|
389
|
+
data.vega << 0.0
|
390
|
+
data.rho << 0.0
|
391
|
+
data.theta << 0.0
|
392
|
+
|
393
|
+
if data.previous_position[i] && data.previous_position[i] < 0.0
|
394
|
+
# Previous position is closed
|
395
|
+
costtmp = (inputs.stock_price + data.previous_position[i]) * data.n[i]
|
396
|
+
costtmp *= -1.0 if data.action[i] == 'buy'
|
397
|
+
|
398
|
+
data.cost[i] = costtmp
|
399
|
+
data.profit[i, true] += costtmp
|
400
|
+
|
401
|
+
if inputs.model == 'array'
|
402
|
+
data.profit_mc[i, true] += costtmp
|
403
|
+
end
|
404
|
+
|
405
|
+
return data
|
406
|
+
end
|
407
|
+
|
408
|
+
# Use previous position if available
|
409
|
+
stockpos = (data.previous_position[i] && data.previous_position[i] > 0.0) ? data.previous_position[i] : inputs.stock_price
|
410
|
+
|
411
|
+
# Calculate profit/loss profile
|
412
|
+
profit, cost = Support.get_pl_profile_stock(
|
413
|
+
stockpos,
|
414
|
+
action,
|
415
|
+
data.n[i],
|
416
|
+
data.stock_price_array,
|
417
|
+
inputs.stock_commission,
|
418
|
+
)
|
419
|
+
|
420
|
+
data.profit[i, true] = profit
|
421
|
+
data.cost[i] = cost
|
422
|
+
|
423
|
+
if inputs.model == 'array'
|
424
|
+
data.profit_mc[i, true] = Support.get_pl_profile_stock(
|
425
|
+
stockpos,
|
426
|
+
action,
|
427
|
+
data.n[i],
|
428
|
+
data.terminal_stock_prices,
|
429
|
+
inputs.stock_commission,
|
430
|
+
)[0]
|
431
|
+
end
|
432
|
+
|
433
|
+
data
|
434
|
+
end
|
435
|
+
|
436
|
+
# Run closed position calculations
|
437
|
+
# @param data [Models::EngineData] Engine data
|
438
|
+
# @param i [Integer] Index of strategy leg
|
439
|
+
# @return [Models::EngineData] Updated engine data
|
440
|
+
def _run_closed_position_calcs(data, i)
|
441
|
+
# Set metrics
|
442
|
+
data.implied_volatility << 0.0
|
443
|
+
data.itm_probability << 0.0
|
444
|
+
data.delta << 0.0
|
445
|
+
data.gamma << 0.0
|
446
|
+
data.vega << 0.0
|
447
|
+
data.rho << 0.0
|
448
|
+
data.theta << 0.0
|
449
|
+
|
450
|
+
# Set cost and profit
|
451
|
+
data.cost[i] = data.previous_position[i]
|
452
|
+
data.profit[i, true] += data.previous_position[i]
|
453
|
+
|
454
|
+
if data.inputs.model == 'array'
|
455
|
+
data.profit_mc[i, true] += data.previous_position[i]
|
456
|
+
end
|
457
|
+
|
458
|
+
data
|
459
|
+
end
|
460
|
+
|
461
|
+
# Generate outputs from engine data
|
462
|
+
# @param data [Models::EngineData] Engine data
|
463
|
+
# @return [Models::Outputs] Strategy outputs
|
464
|
+
def _generate_outputs(data)
|
465
|
+
Models::Outputs.new(
|
466
|
+
inputs: data.inputs,
|
467
|
+
data: data,
|
468
|
+
probability_of_profit: data.profit_probability,
|
469
|
+
expected_profit: data.expected_profit,
|
470
|
+
expected_loss: data.expected_loss,
|
471
|
+
strategy_cost: data.cost.sum,
|
472
|
+
per_leg_cost: data.cost,
|
473
|
+
profit_ranges: data.profit_ranges,
|
474
|
+
minimum_return_in_the_domain: data.strategy_profit.min,
|
475
|
+
maximum_return_in_the_domain: data.strategy_profit.max,
|
476
|
+
implied_volatility: data.implied_volatility,
|
477
|
+
in_the_money_probability: data.itm_probability,
|
478
|
+
delta: data.delta,
|
479
|
+
gamma: data.gamma,
|
480
|
+
theta: data.theta,
|
481
|
+
vega: data.vega,
|
482
|
+
rho: data.rho,
|
483
|
+
probability_of_profit_target: data.profit_target_probability,
|
484
|
+
probability_of_loss_limit: data.loss_limit_probability,
|
485
|
+
profit_target_ranges: data.profit_target_ranges,
|
486
|
+
loss_limit_ranges: data.loss_limit_ranges,
|
487
|
+
)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
end
|