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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 42772c157960f755fbc0dd3dedc3dc41dcee05d7caf3d78107a9884ebed2ec08
|
4
|
+
data.tar.gz: 1063a3be2be3fae5ce37ede1c6ca702c7934d55738a4601af5e69d9feee736e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7b528868a81353a9cd39e0190d1cc31e02d3abc20b2ed912dd372722da26b2c33fc9cf33da0f7a90819b16ed92f39c8f3cabdfebd4442b9427c7dc2a569b3e08
|
7
|
+
data.tar.gz: 5dbe0d912bc99a367c0c987e702aa896c0e3ce5201d0c598517d62235fa8013a260b5d7effb137f17b34dca96a5d500fea4b6f4fe993389e0654d94e63ff1d50
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-shopify: rubocop.yml
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
TargetRubyVersion: 3.3
|
6
|
+
NewCops: enable
|
7
|
+
Exclude:
|
8
|
+
- 'vendor/**/*'
|
9
|
+
- 'bin/**/*'
|
10
|
+
- 'spec/spec_helper.rb'
|
11
|
+
- 'tmp/**/*'
|
12
|
+
|
13
|
+
# General style settings
|
14
|
+
Style/StringLiterals:
|
15
|
+
EnforcedStyle: single_quotes
|
16
|
+
Enabled: true
|
17
|
+
|
18
|
+
Style/StringLiteralsInInterpolation:
|
19
|
+
EnforcedStyle: single_quotes
|
20
|
+
Enabled: true
|
21
|
+
|
22
|
+
# Documentation is important for a library
|
23
|
+
Style/Documentation:
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
# Use modern Ruby features
|
27
|
+
Style/FrozenStringLiteralComment:
|
28
|
+
Enabled: true
|
29
|
+
EnforcedStyle: always
|
30
|
+
|
31
|
+
# Method length and complexity
|
32
|
+
Metrics/MethodLength:
|
33
|
+
Max: 30
|
34
|
+
|
35
|
+
Metrics/AbcSize:
|
36
|
+
Max: 30
|
37
|
+
|
38
|
+
Metrics/CyclomaticComplexity:
|
39
|
+
Max: 15
|
40
|
+
|
41
|
+
Metrics/PerceivedComplexity:
|
42
|
+
Max: 15
|
43
|
+
|
44
|
+
Metrics/ParameterLists:
|
45
|
+
Max: 8
|
46
|
+
|
47
|
+
# Class and module length
|
48
|
+
Metrics/ClassLength:
|
49
|
+
Max: 300
|
50
|
+
|
51
|
+
Metrics/ModuleLength:
|
52
|
+
Max: 300
|
53
|
+
|
54
|
+
# Line length
|
55
|
+
Layout/LineLength:
|
56
|
+
Max: 100
|
57
|
+
Exclude:
|
58
|
+
- 'spec/**/*'
|
59
|
+
|
60
|
+
# Block length exception for specs
|
61
|
+
Metrics/BlockLength:
|
62
|
+
Max: 30
|
63
|
+
Exclude:
|
64
|
+
- 'spec/**/*'
|
65
|
+
|
66
|
+
# Naming conventions
|
67
|
+
Naming/MethodParameterName:
|
68
|
+
MinNameLength: 1 # Allow single letter variables for mathematical formulas
|
69
|
+
|
70
|
+
# Prefer compact module/class definitions
|
71
|
+
Style/ClassAndModuleChildren:
|
72
|
+
Enabled: true
|
73
|
+
EnforcedStyle: nested
|
74
|
+
|
75
|
+
# Discourage usage of globals
|
76
|
+
Style/GlobalVars:
|
77
|
+
Enabled: true
|
78
|
+
|
79
|
+
# Allow more descriptive module/class variable names
|
80
|
+
Naming/ClassAndModuleCamelCase:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
# Use snake_case for variables and methods
|
84
|
+
Naming/VariableName:
|
85
|
+
EnforcedStyle: snake_case
|
86
|
+
|
87
|
+
Naming/MethodName:
|
88
|
+
EnforcedStyle: snake_case
|
89
|
+
|
90
|
+
# Parentheses for method definitions
|
91
|
+
Style/DefWithParentheses:
|
92
|
+
Enabled: true
|
93
|
+
|
94
|
+
# Method call with or without parentheses
|
95
|
+
Style/MethodCallWithArgsParentheses:
|
96
|
+
Enabled: false
|
97
|
+
|
98
|
+
# Raise exceptions with explicit new
|
99
|
+
Style/RaiseArgs:
|
100
|
+
EnforcedStyle: exploded
|
101
|
+
|
102
|
+
# Prefer the newer safe navigation operator
|
103
|
+
Style/SafeNavigation:
|
104
|
+
Enabled: true
|
105
|
+
|
106
|
+
# Trailing commas
|
107
|
+
Style/TrailingCommaInArrayLiteral:
|
108
|
+
EnforcedStyleForMultiline: consistent_comma
|
109
|
+
|
110
|
+
Style/TrailingCommaInHashLiteral:
|
111
|
+
EnforcedStyleForMultiline: consistent_comma
|
112
|
+
|
113
|
+
# Prefer %i for symbol arrays
|
114
|
+
Style/SymbolArray:
|
115
|
+
EnforcedStyle: percent
|
116
|
+
MinSize: 3
|
117
|
+
|
118
|
+
# Prefer %w for word arrays
|
119
|
+
Style/WordArray:
|
120
|
+
EnforcedStyle: percent
|
121
|
+
MinSize: 3
|
122
|
+
|
123
|
+
# Prefer &&/|| over and/or
|
124
|
+
Style/AndOr:
|
125
|
+
EnforcedStyle: always
|
126
|
+
|
127
|
+
# Accessibility modifiers should be indented consistently
|
128
|
+
Layout/AccessModifierIndentation:
|
129
|
+
EnforcedStyle: indent
|
130
|
+
|
131
|
+
# Consistent empty lines
|
132
|
+
Layout/EmptyLinesAroundClassBody:
|
133
|
+
EnforcedStyle: empty_lines
|
134
|
+
|
135
|
+
Layout/EmptyLinesAroundModuleBody:
|
136
|
+
EnforcedStyle: empty_lines
|
137
|
+
|
138
|
+
Layout/EmptyLinesAroundMethodBody:
|
139
|
+
EnforcedStyle: empty_lines
|
data/.yardopts
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in option_lab.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
# Runtime dependencies - these are always installed
|
9
|
+
gem "numo-narray", "~> 0.9.2"
|
10
|
+
gem "numo-linalg", "~> 0.1.7"
|
11
|
+
gem "distribution", "~> 0.8.0"
|
12
|
+
gem "unicode_plot", "~> 0.0.5"
|
13
|
+
gem "holidays", "~> 8.6.0"
|
14
|
+
gem "prime"
|
15
|
+
gem "bigdecimal"
|
16
|
+
gem "matrix"
|
17
|
+
|
18
|
+
# Development dependencies - only installed with `bundle install --with development`
|
19
|
+
group :development do
|
20
|
+
gem "rake", "~> 13.0"
|
21
|
+
gem "rspec", "~> 3.12"
|
22
|
+
gem "rubocop-shopify", require: false
|
23
|
+
gem "solargraph", "~> 0.49.0"
|
24
|
+
gem "yard", "~> 0.9.34"
|
25
|
+
gem "redcarpet", "~> 3.6.1" # Markdown processor for YARD
|
26
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Your Name
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# OptionLab
|
2
|
+
|
3
|
+
OptionLab is a lightweight Ruby library designed to provide quick evaluation of options trading strategies.
|
4
|
+
It aims to be a direct port of the popular Python library - [OptionLab](https://github.com/rgaveiga/optionlab)
|
5
|
+
|
6
|
+
***docs page coming soon***
|
7
|
+
[](https://xjackk.github.io/option_lab/)
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
- Calculate profit/loss profiles for options strategies
|
12
|
+
- Estimate probability of profit using Black-Scholes or custom models
|
13
|
+
- Calculate option Greeks (Delta, Gamma, Theta, Vega, Rho)
|
14
|
+
- Generate profit/loss diagrams
|
15
|
+
- Support for complex multi-leg strategies
|
16
|
+
- Handle stock positions and previously closed trades
|
17
|
+
- Support for different dividend yield and interest rate scenarios
|
18
|
+
- Business day calculations across different countries
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'option_lab'
|
26
|
+
```
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
```
|
31
|
+
$ bundle install
|
32
|
+
```
|
33
|
+
|
34
|
+
Or install it yourself as:
|
35
|
+
|
36
|
+
```
|
37
|
+
$ gem install option_lab
|
38
|
+
```
|
39
|
+
|
40
|
+
## Requirements
|
41
|
+
|
42
|
+
OptionLab requires:
|
43
|
+
|
44
|
+
- Ruby 3.3.0 or higher
|
45
|
+
- numo-narray gem for numerical computations
|
46
|
+
- distribution gem for statistical calculations
|
47
|
+
|
48
|
+
## Basic Usage
|
49
|
+
|
50
|
+
The evaluation of a strategy is done by calling the `run_strategy` method provided by the library. This method receives the input data as a Ruby hash or an `Inputs` object.
|
51
|
+
|
52
|
+
Here's an example of evaluating a naked call strategy:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
require 'option_lab'
|
56
|
+
|
57
|
+
# Define the strategy
|
58
|
+
input_data = {
|
59
|
+
stock_price: 164.04,
|
60
|
+
start_date: Date.new(2023, 11, 22),
|
61
|
+
target_date: Date.new(2023, 12, 17),
|
62
|
+
volatility: 0.272,
|
63
|
+
interest_rate: 0.0002,
|
64
|
+
min_stock: 120,
|
65
|
+
max_stock: 200,
|
66
|
+
strategy: [
|
67
|
+
{
|
68
|
+
type: "call",
|
69
|
+
strike: 175.0,
|
70
|
+
premium: 1.15,
|
71
|
+
n: 100,
|
72
|
+
action: "sell"
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
|
77
|
+
# Run the strategy calculation
|
78
|
+
outputs = OptionLab.run_strategy(input_data)
|
79
|
+
|
80
|
+
# Export P/L data to CSV
|
81
|
+
OptionLab.pl_to_csv(outputs, filename: "covered_call_pl.csv")
|
82
|
+
```
|
83
|
+
|
84
|
+
## Analyzing Results
|
85
|
+
|
86
|
+
The `Outputs` object returned by `run_strategy` contains a wealth of information:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# Key probability metrics
|
90
|
+
probability_of_profit = outputs.probability_of_profit
|
91
|
+
profit_ranges = outputs.profit_ranges
|
92
|
+
expected_profit = outputs.expected_profit
|
93
|
+
expected_loss = outputs.expected_loss
|
94
|
+
|
95
|
+
# Strategy costs
|
96
|
+
strategy_cost = outputs.strategy_cost
|
97
|
+
per_leg_cost = outputs.per_leg_cost
|
98
|
+
|
99
|
+
# Returns
|
100
|
+
min_return = outputs.minimum_return_in_the_domain
|
101
|
+
max_return = outputs.maximum_return_in_the_domain
|
102
|
+
|
103
|
+
# Option Greeks
|
104
|
+
delta = outputs.delta
|
105
|
+
gamma = outputs.gamma
|
106
|
+
theta = outputs.theta
|
107
|
+
vega = outputs.vega
|
108
|
+
rho = outputs.rho
|
109
|
+
|
110
|
+
# Print all metrics
|
111
|
+
puts outputs
|
112
|
+
```
|
113
|
+
|
114
|
+
## Contributing
|
115
|
+
|
116
|
+
1. Fork it
|
117
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
118
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
119
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
120
|
+
5. Create a new Pull Request
|
121
|
+
|
122
|
+
## License
|
123
|
+
|
124
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
125
|
+
|
126
|
+
# Plot the profit/loss diagram
|
127
|
+
OptionLab.plot_pl(outputs)
|
128
|
+
```
|
129
|
+
|
130
|
+
## Common Strategies
|
131
|
+
|
132
|
+
OptionLab supports all standard options strategies, including:
|
133
|
+
|
134
|
+
- Covered calls
|
135
|
+
- Naked puts
|
136
|
+
- Bull/bear spreads
|
137
|
+
- Straddles/strangles
|
138
|
+
- Iron condors
|
139
|
+
- Butterflies
|
140
|
+
- Calendar spreads
|
141
|
+
- And more...
|
142
|
+
|
143
|
+
## Advanced Usage
|
144
|
+
|
145
|
+
The library also allows for more advanced use cases, such as:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
# Create a custom distribution model
|
149
|
+
bs_inputs = OptionLab::Models::BlackScholesModelInputs.new(
|
150
|
+
stock_price: 168.99,
|
151
|
+
volatility: 0.483,
|
152
|
+
interest_rate: 0.045,
|
153
|
+
years_to_target_date: 24.0 / 365
|
154
|
+
)
|
155
|
+
|
156
|
+
# Generate price array with 10,000 samples
|
157
|
+
prices = OptionLab.create_price_array(bs_inputs, n: 10_000, seed: 42)
|
158
|
+
|
159
|
+
# Run a strategy with the custom price array
|
160
|
+
input_data = {
|
161
|
+
stock_price: 168.99,
|
162
|
+
volatility: 0.483,
|
163
|
+
interest_rate: 0.045,
|
164
|
+
min_stock: 120,
|
165
|
+
max_stock: 200,
|
166
|
+
model: "array",
|
167
|
+
array: prices,
|
168
|
+
strategy: [
|
169
|
+
{ type: "stock", n: 100, action: "buy" },
|
170
|
+
{
|
171
|
+
type: "call",
|
172
|
+
strike: 185.0,
|
173
|
+
premium: 4.1,
|
174
|
+
n: 100,
|
175
|
+
action: "sell"
|
176
|
+
}
|
177
|
+
]
|
178
|
+
}
|
179
|
+
|
180
|
+
outputs = OptionLab.run_strategy(input_data)
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require 'rubocop/rake_task'
|
9
|
+
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
task default: %i[spec rubocop]
|
13
|
+
|
14
|
+
desc 'Run example script'
|
15
|
+
task :example do
|
16
|
+
ruby 'examples/covered_call.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Run benchmarks'
|
20
|
+
task :benchmark do
|
21
|
+
ruby 'spec/benchmarks/benchmark.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Generate documentation using YARD'
|
25
|
+
task :doc do
|
26
|
+
sh 'mkdir -p docs/images'
|
27
|
+
sh 'yard doc lib/**/*.rb --output-dir docs'
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Clean temporary files and build artifacts'
|
31
|
+
task :clean do
|
32
|
+
sh 'rm -rf *.gem *.rbc coverage .rspec docs doc'
|
33
|
+
sh 'rm -rf .bundle vendor Gemfile.lock'
|
34
|
+
sh 'rm -rf .yardoc log pkg tmp'
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'Open a console with the gem loaded'
|
38
|
+
task :console do
|
39
|
+
require 'irb'
|
40
|
+
require 'irb/completion'
|
41
|
+
require 'option_lab'
|
42
|
+
ARGV.clear
|
43
|
+
IRB.start
|
44
|
+
end
|