ib-ruby 0.4.3

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.
@@ -0,0 +1,109 @@
1
+ #
2
+ # Copyright (C) 2009 Wes Devauld
3
+ #
4
+ # This library is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 2.1 of the
7
+ # License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful, but
10
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA
18
+ #
19
+
20
+ #
21
+ # Note that the :description field is particular to ib-ruby, and is NOT part of the standard TWS API.
22
+ # It is never transmitted to IB. It's purely used clientside, and you can store any arbitrary string that
23
+ # you may find useful there.
24
+ #
25
+
26
+ module IB
27
+ module Symbols
28
+ Forex = {
29
+ :audusd => Datatypes::Contract.new({
30
+ :symbol => "AUD",
31
+ :exchange => "IDEALPRO",
32
+ :currency => "USD",
33
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
34
+ :description => "AUDUSD"
35
+ }),
36
+ :gbpusd => Datatypes::Contract.new({
37
+ :symbol => "GBP",
38
+ :exchange => "IDEALPRO",
39
+ :currency => "USD",
40
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
41
+ :description => "GBPUSD"
42
+ }),
43
+
44
+ :euraud => Datatypes::Contract.new({
45
+ :symbol => "EUR",
46
+ :exchange => "IDEALPRO",
47
+ :currency => "AUD",
48
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
49
+ :description => "EURAUD"
50
+ }),
51
+
52
+ :eurgbp => Datatypes::Contract.new({
53
+ :symbol => "EUR",
54
+ :exchange => "IDEALPRO",
55
+ :currency => "GBP",
56
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
57
+ :description => "EURGBP"
58
+ }),
59
+
60
+ :eurjpy => Datatypes::Contract.new({
61
+ :symbol => "EUR",
62
+ :exchange => "IDEALPRO",
63
+ :currency => "JPY",
64
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
65
+ :description => "EURJPY"
66
+ }),
67
+
68
+ :eurusd => Datatypes::Contract.new({
69
+ :symbol => "EUR",
70
+ :exchange => "IDEALPRO",
71
+ :currency => "USD",
72
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
73
+ :description => "EURUSD"
74
+ }),
75
+
76
+ :eurcad => Datatypes::Contract.new({
77
+ :symbol => "EUR",
78
+ :exchange => "IDEALPRO",
79
+ :currency => "CAD",
80
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
81
+ :description => "EURCAD"
82
+ }),
83
+
84
+ :usdchf => Datatypes::Contract.new({
85
+ :symbol => "USD",
86
+ :exchange => "IDEALPRO",
87
+ :currency => "CHF",
88
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
89
+ :description => "USDCHF"
90
+ }),
91
+
92
+ :usdcad => Datatypes::Contract.new({
93
+ :symbol => "USD",
94
+ :exchange => "IDEALPRO",
95
+ :currency => "CAD",
96
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
97
+ :description => "USDCAD"
98
+ }),
99
+
100
+ :usdjpy => Datatypes::Contract.new({
101
+ :symbol => "USD",
102
+ :exchange => "IDEALPRO",
103
+ :currency => "JPY",
104
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:forex],
105
+ :description => "USDJPY"
106
+ })
107
+ }
108
+ end # Contracts
109
+ end
@@ -0,0 +1,109 @@
1
+ #
2
+ # Copyright (C) 2009 Wes Devauld
3
+ #
4
+ # This library is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 2.1 of the
7
+ # License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful, but
10
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA
18
+ #
19
+
20
+
21
+ # The Futures constant is currently for testing purposes. It guesses the front month
22
+ # currency future using a crude algorithm that does not take into account expiry/rollover day.
23
+ # This will be valid most of the time, but near/after expiry day the next quarter's contract
24
+ # takes over as the volume leader.
25
+ #
26
+ #
27
+ # Note that the :description field is particular to ib-ruby, and is NOT part of the standard TWS API.
28
+ # It is never transmitted to IB. It's purely used clientside, and you can store any arbitrary string that
29
+ # you may find useful there.
30
+ #
31
+
32
+ module IB
33
+ module Symbols
34
+
35
+ # Get the next valid quarter month >= today, for finding the
36
+ # front month of quarterly futures.
37
+ #
38
+ # N.B. This will not work as expected near/after expiration during that month, as
39
+ # volume rolls over to the next quarter even though the current month is still valid!
40
+ #
41
+
42
+
43
+ def self.next_quarter_month(time)
44
+ sprintf("%02d", [3, 6, 9, 12].find{|month| month >= time.month })
45
+ end
46
+
47
+ def self.next_quarter_year(time)
48
+ if self.next_quarter_month(time).to_i < time.month
49
+ time.year + 1
50
+ else
51
+ time.year
52
+ end
53
+ end
54
+
55
+ def self.next_expiry(time)
56
+ "#{ self.next_quarter_year(time) }#{ self.next_quarter_month(time) }"
57
+ end
58
+
59
+ Futures =
60
+ {
61
+ :es => Datatypes::Contract.new({
62
+ :symbol => "ES",
63
+ :expiry => self.next_expiry(Time.now),
64
+ :exchange => "GLOBEX",
65
+ :currency => "USD",
66
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:future],
67
+ :multiplier => 50,
68
+ :description => "E-Mini S&P 500"
69
+ }),
70
+
71
+ :gbp => Datatypes::Contract.new({
72
+ :symbol => "GBP",
73
+ :expiry => self.next_expiry(Time.now),
74
+ :exchange => "GLOBEX",
75
+ :currency => "USD",
76
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:future],
77
+ :multiplier => 62500,
78
+ :description => "British Pounds"
79
+ }),
80
+ :eur => Datatypes::Contract.new({
81
+ :symbol => "EUR",
82
+ :expiry => self.next_expiry(Time.now),
83
+ :exchange => "GLOBEX",
84
+ :currency => "USD",
85
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:future],
86
+ :multiplier => 12500,
87
+ :description => "Euro FX"
88
+ }),
89
+ :jpy => Datatypes::Contract.new({
90
+ :symbol => "JPY",
91
+ :expiry => self.next_expiry(Time.now),
92
+ :exchange => "GLOBEX",
93
+ :currency => "USD",
94
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:future],
95
+ :multiplier => 12500000,
96
+ :description => "Japanese Yen"
97
+ }),
98
+ :hsi => Datatypes::Contract.new({
99
+ :symbol => "HSI",
100
+ :expiry => self.next_expiry(Time.now),
101
+ :exchange => "HKFE",
102
+ :currency => "HKD",
103
+ :sec_type => Datatypes::Contract::SECURITY_TYPES[:future],
104
+ :multiplier => 50,
105
+ :description => "Hang Seng Index"
106
+ })
107
+ }
108
+ end
109
+ end
data/lib/ib-ruby.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'version'
2
+
3
+ module IbRuby
4
+ end # module IbRuby
5
+
6
+ require 'ib-ruby/datatypes'
7
+ require 'ib-ruby/ib'
8
+ require 'ib-ruby/messages'
9
+ require 'ib-ruby/symbols/forex'
10
+ require 'ib-ruby/symbols/futures'
data/lib/version.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'pathname'
2
+
3
+ module IbRuby
4
+
5
+ VERSION_FILE = Pathname.new(__FILE__).dirname + '../VERSION' # :nodoc:
6
+ VERSION = VERSION_FILE.exist? ? VERSION_FILE.read.strip : nil
7
+
8
+ end
@@ -0,0 +1,131 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe IB::Datatypes::Contract do
4
+
5
+ it 'instantiates without options' do
6
+ x = IB::Datatypes::Contract.new
7
+ x.should_not be_nil
8
+ end
9
+
10
+ it 'allows setting attributes' do
11
+ expect {
12
+ x = IB::Datatypes::Contract.new
13
+ x.symbol = "TEST"
14
+ x.sec_type = IB::Datatypes::Contract::SECURITY_TYPES[:stock]
15
+ x.expiry = 200609
16
+ x.strike = 1234
17
+ x.right = "put"
18
+ x.multiplier = 123
19
+ x.exchange = "SMART"
20
+ x.currency = "USD"
21
+ x.local_symbol = "baz"
22
+ }.to_not raise_error
23
+ end
24
+
25
+ it 'raises on wrong security type' do
26
+ expect {
27
+ x = IB::Datatypes::Contract.new({:sec_type => "asdf"})
28
+ }.to raise_error ArgumentError
29
+
30
+ expect {
31
+ x = IB::Datatypes::Contract.new
32
+ x.sec_type = "asdf"
33
+ }.to raise_error ArgumentError
34
+
35
+ end
36
+
37
+ it 'accepts pre-determined security types' do
38
+ IB::Datatypes::Contract::SECURITY_TYPES.values.each do |type|
39
+ expect {
40
+ x = IB::Datatypes::Contract.new({:sec_type => type})
41
+ }.to_not raise_error
42
+
43
+ expect {
44
+ x = IB::Datatypes::Contract.new
45
+ x.sec_type = type
46
+ }.to_not raise_error
47
+ end
48
+ end
49
+
50
+ it 'raises on wrong expiry' do
51
+ expect {
52
+ x = IB::Datatypes::Contract.new({:expiry => "foo"})
53
+ }.to raise_error ArgumentError
54
+
55
+ expect {
56
+ x = IB::Datatypes::Contract.new
57
+ x.expiry = "foo"
58
+ }.to raise_error ArgumentError
59
+ end
60
+
61
+ it 'accepts correct expiry' do
62
+ expect {
63
+ x = IB::Datatypes::Contract.new({:expiry => "200607"})
64
+ }.to_not raise_error
65
+
66
+ expect {
67
+ x = IB::Datatypes::Contract.new
68
+ x.expiry = "200607"
69
+ }.to_not raise_error
70
+
71
+ expect {
72
+ x = IB::Datatypes::Contract.new({:expiry => 200607})
73
+ }.to_not raise_error
74
+
75
+ expect {
76
+ x = IB::Datatypes::Contract.new
77
+ x.expiry = 200607
78
+ x.expiry.should == "200607" # converted to a string
79
+ }.to_not raise_error
80
+
81
+ end
82
+
83
+ it 'raises on incorrect right (option type)' do
84
+ expect {
85
+ x = IB::Datatypes::Contract.new({:right => "foo"})
86
+ }.to raise_error ArgumentError
87
+ expect {
88
+ x = IB::Datatypes::Contract.new
89
+ x.right = "foo"
90
+ }.to raise_error ArgumentError
91
+ end
92
+
93
+ it 'accepts all correct values for right (option type)' do
94
+ ["PUT", "put", "P", "p", "CALL", "call", "C", "c"].each do |right|
95
+ expect {
96
+ x = IB::Datatypes::Contract.new({:right => right})
97
+ }.to_not raise_error
98
+
99
+ expect {
100
+ x = IB::Datatypes::Contract.new
101
+ x.right = right
102
+ }.to_not raise_error
103
+ end
104
+ end
105
+
106
+ context "serialization" do
107
+ let(:stock) do
108
+ stock = IB::Datatypes::Contract.new
109
+ stock.symbol = "TEST"
110
+ stock.sec_type = IB::Datatypes::Contract::SECURITY_TYPES[:stock]
111
+ stock.expiry = 200609
112
+ stock.strike = 1234
113
+ stock.right = "put"
114
+ stock.multiplier = 123
115
+ stock.exchange = "SMART"
116
+ stock.currency = "USD"
117
+ stock.local_symbol = "baz"
118
+ stock
119
+ end
120
+
121
+ it "serializes long" do
122
+ stock.serialize_long(20).should ==
123
+ ["TEST", IB::Datatypes::Contract::SECURITY_TYPES[:stock], "200609", 1234, "PUT", 123, "SMART", nil, "USD", "baz"]
124
+ end
125
+
126
+ it "serializes short" do
127
+ stock.serialize_short(20).should ==
128
+ ["TEST", IB::Datatypes::Contract::SECURITY_TYPES[:stock], "200609", 1234, "PUT", 123, "SMART", "USD", "baz"]
129
+ end
130
+ end #serialization
131
+ end # describe IB::Datatypes::Contract
@@ -0,0 +1,11 @@
1
+ require File.expand_path(
2
+ File.join(File.dirname(__FILE__), %w[.. lib ib-ruby]))
3
+
4
+ RSpec.configure do |config|
5
+ # config.exclusion_filter = { :slow => true }
6
+ # config.filter = { :focus => true }
7
+ # config.include(UserExampleHelpers)
8
+ # config.mock_with :mocha
9
+ # config.mock_with :flexmock
10
+ # config.mock_with :rr
11
+ end
data/tasks/common.rake ADDED
@@ -0,0 +1,18 @@
1
+ #task :default => 'test:run'
2
+ #task 'gem:release' => 'test:run'
3
+
4
+ task :notes do
5
+ puts 'Output annotations (TBD)'
6
+ end
7
+
8
+ #Bundler not ready for prime time just yet
9
+ #desc 'Bundle dependencies'
10
+ #task :bundle do
11
+ # output = `bundle check 2>&1`
12
+ #
13
+ # unless $?.to_i == 0
14
+ # puts output
15
+ # system "bundle install"
16
+ # puts
17
+ # end
18
+ #end
data/tasks/doc.rake ADDED
@@ -0,0 +1,14 @@
1
+ desc 'Alias to doc:rdoc'
2
+ task :doc => 'doc:rdoc'
3
+
4
+ namespace :doc do
5
+ require 'rake/rdoctask'
6
+ Rake::RDocTask.new do |rdoc|
7
+ # Rake::RDocTask.new(:rdoc => "rdoc", :clobber_rdoc => "clobber", :rerdoc => "rerdoc") do |rdoc|
8
+ rdoc.rdoc_dir = DOC_PATH.basename.to_s
9
+ rdoc.title = "#{NAME} #{CLASS_NAME::VERSION} Documentation"
10
+ rdoc.main = "README.doc"
11
+ rdoc.rdoc_files.include('README*')
12
+ rdoc.rdoc_files.include('lib/**/*.rb')
13
+ end
14
+ end
data/tasks/gem.rake ADDED
@@ -0,0 +1,40 @@
1
+ desc "Alias to gem:release"
2
+ task :release => 'gem:release'
3
+
4
+ desc "Alias to gem:install"
5
+ task :install => 'gem:install'
6
+
7
+ desc "Alias to gem:build"
8
+ task :gem => 'gem:build'
9
+
10
+ namespace :gem do
11
+ gem_file = "#{NAME}-#{CLASS_NAME::VERSION}.gem"
12
+
13
+ desc "(Re-)Build gem"
14
+ task :build do
15
+ puts "Remove existing gem package"
16
+ rm_rf PKG_PATH
17
+ puts "Build new gem package"
18
+ system "gem build #{NAME}.gemspec"
19
+ puts "Move built gem to package dir"
20
+ mkdir_p PKG_PATH
21
+ mv gem_file, PKG_PATH
22
+ end
23
+
24
+ desc "Cleanup already installed gem(s)"
25
+ task :cleanup do
26
+ puts "Cleaning up installed gem(s)"
27
+ system "gem cleanup #{NAME}"
28
+ end
29
+
30
+ desc "Build and install gem"
31
+ task :install => :build do
32
+ system "gem install #{PKG_PATH}/#{gem_file}"
33
+ end
34
+
35
+ desc "Build and push gem to Gemcutter"
36
+ task :release => [:build, 'git:tag'] do
37
+ puts "Pushing gem to Gemcutter"
38
+ system "gem push #{PKG_PATH}/#{gem_file}"
39
+ end
40
+ end
data/tasks/git.rake ADDED
@@ -0,0 +1,34 @@
1
+ desc "Alias to git:commit"
2
+ task :git => 'git:commit'
3
+
4
+ namespace :git do
5
+
6
+ desc "Stage and commit your work [with message]"
7
+ task :commit, [:message] do |t, args|
8
+ puts "Staging new (unversioned) files"
9
+ system "git add --all"
10
+ if args.message
11
+ puts "Committing with message: #{args.message}"
12
+ system %Q[git commit -a -m "#{args.message}" --author arvicco]
13
+ else
14
+ puts "Committing"
15
+ system %Q[git commit -a -m "No message" --author arvicco]
16
+ end
17
+ end
18
+
19
+ desc "Push local changes to Github"
20
+ task :push => :commit do
21
+ puts "Pushing local changes to remote"
22
+ system "git push"
23
+ end
24
+
25
+ desc "Create (release) tag on Github"
26
+ task :tag => :push do
27
+ tag = CLASS_NAME::VERSION
28
+ puts "Creating git tag: #{tag}"
29
+ system %Q{git tag -a -m "Release tag #{tag}" #{tag}}
30
+ puts "Pushing #{tag} to remote"
31
+ system "git push origin #{tag}"
32
+ end
33
+
34
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,15 @@
1
+ desc 'Alias to spec:spec'
2
+ task :spec => 'spec:spec'
3
+
4
+ namespace :spec do
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run all specs"
8
+ RSpec::Core::RakeTask.new(:spec) { |task|}
9
+
10
+ desc "Run specs with RCov"
11
+ RSpec::Core::RakeTask.new(:rcov) do |task|
12
+ task.rcov = true
13
+ task.rcov_opts = ['--exclude', 'spec']
14
+ end
15
+ end
@@ -0,0 +1,71 @@
1
+ class Version
2
+ attr_accessor :major, :minor, :patch, :build
3
+
4
+ def initialize(version_string)
5
+ raise "Invalid version #{version_string}" unless version_string =~ /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/
6
+ @major = $1.to_i
7
+ @minor = $2.to_i
8
+ @patch = $3.to_i
9
+ @build = $4
10
+ end
11
+
12
+ def bump_major(x)
13
+ @major += x.to_i
14
+ @minor = 0
15
+ @patch = 0
16
+ @build = nil
17
+ end
18
+
19
+ def bump_minor(x)
20
+ @minor += x.to_i
21
+ @patch = 0
22
+ @build = nil
23
+ end
24
+
25
+ def bump_patch(x)
26
+ @patch += x.to_i
27
+ @build = nil
28
+ end
29
+
30
+ def update(major, minor, patch, build=nil)
31
+ @major = major
32
+ @minor = minor
33
+ @patch = patch
34
+ @build = build
35
+ end
36
+
37
+ def write(desc = nil)
38
+ CLASS_NAME::VERSION_FILE.open('w') { |file| file.puts to_s }
39
+ (BASE_PATH + 'HISTORY').open('a') do |file|
40
+ file.puts "\n== #{to_s} / #{Time.now.strftime '%Y-%m-%d'}\n"
41
+ file.puts "\n* #{desc}\n" if desc
42
+ end
43
+ end
44
+
45
+ def to_s
46
+ [major, minor, patch, build].compact.join('.')
47
+ end
48
+ end
49
+
50
+ desc 'Set version: [x.y.z] - explicitly, [1/10/100] - bump major/minor/patch, [.build] - build'
51
+ task :version, [:command, :desc] do |t, args|
52
+ version = Version.new(CLASS_NAME::VERSION)
53
+ case args.command
54
+ when /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/ # Set version explicitly
55
+ version.update($1, $2, $3, $4)
56
+ when /^\.(.*?)$/ # Set build
57
+ version.build = $1
58
+ when /^(\d{1})$/ # Bump patch
59
+ version.bump_patch $1
60
+ when /^(\d{1})0$/ # Bump minor
61
+ version.bump_minor $1
62
+ when /^(\d{1})00$/ # Bump major
63
+ version.bump_major $1
64
+ else # Unknown command, just display VERSION
65
+ puts "#{NAME} #{version}"
66
+ next
67
+ end
68
+
69
+ puts "Writing version #{version} to VERSION file"
70
+ version.write args.desc
71
+ end