tradingrobotdsl 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/History.txt +11 -0
  2. data/lib/dsl.rb +426 -156
  3. data/spec/contract_spec.rb +42 -42
  4. data/spec/robot_spec.rb +77 -77
  5. metadata +2 -2
data/History.txt CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.0.2 / 2007-11-22
2
+ * New features
3
+ * Plugin system for smarting
4
+ * IB online and history quotes plugins
5
+ * IB order placement plugin
6
+ * Techical analysis plugin new functions: SMA and EMA
7
+ * Bug fixes
8
+ * OpenTick plugins temporary removed for testing
9
+
10
+ * Bug fixes
11
+ * duration command
1
12
  == 0.0.1 / 2007-11-18
2
13
 
3
14
  * Birthday!
data/lib/dsl.rb CHANGED
@@ -1,173 +1,443 @@
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)
1
+
2
+ =begin
3
+ Trading robot Domain specific language implementation
4
+ =end
5
+
6
+
7
+ require 'thread'
8
+
10
9
  module DSL
11
10
 
12
- VERSION='0.0.1'
11
+ VERSION='0.0.2'
12
+
13
13
  class NoDataException < RuntimeError
14
14
  attr :deep_level
15
15
  def initialize(deep_level)
16
16
  @deep_level = deep_level
17
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
18
+ end # class NoDataException
19
+
20
+ ############################### class Plugin ##$################################
21
+
22
+ module Plugin
23
+ def initialize(robot)
24
+ @robot = robot
25
+ @connection = nil
26
+ self.class.message.each do |name, command|
27
+ if name == :input
28
+ command.each do |name, command|
29
+ @robot.register_command(name, lambda {|*args| self.send(command, *args)}, self)
30
+ end
31
+ else
32
+ @robot.register_command(name, lambda {|*args| self.send(command, *args)})
33
+ end
34
+ end
35
+ end
36
+
37
+ def run(hash)
38
+ raise "you must define own run method in subclasses"
39
+ end
40
+
41
+ def parse_params(hash)
42
+ hash.each do |param|
43
+ self.instance_variable_set "@" + param[0].to_s, param[1]
44
+ end
45
+ end
46
+ end # module Plugin
47
+
48
+ module IBPluginFunctions
49
+ # make structure Contract for ib-ruby module
50
+ def make_contract(contract)
51
+ IB::Datatypes::Contract.new(:symbol => contract.symbol.to_s, :exchange => contract.exchange.to_s,
52
+ :expiry => contract.expiry, :currency => contract.currency, :sec_type => contract.sec_type)
53
+ end
54
+ end
55
+
56
+ class IBHistoryPlugin
57
+ include Plugin
58
+ include IBPluginFunctions
59
+ @@ticker_id = 1
60
+
61
+ def self.message
62
+ {
63
+ :input => { :history => :run }
64
+ }
65
+ end
66
+
67
+ def run(*args)
68
+ hash=args[0]
69
+ $:.push(File.dirname(__FILE__) + "/../../ib-ruby/lib/")
70
+ require 'ib'
71
+ require 'datatypes'
72
+ require 'messages'
73
+ require 'timeout'
74
+
75
+ @duration = 0
76
+ parse_params(hash)
77
+ @id = []
78
+ @connection = IB::IB.new({:client_id => "quotes"})
79
+ #@connection.dispatch(IB::OutgoingMessages::SetServerLoglevel.new(:loglevel => 5))
80
+ @connection.subscribe(IB::IncomingMessages::HistoricalData, lambda {|msg| on_quote(msg)})
81
+ @mutex = Mutex.new
82
+ @condition = ConditionVariable.new
83
+ end # run
84
+
85
+ def subscribe_contract(contract)
86
+ ib_contract = make_contract(contract)
87
+ msg = IB::OutgoingMessages::RequestHistoricalData.new(:ticker_id => @@ticker_id += 1,
88
+ :contract => ib_contract, :end_date_time => @end, :duration => @duration,
89
+ :bar_size => @size, :what_to_show => @show, :use_RTH => 1, :format_date => 2)
90
+ @id[@@ticker_id] = contract.queue
91
+ @connection.dispatch(msg)
92
+ end # subscribe_contract
93
+
94
+ def query()
95
+ while !@id.inject(true) {|s,x| s &&=x.nil? } do
96
+ #|| (Contract.list_by_value.inject(true) {|s,x| s &&= (x.queue.size >= @deep)})
97
+ @mutex.synchronize do
98
+ begin
99
+ @condition.wait(@mutex) unless @id.inject(true) {|s,x| x == nil ? s : s &&= x.size >= @robot.deep_level}
100
+ yield
101
+ rescue NoDataException => e
102
+ @robot.deep_level = e.deep_level
103
+ retry
104
+ end
105
+ end # synchronize
106
+ end # while
107
+ end
108
+
109
+ def on_quote(msg)
110
+ queue = @id[msg.data[:req_id]]
111
+ cond = @id.inject(true) {|s,x| x == nil ? s : s &&= x.size >= @robot.deep_level}
112
+ @mutex.synchronize do
113
+ msg.data[:history].each do |q|
114
+ queue.push q
115
+ end
116
+ end
117
+ @condition.signal if !cond && @id.inject(true) {|s,x| x == nil ? s : s &&= x.size >= @robot.deep_level}
118
+ end
119
+ end # class IBHistoryPlugin
120
+
121
+ class IBOnlinePlugin
122
+ include Plugin
123
+ include IBPluginFunctions
124
+ @@ticker_id = 1
125
+
126
+ def self.message
127
+ {
128
+ :input => { :online => :run }
129
+ }
130
+ end
131
+
132
+ def run(*args)
133
+ hash=args[0]
134
+ $:.push(File.dirname(__FILE__) + "/../../ib-ruby/lib/")
135
+ require 'ib'
136
+ require 'datatypes'
137
+ require 'messages'
138
+ require 'timeout'
139
+
140
+ @duration = 0
141
+ parse_params(hash)
142
+ @id = []
143
+ @data = []
144
+ @connection = IB::IB.new({:client_id => "quotes"})
145
+ #@connection.dispatch(IB::OutgoingMessages::SetServerLoglevel.new(:loglevel => 5))
146
+ @connection.subscribe(IB::IncomingMessages::TickPrice, lambda {|msg| on_quote(msg)})
147
+ # @connection.subscribe(IB::IncomingMessages::TickSize, lambda {|msg| on_trade(msg)})
148
+ @mutex = Mutex.new
149
+ @condition = ConditionVariable.new
150
+ end
151
+
152
+ def subscribe_contract(contract)
153
+ ib_contract = make_contract(contract)
154
+ msg = IB::OutgoingMessages::RequestMarketData.new(:ticker_id => @@ticker_id += 1,
155
+ :contract => ib_contract)
156
+ @id[@@ticker_id] = contract
157
+ @data[@@ticker_id] = {:open => 0, :high => 0, :low => 1e6, :close => 0}
158
+ @connection.dispatch(msg)
159
+ end
160
+
161
+ def filter
162
+ while true
163
+ begin
164
+ timeout(@duration) do
165
+ # fake loop
166
+ while true do
167
+ end
168
+ end
169
+ rescue TimeoutError
170
+ @id.size.times do |request_id|
171
+ next if @id[request_id].nil?
172
+ @id[request_id].queue.push :open => @data[request_id][:open],
173
+ :high => @data[request_id][:high],
174
+ :low => @data[request_id][:low],
175
+ :close => @data[request_id][:close]
176
+ @data[request_id][:open] = @data[request_id][:high] = @data[request_id][:low] = @data[request_id][:close]
177
+ end # times
178
+ @condition.signal if @id.inject(true) {|s,x| x == nil ? s : s &&= x.queue.size >= @robot.deep_level}
179
+ end # begin
180
+ end # while
181
+ end
182
+
183
+ def on_quote(msg)
184
+ return unless msg.data[:type] == :last
185
+ @mutex.synchronize do
186
+ case @duration
187
+ when 0:
188
+ @id[msg.data[:ticker_id]].queue.push :price => msg.data[:price].to_f, :size => msg.data[:size].to_i
189
+ @condition.signal
190
+ else
191
+ id = msg.data[:ticker_id]
192
+ price = msg.data[:price].to_f
193
+ @data[id][:open] = price if @data[id][:open] == 0
194
+ @data[id][:high] = price if price > @data[id][:high]
195
+ @data[id][:low] = price if price < @data[id][:low]
196
+ @data[id][:close] = price
197
+ end
198
+ end
199
+ # p "#{@id[msg.data[:ticker_id]].symbol.to_s} #{msg.data[:size]} #{msg.data[:price].to_digits}"
200
+ end
201
+
202
+ def query(*args)
203
+ Thread.abort_on_exception = true
204
+ @filter_thread = Thread.new do
205
+ self.filter
206
+ end
207
+ while true
208
+ @mutex.synchronize do
209
+ begin
210
+ @condition.wait(@mutex) unless @id.inject(true) {|s,x| x == nil ? s : s &&= x.queue.size >= @robot.deep_level}
211
+ yield
212
+ rescue NoDataException => e
213
+ @robot.deep_level = e.deep_level
214
+ retry
215
+ end
216
+ end # synchronize
217
+ end # while
218
+ end
219
+
220
+ end # IBOnlinePlugin
221
+
222
+ class IBOrderPlugin
223
+ include Plugin
224
+ include IBPluginFunctions
225
+ @@order_id = 1
226
+
227
+ def self.message
228
+ { :order => :order }
229
+ end
230
+
231
+ def initialize(robot)
232
+ $:.push(File.dirname(__FILE__) + "/../../ib-ruby/lib/")
233
+ require 'ib'
234
+ require 'datatypes'
235
+ require 'messages'
236
+
237
+ super(robot)
238
+ @connection = IB::IB.new({:client_id => "orders"})
239
+ #@connection.dispatch(IB::OutgoingMessages::SetServerLoglevel.new(:loglevel => 5))
240
+ @connection.subscribe(IB::IncomingMessages::NextValidID, lambda {|msg| @@order_id = msg.data[:order_id]})
241
+ @robot.class.const_set "BUY", "BUY"
242
+ @robot.class.const_set "SELL", "SELL"
243
+ @robot.class.const_set "MKT", "MKT"
244
+ @robot.class.const_set "LMT", "LMT"
245
+ end # hash
246
+
247
+ # usage CONTRACT, ORDER_SIDE (BUY, SELL), ORDER_TYPE (MKT, LMT), SIZE, LIMIT_PRICE
248
+ def order(*args)
249
+ contract = args.shift
250
+ side = args.shift
251
+ type = args.shift
252
+ size = args.shift
253
+ order = {
254
+ :id => 1,
255
+ :action => side,
256
+ :order_type => type,
257
+ :total_quantity => size,
258
+ :transmit => "1"
259
+ }
260
+ case type
261
+ when "LMT":
262
+ order[:limit_price] = args.shift
263
+ end
264
+ @connection.dispatch(IB::OutgoingMessages::PlaceOrder.new(
265
+ :order_id => @@order_id += 1,
266
+ :contract => make_contract(contract),
267
+ :order => IB::Datatypes::Order.new(order)
268
+ ))
269
+ end
270
+ end # IBOrderPlugin
271
+
272
+ class TAPlugin
273
+ include Plugin
274
+ def self.message
275
+ { :sma => :sma,
276
+ :ema => :ema
277
+ }
278
+ end
279
+
280
+ def sma(contract, period)
281
+ raise NoDataException.new(period) if @robot.deep_level < period
282
+ (@robot.deep_level-period..@robot.deep_level-1).inject(0) {|s,x| s+=contract.price(x)} / period.to_f
283
+ end # def sma
284
+
285
+ def ema(contract, period, step = @robot.deep_level - period)
286
+ raise NoDataException.new(period) if @robot.deep_level < period
287
+ case period
288
+ when 1
289
+ return contract.queue[step][:close].to_f
290
+ else
291
+ k = 2 / ( 1 + period )
292
+ ema_prev = ema(contract, period - 1, step + 1)
293
+ return ema_prev + k * ( contract.price(step) - ema_prev )
294
+ end
295
+ end # def ema
296
+ end
297
+
298
+ ############################### class Contract ##################################
299
+
300
+ class Contract
301
+ @@list_by_symbol={}
302
+ attr_reader :symbol, :exchange, :expiry, :sec_type, :currency, :queue
303
+ attr_accessor :request_id
304
+
305
+ def initialize(contract_symbol)
306
+ @symbol = contract_symbol
307
+ @queue = []
308
+ end
309
+
310
+ def self.list_by_symbol
311
+ @@list_by_symbol
312
+ end
313
+
314
+ def self.find(contract_symbol)
315
+ @@list_by_symbol[contract_symbol]=Contract.new(contract_symbol) unless @@list_by_symbol.key?(contract_symbol)
316
+ @@list_by_symbol[contract_symbol]
317
+ end
318
+
319
+ def method_missing(m, *args)
320
+ if m.to_s =~ /[A-Z]+/
321
+ @exchange = m unless instance_variable_defined?(:@exchange)
322
+ @sec_type = "STK" unless instance_variable_defined?(:@sec_type) || (args.size > 0 && @sec_type = args.shift)
323
+ @currency = "USD" unless instance_variable_defined?(:@currency) || (args.size > 0 && @currency = args.shift)
324
+ @expiry = "" unless instance_variable_defined?(:@expiry) || (args.size > 0 && @expiry = args.shift)
325
+ self
326
+ else
327
+ super
328
+ end
49
329
  end
50
330
 
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
331
+ def price(lag = 0)
332
+ f = @queue[lag]
333
+ return if f.nil?
334
+ if f.key?(:price)
335
+ f[:price]
336
+ else
337
+ f[:close].to_f #FIXME
338
+ end
339
+ end
340
+
62
341
  end # class Contract
63
342
 
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
343
+ ############################### class Robot ##################################
344
+
345
+ class Robot
346
+ attr_accessor :deep_level
347
+ @@commands = {}
348
+
349
+ def initialize
72
350
  @mutex = Mutex.new
73
351
  @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
352
+ @deep_level = 1
353
+ end
354
+
355
+ def commands
356
+ @@commands
357
+ end
358
+
359
+ def method_missing(m, *args)
360
+ raise "unknown command #{m}" unless @@commands.key?(m)
361
+ @@commands[m].call(*args)
362
+ end
363
+
364
+ # load plugins
365
+ def plugin(*args)
366
+ args.each { |plugin| plugin.new(self) }
367
+ end # plugin
368
+
369
+ # query quotes from input_plugin
370
+ def query(*args)
371
+ args.each do |contract|
372
+ @input_plugin.subscribe_contract(contract)
373
+ end
374
+ @input_plugin.query do
375
+ yield
376
+ args.each {|contract|
377
+ contract.queue.shift
378
+ }
379
+ end
380
+ end
381
+
382
+ # register plugin command in global commands list
383
+ def register_command(command, body, input_plugin=nil)
384
+ @@commands[command] = body
385
+ @input_plugin = input_plugin if input_plugin
386
+ end
387
+ end # class Robot
388
+
389
+ end # module DSL
148
390
 
149
391
  ############################### 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
392
+
393
+ module Kernel
394
+ def robot(&blk)
395
+ DSL::Robot.new.instance_eval(&blk)
396
+ end
397
+ end # Kernel
157
398
 
158
399
  ############################### 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
400
+
401
+ def Object.const_missing(m)
402
+ if m.to_s =~ /[A-Z]+/
403
+ # FIXME workaround
404
+ case m
405
+ when :FUT
406
+ "FUT"
407
+ when :STK
408
+ "STK"
409
+ when :USD
410
+ "USD"
411
+ else
412
+ return DSL::Robot.const_get(m) if DSL::Robot.const_defined?(m)
413
+ DSL::Contract::find(m)
414
+ end
415
+ else
416
+ super
417
+ end
418
+ end # Object.const_missing
419
+
420
+ if $0 == __FILE__
421
+ robot do
422
+
423
+ # loading plugins, first must be plugin for query
424
+ plugin DSL::IBHistoryPlugin, DSL::IBOrderPlugin, DSL::TAPlugin
425
+ # input plugin configuration
426
+ #online :duration => 10
427
+ history :end => "20071101 00:00:00 GMT", :duration => "1 D",
428
+ :size => 10, :show => "TRADES"
429
+ # contract specifications
430
+ YM.ECBOT FUT, USD, 200712
431
+ # main loop
432
+ query YM do
433
+ if (ema YM, 9)/(ema YM, 25) - 1 > 0.05
434
+ order YM, BUY, MKT, 1
435
+ end
436
+ if (ewa YM, 9)/(ewa YM, 25) - 1 < -0.05
437
+ order YM, SELL, MKT, 1
438
+ end
439
+ end
440
+
441
+
442
+ end # robot
443
+ end
@@ -1,32 +1,32 @@
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
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
7
  end
8
8
  it "contract list_by_id should be empty" do
9
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
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
22
  end
23
23
 
24
24
  it "should be instance of DSL::Contract" do
25
- @contract.should be_an_instance_of(DSL::Contract)
25
+ @contract.should be_an_instance_of(DSL::Contract)
26
26
  end
27
-
28
- it "should set contract name" do
29
- @contract.name.should eql(:MSFT)
27
+
28
+ it "should set contract name" do
29
+ @contract.name.should eql(:MSFT)
30
30
  end
31
31
 
32
32
  it "should set exchange" do
@@ -37,23 +37,23 @@ describe DSL::Contract, "when created" do
37
37
  pending "ror2ru question" do
38
38
  @contract.public_methods.should_not include("subscribe")
39
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
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/robot_spec.rb CHANGED
@@ -1,23 +1,23 @@
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
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
21
 
22
22
  it "should be instance of DSL::Robot" do
23
23
  @robot.should be_an_instance_of(DSL::Robot)
@@ -30,59 +30,59 @@ describe DSL::Robot, "when created" do
30
30
  it "should not have query method alias" do
31
31
  @robot.public_methods.should_not include("query")
32
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
-
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
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
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
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))
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
86
  end
87
87
 
88
88
  it "should send subscribe message to contracts" do
@@ -94,9 +94,9 @@ describe DSL::Robot, "on query command" do
94
94
  after :each do
95
95
  DSL::Contract.list_by_name.clear
96
96
  DSL::Contract.list_by_id.clear
97
- end
98
- end # on query command
99
-
100
-
101
-
102
-
97
+ end
98
+ end # on query command
99
+
100
+
101
+
102
+
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: tradingrobotdsl
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-11-19 00:00:00 +03:00
6
+ version: 0.0.2
7
+ date: 2007-11-24 00:00:00 +03:00
8
8
  summary: Domain specific language for automated trading (robots)
9
9
  require_paths:
10
10
  - lib