From af178422e2d00d20beef2a29b37133370f283f6b Mon Sep 17 00:00:00 2001 From: Chuck Remes Date: Wed, 22 Jun 2016 13:48:24 -0500 Subject: [PATCH 1/3] Add DSL block style for initializing a call or put option Many Ruby objects offer the ability to initialize an instance object using a block. It's a very natural Ruby idiom. Added appropriate methods to support this idiom and removed the attr_accessors. Aliased the new methods to the old methods so the original API would continue to work. Would like to deprecate this old API. Aliased shorter names for the 'calc' methods. The original method naming is a bit clunky so this cleans it up. --- lib/options_library/option_call.rb | 4 +-- lib/options_library/option_model.rb | 54 +++++++++++++++++++++++++++-- lib/options_library/option_put.rb | 4 +-- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/lib/options_library/option_call.rb b/lib/options_library/option_call.rb index c0bb4b4..1619418 100644 --- a/lib/options_library/option_call.rb +++ b/lib/options_library/option_call.rb @@ -5,8 +5,8 @@ module Option class Call < Model - def initialize - super(:call) + def initialize(&blk) + super(:call, &blk) end end diff --git a/lib/options_library/option_model.rb b/lib/options_library/option_model.rb index 0b6ade1..6b81e4b 100644 --- a/lib/options_library/option_model.rb +++ b/lib/options_library/option_model.rb @@ -14,9 +14,11 @@ class Model CALC_THETA_METHODS = { :call=>Calculator.method('theta_call'), :put=>Calculator.method('theta_put') } IMPLIED_VOL_METHODS = { :call=>Calculator.method('implied_vol_call'), :put=>Calculator.method('implied_vol_put') } - attr_accessor :underlying, :strike, :time, :interest, :sigma, :dividend, :option_type + attr_accessor :option_type + + def initialize(option_type, &blk) + raise 'Unknown option_type' unless KNOWN_OPTION_TYPES.include?(option_type) - def initialize(option_type) self.option_type = option_type self.underlying = 0.0 self.strike = 0.0 @@ -24,32 +26,78 @@ def initialize(option_type) self.interest = 0.0 self.sigma = 0.0 self.dividend = 0.0 - raise 'Unknown option_type' unless KNOWN_OPTION_TYPES.include?(option_type) + + if block_given? + # http://graysoftinc.com/ruby-voodoo/dsl-block-styles + blk.arity == 1 ? blk.call(self) : instance_eval(&blk) + end + end + + def underlying(value=nil) + @underlying = value if value + @underlying + end + alias_method :underlying=, :underlying + + def strike(value=nil) + @strike = value if value + @strike + end + alias_method :strike=, :strike + + def time(value=nil) + @time = value if value + @time + end + alias_method :time=, :time + + def interest(value=nil) + @interest = value if value + @interest + end + alias_method :interest=, :interest + + def sigma(value=nil) + @sigma = value if value + @sigma + end + alias_method :sigma=, :sigma + + def dividend(value=nil) + @dividend = value if value + @dividend end + alias_method :dividend=, :dividend def calc_price CALC_PRICE_METHODS[option_type].call(underlying, strike, time, interest, sigma, dividend) end + alias_method :price, :calc_price def calc_delta CALC_DELTA_METHODS[option_type].call(underlying, strike, time, interest, sigma, dividend) end + alias_method :delta, :calc_delta def calc_gamma Calculator.gamma(underlying, strike, time, interest, sigma, dividend) end + alias_method :gamma, :calc_gamma def calc_theta CALC_THETA_METHODS[option_type].call(underlying, strike, time, interest, sigma, dividend) end + alias_method :theta, :calc_theta def calc_vega Calculator.vega(underlying, strike, time, interest, sigma, dividend) end + alias_method :vega, :calc_vega def calc_implied_vol(target_price) IMPLIED_VOL_METHODS[option_type].call(underlying, strike, time, interest, target_price, dividend) end + alias_method :implied_vol_for_price, :calc_implied_vol end end diff --git a/lib/options_library/option_put.rb b/lib/options_library/option_put.rb index bc3cba3..3fecaa7 100644 --- a/lib/options_library/option_put.rb +++ b/lib/options_library/option_put.rb @@ -5,8 +5,8 @@ module Option class Put < Model - def initialize - super(:put) + def initialize(&blk) + super(:put, &blk) end end From e2c573a0e85583d5d9baa911896cadbfbde68129 Mon Sep 17 00:00:00 2001 From: Chuck Remes Date: Wed, 22 Jun 2016 13:52:55 -0500 Subject: [PATCH 2/3] Modified README to use new DSL block style initialization. Removed section on Testing since there are no tests. --- README.md | 59 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 09b89d3..2fdf775 100644 --- a/README.md +++ b/README.md @@ -30,35 +30,44 @@ Usage require 'rubygems' require 'options_library' - call = Option::Call.new - call.underlying = 95.40 # spot price of the underlying - call.strike = 90.00 # strike price of option - call.time = 0.015 # time in years - call.interest = 0.01 # equates to 1% risk free interest - call.sigma = 0.4875 # equates to 48.75% volatility - call.dividend = 0.0 # no annual dividend yield - - price = call.calc_price # theoretical value of the option - delta = call.calc_delta # option price sensitivity to a change in underlying price - gamma = call.calc_gamma # option delta sensitivity to a change in underlying price - vega = call.calc_vega # option price sensitivity to a change in sigma (volatility) - - implied_vol = call.calc_implied_vol( 1.80 ) # implied volatility based on the target price + # Build the option out with a block builder + call = Option::Call.new do + underlying 95.40 # spot price of the underlying + strike 90.5 # strike price of option + time 0.015 # time in years + interest 0.01 # equates to 1% risk free interest + sigma 0.4875 # equates to 48.75% volatility + dividend 0.05 # 5% annual dividend yield + end + + # Values are calculated when requested + p call.price # theoretical value of the option + p call.delta # option price sensitivity to a change in underlying price + p call.gamma # option delta sensitivity to a change in underlying price + p call.vega # option price sensitivity to a change in sigma (volatility) + + # implied volatility based on a target price + p call.implied_vol_for_price( 1.80 ) # Or go straight at the Calculator methods # Option::Calculator.price_call( underlying, strike, time, interest, sigma, dividend ) - call_price = Option::Calculator.price_call( 94.5, 90.5, 0.015, 0.01, 0.4875, 0.0 ) - -Testing -------- - -To run the tests: - - $ rake - -To add tests see the `Commands` section earlier in this -README. + call_price = Option::Calculator.price_call( 95.40, 90.5, 0.015, 0.01, 0.4875, 0.05 ) + # Deprecated API + call = Option::Call.new + call.underlying = 95.40 + call.strike = 90.00 + call.time = 0.015 + call.interest = 0.01 + call.sigma = 0.4875 + call.dividend = 0.0 + + price = call.calc_price + delta = call.calc_delta + gamma = call.calc_gamma + vega = call.calc_vega + + implied_vol = call.calc_implied_vol( 1.80 ) Contributing ------------ From 45a68e9058807684b024562e5c4db7fb334be2c7 Mon Sep 17 00:00:00 2001 From: Chuck Remes Date: Wed, 22 Jun 2016 13:53:32 -0500 Subject: [PATCH 3/3] bumped to 1.0.4, removed superfluous files, added simple license Rubygems now complains when no license exists, so I just chose the simple 0-clause BSD license. Really just says "use as-is at your own risk" --- options_library.gemspec | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/options_library.gemspec b/options_library.gemspec index c1a989e..030e5d1 100644 --- a/options_library.gemspec +++ b/options_library.gemspec @@ -2,21 +2,37 @@ Gem::Specification.new do |s| s.name = %q{options_library} - s.version = "1.0.3" + s.version = "1.0.4" s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= s.authors = ["Dan Tylenda-Emmons"] - s.date = %q{2011-02-17} - s.description = %q{A gem used to calc the price of an option.} + s.date = %q{2016-06-22} + s.description = %q{A gem used to calc the price of call and put options.} s.email = %q{jrubyist@gmail.com} - s.extra_rdoc_files = ["README.md", "lib/options_library.rb", "lib/options_library/option_calculator.rb", "lib/options_library/option_call.rb", "lib/options_library/option_model.rb", "lib/options_library/option_put.rb"] - s.files = ["Manifest", "README.md", "Rakefile", "lib/options_library.rb", "lib/options_library/option_calculator.rb", "lib/options_library/option_call.rb", "lib/options_library/option_model.rb", "lib/options_library/option_put.rb", "options_library.gemspec", "options_library/options_library.iml"] + s.extra_rdoc_files = [ + "README.md", + "lib/options_library.rb", + "lib/options_library/option_calculator.rb", + "lib/options_library/option_call.rb", + "lib/options_library/option_model.rb", + "lib/options_library/option_put.rb"] + s.files = [ + "Manifest", + "README.md", + "lib/options_library.rb", + "lib/options_library/option_calculator.rb", + "lib/options_library/option_call.rb", + "lib/options_library/option_model.rb", + "lib/options_library/option_put.rb", + "options_library.gemspec"] s.homepage = %q{http://github.com/codertrader/options_library} s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Options_library", "--main", "README.md"] s.require_paths = ["lib"] s.rubyforge_project = %q{options_library} s.rubygems_version = %q{1.5.2} - s.summary = %q{A gem used to calc the price of an option.} + s.summary = %q{A gem used to calc the price of call and put options. Can also produce all of the greeks and will compute + implied volatility given a target price.} + s.license = "0BSD" if s.respond_to? :specification_version then s.specification_version = 3