tradingrobotdsl 0.0.1

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.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.0.1 / 2007-11-18
2
+
3
+ * Birthday!
4
+ * order placement doesn't work, only quotes
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/dsl.rb
6
+ spec/contract_spec.rb
7
+ spec/helper.rb
8
+ spec/robot_spec.rb
data/README.txt ADDED
@@ -0,0 +1,79 @@
1
+ tradingrobotdsl: Domain specific language for automated trading (robot)
2
+ Copyright (C) 2007 by Timur Adigamov (timur at adigamov dot com)
3
+ http://www.tradeindexfuture.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Domain specific language library for programming trading robots.
8
+ Quotes get from opentick server (through opentick-ruby module)
9
+ Order place to Trader Workstation socket API (through ib-ruby module)
10
+
11
+ This is an alpha test quality release. I specifically DO NOT recommend
12
+ that it be used for live trading under any circumstances. It has not
13
+ been tested. I am releasing it in the hopes that the many eyes of the
14
+ community will help with enhancing it, so that we all will have a
15
+ robust and reliable library to use.
16
+
17
+ == FEATURES/PROBLEMS:
18
+
19
+ * get quotes from opentick servers
20
+ * simple average indicator realized :)
21
+ * order placement (buy/sell) doesn't work
22
+
23
+ == SYNOPSIS:
24
+
25
+ robot do
26
+ login 'test_opentick', '123123'
27
+ history :duration => 300, :from => Time.now-10*24*3600, :to => Time.now
28
+ query MSFT do
29
+ if (avg MSFT, 9)/(avg MSFT, 25) - 1 > 0.5
30
+ puts "buy MSFT"
31
+ end
32
+ if (avg MSFT, 9)/(avg MSFT, 25) - 1 < - 0.5
33
+ puts "sell MSFT"
34
+ end
35
+ end # query
36
+ end # robot
37
+
38
+ == REQUIREMENTS:
39
+
40
+ * ib-ruby
41
+ * opentick-ruby
42
+
43
+ == INSTALL:
44
+
45
+ * sudo gem install ib-ruby
46
+ * sudo gem install opentick-ruby
47
+ * sudo gem install tradingrobotdsl
48
+
49
+ == LICENSE:
50
+
51
+ This library is free software; you can redistribute it and/or modify
52
+ it under the terms of the GNU Lesser General Public License as
53
+ published by the Free Software Foundation; either version 2.1 of the
54
+ License, or (at your option) any later version.
55
+
56
+ This library is distributed in the hope that it will be useful, but
57
+ WITHOUT ANY WARRANTY; without even the implied warranty of
58
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
59
+ Lesser General Public License for more details.
60
+
61
+ You should have received a copy of the GNU Lesser General Public
62
+ License along with this library; if not, write to the Free Software
63
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
64
+ 02110-1301 USA
65
+
66
+
67
+ == Use At Your Own Risk
68
+
69
+ As the license indicates, this code is provided AS IS, WITH NO
70
+ WARRANTY WHATSOEVER, not even an implied warranty of merchantability
71
+ or fitness for a particular purpose.
72
+
73
+ *ANY USE YOU MAKE OF THIS CODE IS ENTIRELY AT YOUR OWN RISK.*
74
+
75
+ This code may contain any number of errors or bugs, both known and
76
+ unknown. Use of this code may result in monetary loss due to known or
77
+ unknown bugs and errors. In no event shall the author be liable or
78
+ responsible for any loss whatsoever, direct or indirect, that may
79
+ occur as a result of your use of this code.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/dsl.rb'
6
+
7
+ Hoe.new('tradingrobotdsl', DSL::VERSION) do |p|
8
+ p.rubyforge_name = 'tradingrobotdsl'
9
+ p.author = 'Timur Adigamov'
10
+ p.email = 'timur at adigamov dot com'
11
+ p.summary = 'Domain specific language for automated trading (robots)'
12
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
+ p.url = "http://www.tradeindexfuture.com"
14
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
+ p.remote_rdoc_dir = ''
16
+ end
17
+
18
+ # vim: syntax=Ruby
data/lib/dsl.rb ADDED
@@ -0,0 +1,173 @@
1
+ =begin
2
+ Trading robot Domain specific language implementation
3
+ =end
4
+
5
+ $:.push(File.dirname(__FILE__) + "/../../opentick-ruby/lib/")
6
+
7
+ require 'opentick.rb'
8
+ require 'thread'
9
+ #add_stubs(@user, :password= => nil, :password_confirmation= => nil, :new_password= => nil)
10
+ module DSL
11
+
12
+ VERSION='0.0.1'
13
+ class NoDataException < RuntimeError
14
+ attr :deep_level
15
+ def initialize(deep_level)
16
+ @deep_level = deep_level
17
+ end
18
+ end # class NoDataException
19
+
20
+ class Contract
21
+ @@list_by_name={}
22
+ @@list_by_id=[]
23
+ attr_reader :name, :exchange, :queue
24
+ attr_accessor :request_id
25
+
26
+ def initialize(contract_name, exchange=nil)
27
+ @name = contract_name
28
+ @exchange = exchange
29
+ @request_id = 0
30
+ @queue = []
31
+ end
32
+
33
+ def self.list_by_name
34
+ @@list_by_name
35
+ end
36
+
37
+ def self.list_by_id
38
+ @@list_by_id
39
+ end
40
+
41
+ def self.find(contract_name)
42
+ @@list_by_name[contract_name]=Contract.new(contract_name) unless @@list_by_name.key?(contract_name)
43
+ @@list_by_name[contract_name]
44
+ end
45
+
46
+ def method_missing(m, *args)
47
+ @exchange = m
48
+ self
49
+ end
50
+
51
+ def price
52
+ @queue.first[:closePrice] unless @queue.first.nil?
53
+ end
54
+
55
+ def subscribe_history(ot, robot)
56
+ msg = OpenTick::OutgoingMessages::HistoryStreamRequest.new(:exchange => @exchange.to_s,
57
+ :ticker => @name.to_s, :from => robot.from.to_i, :to => robot.to.to_i, :type => 4, :interval => 1)
58
+ ot.dispatch(msg)
59
+ @request_id = msg.reqId
60
+ @@list_by_id[@request_id] = self
61
+ end
62
+ end # class Contract
63
+
64
+ ############################### class Robot ##################################
65
+
66
+ class Robot
67
+ attr_accessor :duration, :from, :to, :deep
68
+
69
+ def initialize(opentick_connection)
70
+ @ot = opentick_connection
71
+ @logged = false
72
+ @mutex = Mutex.new
73
+ @condition = ConditionVariable.new
74
+ @deep = 1
75
+ end
76
+
77
+ def login(username, password)
78
+ @ot.login(username, password)
79
+ @logged = true
80
+ end # login
81
+
82
+ def login?
83
+ @logged
84
+ end
85
+
86
+ def history(options)
87
+ raise "parameters absence" unless options.key?(:duration) && options.key?(:from) && options.key?(:to)
88
+ raise "duration > to - from" unless options[:duration] < options[:to] - options[:from]
89
+ @duration = options[:duration]
90
+ @from = options[:from]
91
+ @to = options[:to]
92
+ class << self
93
+ alias_method :query, :query_history
94
+ end
95
+ DSL::Contract.class_eval {
96
+ alias_method :subscribe, :subscribe_history
97
+ }
98
+ end # history
99
+
100
+ def on_history_quote(msg)
101
+ contract = Contract.list_by_id[msg.reqId]
102
+ cond =Contract.list_by_name.values.inject(true) {|s,x| s &&= (x.queue.size >= @deep)}
103
+ @mutex.synchronize do
104
+ msg.data.each {|q|
105
+ if q[:type]==0 && q[:time].nil?
106
+ contract.request_id = 0
107
+ else
108
+ contract.queue.push q
109
+ end
110
+ }
111
+ @condition.signal if !cond && Contract.list_by_name.values.inject(true) {|s,x| s &&= (x.queue.size >= @deep)}
112
+ end
113
+ end
114
+
115
+ def query_history(*args)
116
+ @ot.subscribe(OpenTick::IncomingMessages::HistoryStreamResponse, lambda {|msg| on_history_quote(msg)})
117
+ args.each do |contract|
118
+ contract.subscribe(@ot, self)
119
+ end
120
+ begin
121
+ while (Contract.list_by_name.values.inject(0) {|s,x| s+=x.request_id } > 0) ||
122
+ (args.inject(true) {|s,x| s &&= (x.queue.size >= @deep)}) do
123
+ @mutex.synchronize do
124
+ begin
125
+ @condition.wait(@mutex) unless args.inject(true) {|s,x| s &&= (x.queue.size >= @deep)}
126
+ yield
127
+ rescue NoDataException => e
128
+ @deep = e.deep_level
129
+ retry
130
+ end
131
+ args.each {|contract|
132
+ contract.queue.shift
133
+ }
134
+ end # synchronize
135
+ end # while
136
+ end # begin
137
+ end # query_history
138
+
139
+ def avg(contract, period)
140
+ raise unless contract.is_a?(DSL::Contract)
141
+ raise unless period.is_a?(Fixnum)
142
+ raise NoDataException.new(period) if @deep < period
143
+ contract.queue[0..period-1].inject(0) {|s,x| s+=x[:closePrice]} / period
144
+ end
145
+ end # class Robot
146
+
147
+ end # module DSL
148
+
149
+ ############################### module Kernel ################################
150
+
151
+ module Kernel
152
+ # change context
153
+ def robot(*args, &blk)
154
+ DSL::Robot.new(*args).instance_eval(&blk)
155
+ end
156
+ end # Kernel
157
+
158
+ ############################### class Object #################################
159
+
160
+ def Object.const_missing(m)
161
+ DSL::Contract::find(m)
162
+ end # Object.const_missing
163
+
164
+ if $0 == __FILE__
165
+ robot OpenTick::OpenTick.new("feed1.opentick.com", 10010) do
166
+ login 'test_opentick', '123123'
167
+ history :duration => 300, :from => Time.now-10*24*3600, :to => Time.now
168
+ query GOOG.Q,CSCO.Q do
169
+ p avg GOOG.Q, 3
170
+ p avg CSCO.Q, 3
171
+ end # query
172
+ end # robot
173
+ end
@@ -0,0 +1,59 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)),"helper")
2
+ require "#{LIB_DIR}/dsl"
3
+
4
+ describe DSL::Contract, "before created" do
5
+ it "contract list_by_name should be empty" do
6
+ DSL::Contract.list_by_name.should be_empty
7
+ end
8
+ it "contract list_by_id should be empty" do
9
+ DSL::Contract.list_by_id.should be_empty
10
+ end
11
+ end
12
+
13
+ describe DSL::Contract, "when created" do
14
+ before :each do
15
+ @contract = robot(nil) do
16
+ MSFT.Q
17
+ end
18
+ end
19
+
20
+ it "should exists" do
21
+ @contract.should_not be_nil
22
+ end
23
+
24
+ it "should be instance of DSL::Contract" do
25
+ @contract.should be_an_instance_of(DSL::Contract)
26
+ end
27
+
28
+ it "should set contract name" do
29
+ @contract.name.should eql(:MSFT)
30
+ end
31
+
32
+ it "should set exchange" do
33
+ @contract.exchange.should eql(:Q)
34
+ end
35
+
36
+ it "should not have subscribe method alias" do
37
+ pending "ror2ru question" do
38
+ @contract.public_methods.should_not include("subscribe")
39
+ end
40
+ end
41
+
42
+ after :each do
43
+ DSL::Contract.list_by_name.clear
44
+ DSL::Contract.list_by_id.clear
45
+ end
46
+ end
47
+
48
+ describe DSL::Contract, "in history mode" do
49
+ it "should create method alias subscribe->subscribe_query" do
50
+ @robot = DSL::Robot.new(nil)
51
+ @robot.history :duration => 1, :from => Time.now - 2, :to => Time.now
52
+ DSL::Contract.new("MSFT").public_methods.should include("subscribe")
53
+ end
54
+
55
+ it "should subscribe to history quotes" do
56
+ @robot = DSL::Robot.new(nil)
57
+ @ot = mock("OpenTick")
58
+ end
59
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "rubygems"
2
+ require "spec"
3
+
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)), *%w[.. lib])
@@ -0,0 +1,102 @@
1
+ # robot_spec.rb
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)),"helper")
4
+ require "#{LIB_DIR}/dsl"
5
+
6
+ describe Kernel, "robot method" do
7
+ it "should change context" do
8
+ robot(nil) {self}.should be_a_kind_of(DSL::Robot)
9
+ end
10
+ end # robot method
11
+
12
+ describe DSL::Robot, "when created" do
13
+ before :each do
14
+ @ot = mock("OpenTick")
15
+ @robot = DSL::Robot.new(@ot)
16
+ end
17
+
18
+ it "should exists" do
19
+ @robot.should_not be_nil
20
+ end
21
+
22
+ it "should be instance of DSL::Robot" do
23
+ @robot.should be_an_instance_of(DSL::Robot)
24
+ end
25
+
26
+ it "should not be logged in" do
27
+ @robot.login?.should be_false
28
+ end
29
+
30
+ it "should not have query method alias" do
31
+ @robot.public_methods.should_not include("query")
32
+ end
33
+
34
+ it "should connect to opentick server on login" do
35
+ @ot.should_receive(:login).
36
+ once.
37
+ with(an_instance_of(String), an_instance_of(String))
38
+
39
+ @robot.login("test_opentick", "123123")
40
+
41
+ @robot.login?.should be_true
42
+ end
43
+ end # when created
44
+
45
+ describe DSL::Robot, "in history mode" do
46
+ before :each do
47
+ @robot = DSL::Robot.new(nil)
48
+ end
49
+
50
+ it "should set @duration, @from and @to" do
51
+ now = Time.now
52
+ @robot.history :duration => 300, :from => now-24*3600, :to => now
53
+ @robot.duration.should eql(300)
54
+ @robot.from.should eql(now-24*3600)
55
+ @robot.to.should eql(now)
56
+ end
57
+
58
+ it "should raise exception on parameter absence" do
59
+ lambda { @robot.history({}) }.should raise_error
60
+ end
61
+
62
+ it "should check @to - @from > @duration" do
63
+ lambda {@robot.history :duration => 300, :from => 1, :to => 2 }.should raise_error
64
+ end
65
+
66
+ it "should create method alias query->query_history" do
67
+ @robot.history :duration => 300, :from => Time.now-24*3600, :to => Time.now
68
+ @robot.public_methods.should include("query")
69
+ end
70
+ end # in history mode
71
+
72
+ describe DSL::Robot, "on query command" do
73
+ before :each do
74
+ @ot = mock("OpenTick")
75
+ @contract = mock("DSL::Contract")
76
+ @robot = robot(@ot) do
77
+ history :duration => 1, :from => 2, :to => 5
78
+ query @contract do
79
+ end
80
+ end
81
+ end
82
+
83
+ it "should subscribe history quotes" do
84
+ @ot.should_receive(:subscribe).once
85
+ #with(OpenTick::IncomingMessages::HistoryStreamResponse, an_instance_of(Proc))
86
+ end
87
+
88
+ it "should send subscribe message to contracts" do
89
+ @contract.should_receive(:subscribe)
90
+ #once
91
+ #with(an_instance_of(OpenTick::IncomingMessages::HistoryStreamResponse), an_instance_of(Proc))
92
+ end
93
+
94
+ after :each do
95
+ DSL::Contract.list_by_name.clear
96
+ DSL::Contract.list_by_id.clear
97
+ end
98
+ end # on query command
99
+
100
+
101
+
102
+
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: tradingrobotdsl
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2007-11-19 00:00:00 +03:00
8
+ summary: Domain specific language for automated trading (robots)
9
+ require_paths:
10
+ - lib
11
+ email: timur at adigamov dot com
12
+ homepage: http://www.tradeindexfuture.com
13
+ rubyforge_project: tradingrobotdsl
14
+ description: "This is an alpha test quality release. I specifically DO NOT recommend that it be used for live trading under any circumstances. It has not been tested. I am releasing it in the hopes that the many eyes of the community will help with enhancing it, so that we all will have a robust and reliable library to use. == FEATURES/PROBLEMS: * get quotes from opentick servers * simple average indicator realized :) * order placement (buy/sell) doesn't work == SYNOPSIS: robot do login 'test_opentick', '123123' history :duration => 300, :from => Time.now-10*24*3600, :to => Time.now query MSFT do if (avg MSFT, 9)/(avg MSFT, 25) - 1 > 0.5 puts \"buy MSFT\" end if (avg MSFT, 9)/(avg MSFT, 25) - 1 < - 0.5 puts \"sell MSFT\" end end # query end # robot"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Timur Adigamov
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - lib/dsl.rb
37
+ - spec/contract_spec.rb
38
+ - spec/helper.rb
39
+ - spec/robot_spec.rb
40
+ test_files: []
41
+
42
+ rdoc_options:
43
+ - --main
44
+ - README.txt
45
+ extra_rdoc_files:
46
+ - History.txt
47
+ - Manifest.txt
48
+ - README.txt
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ requirements: []
54
+
55
+ dependencies:
56
+ - !ruby/object:Gem::Dependency
57
+ name: hoe
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Version::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.3.0
64
+ version: