ib-ruby 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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