tradingrobotdsl 0.0.1 → 0.0.2

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.
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