ruby-trade 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  # Vim files
2
2
  *.swp
3
+ *.swo
3
4
 
4
5
  # Compiled gem
5
6
  *.gem
@@ -1,8 +1,9 @@
1
1
  require "ruby-trade"
2
2
 
3
- TradeAmount = 50_000
3
+ TradeAmount = 10_000
4
4
  InitialPrice = 10.0
5
5
  Distance = 2.0
6
+ UpdateInterval = 3
6
7
 
7
8
  class Marketmaker
8
9
  include RubyTrade::Client
@@ -13,7 +14,7 @@ class Marketmaker
13
14
  update_orders
14
15
 
15
16
  this = self
16
- EM.add_periodic_timer 5 do
17
+ EM.add_periodic_timer UpdateInterval do
17
18
  this.update_orders
18
19
  end
19
20
  end
@@ -26,15 +27,36 @@ class Marketmaker
26
27
  @buy_order.cancel! if @buy_order
27
28
  @sell_order.cancel! if @sell_order
28
29
 
29
- last = @level1 ? @level1[:last] : InitialPrice
30
- last ||= InitialPrice
30
+ last = @level1 ? @level1["last"] : InitialPrice
31
+ last = InitialPrice if last.nil? or last == 0.0
32
+ puts @level1
33
+ puts last
31
34
 
32
35
  # buy and sell a certain percentage from the last
33
36
  buy_price = last * (1.0 - Distance / 100)
34
37
  sell_price = last * (1.0 + Distance / 100)
35
38
 
36
- @buy_order = buy TradeAmount, at: buy_price
37
- @sell_order = sell TradeAmount, at: sell_price
39
+ puts "Buy: #{buy_price}"
40
+ puts "Sell: #{sell_price}"
41
+
42
+ # randomize sending order - sometimes don't send an order just to mess with
43
+ # folks
44
+ should_send_buy = rand > 0.1
45
+ should_send_sell = rand > 0.1
46
+
47
+ if should_send_buy
48
+ @buy_order = buy TradeAmount, at: buy_price
49
+ else
50
+ puts "Disabling buy"
51
+ @buy_order = nil
52
+ end
53
+
54
+ if should_send_sell
55
+ @sell_order = sell TradeAmount, at: sell_price
56
+ else
57
+ puts "Disabling sell"
58
+ @sell_order = nil
59
+ end
38
60
  end
39
61
 
40
62
  end
@@ -0,0 +1,58 @@
1
+ require 'ruby-trade'
2
+
3
+ TradeAmount = 20_000
4
+ InitialPrice = 10.0
5
+ Distance = 2.0
6
+ MaxDistance = 4
7
+ MinTime = 30
8
+ TimeVariance = 600
9
+
10
+ class Slammer
11
+ include RubyTrade::Client
12
+
13
+ def self.level1; @level1; end
14
+
15
+ def self.on_connect *args
16
+ puts "Connected."
17
+
18
+ setup_next_shot
19
+ end
20
+
21
+ def self.setup_next_shot
22
+ time_gap = MinTime + rand(TimeVariance)
23
+ puts "Firing in #{time_gap} seconds..."
24
+
25
+ this = self
26
+ order = nil
27
+ EM.add_timer time_gap do
28
+ shift = Distance + rand(MaxDistance)
29
+ is_sell = rand < 0.5
30
+ amount = TradeAmount
31
+
32
+ base_price = this.level1 ? this.level1["last"] : InitialPrice
33
+ base_price = InitialPrice if base_price.nil? or base_price == 0.0
34
+
35
+ if is_sell
36
+ price = base_price * (1.0 - shift / 100)
37
+ puts "Selling #{amount} at %.2f" % price
38
+ order = sell amount, at: price
39
+ else
40
+ price = base_price * (1.0 + shift / 100)
41
+ puts "Buying #{amount} at %.2f" % price
42
+ order = buy amount, at: price
43
+ end
44
+
45
+ EM.add_timer 0.1 do
46
+ puts "cancelling"
47
+ order.cancel!
48
+ this.setup_next_shot
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.on_tick level1
54
+ @level1 = level1
55
+ end
56
+ end
57
+
58
+ Slammer.connect_to "127.0.0.1", as: "Slammer", ai: true
@@ -116,6 +116,7 @@ module RubyTrade
116
116
  def handle_message data
117
117
  case data["action"]
118
118
  when "order_accept"
119
+ @orders[data["local_id"]].price = data["price"]
119
120
  when "order_fill"
120
121
  update_account data
121
122
  @@parent.on_fill @orders[data["local_id"]], data["amount"], data["price"]
@@ -136,12 +137,12 @@ module RubyTrade
136
137
  end
137
138
 
138
139
  module Client
139
- def self.on_connect *args; end
140
- def self.on_tick *args; end
141
- def self.on_fill *args; end
142
- def self.on_partial_fill *args; end
143
-
144
140
  module ClassMethods
141
+ def on_connect *args; end
142
+ def on_tick *args; end
143
+ def on_fill *args; end
144
+ def on_partial_fill *args; end
145
+
145
146
  # hook so we can call child methods
146
147
  def child= child
147
148
  @@child = child
@@ -3,17 +3,16 @@ require 'observer'
3
3
  class Order
4
4
  include Observable
5
5
 
6
- attr_reader :id, :local_id, :side, :price, :size, :sent_at, :status
6
+ attr_reader :id, :local_id, :side, :size, :sent_at, :status
7
+ attr_accessor :price, :status
7
8
 
8
9
  def initialize id, side, price, size
9
10
  @id, @side, @price, @size = id, side, price, size
10
11
  @sent_at = Time.now
11
12
  @cancelled = false
12
13
  @status = :pending_accept
13
- end
14
14
 
15
- def status= new_status
16
- @status = new_status
15
+ @price = @price.round 2
17
16
  end
18
17
 
19
18
  def <=> order
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "ruby-trade"
3
- s.version = "0.1"
3
+ s.version = "0.2"
4
4
  s.date = "2013-11-09"
5
5
  s.summary = "A stock market simulation game."
6
6
  s.description = ""
@@ -52,8 +52,7 @@ class Exchange
52
52
  }
53
53
  end
54
54
 
55
- def cancel_order order_id
56
- order = @orders[order_id]
55
+ def cancel_order order
57
56
  @book.cancel_order order if order
58
57
  end
59
58
  end
@@ -11,6 +11,9 @@ class Order
11
11
  @initial_size = @size
12
12
  @sent_at = Time.now
13
13
  @status = :pending_accept
14
+
15
+ # normalize the price to be within cents
16
+ @price = @price.round 2
14
17
  end
15
18
 
16
19
  def <=> order
@@ -2,9 +2,15 @@
2
2
  /*global $, SockJS, console, WebSocket*/
3
3
  $(function () {
4
4
  var plot,
5
+ NumPeriods = 300,
6
+ UpdateInterval = 100,
5
7
  sock = new WebSocket("ws://" + window.location.host + "/ws"),
6
8
  time = 0,
7
- ticks = [[], [], []],
9
+ ticks = {
10
+ bids: [],
11
+ asks: [],
12
+ lasts: []
13
+ },
8
14
  level1 = 0.0;
9
15
 
10
16
  sock.onopen = function () {
@@ -27,13 +33,27 @@ $(function () {
27
33
  console.log("Disconnected from WS server.");
28
34
  };
29
35
 
30
- plot = $.plot("#price-chart", ticks, {
36
+ function getData() {
37
+ return [
38
+ {
39
+ data: ticks.bids,
40
+ label: "Bid"
41
+ },
42
+ {
43
+ data: ticks.asks,
44
+ label: "Ask"
45
+ },
46
+ {
47
+ data: ticks.lasts,
48
+ label: "Last"
49
+ }
50
+ ];
51
+ }
52
+
53
+ plot = $.plot("#price-chart", getData(), {
31
54
  series: {
32
55
  shadowSize: 0
33
56
  },
34
- yaxis: {
35
- min: 0
36
- },
37
57
  xaxis: {
38
58
  show: false
39
59
  }
@@ -42,10 +62,23 @@ $(function () {
42
62
  // Push last, update graph
43
63
  setInterval(function () {
44
64
  time++;
45
- ticks[0].push([time, level1.bid]);
46
- ticks[1].push([time, level1.ask]);
47
- ticks[2].push([time, level1.last]);
48
- plot.setData(ticks);
65
+ ticks.bids.push([time, level1.bid > 0 ? level1.bid : null]);
66
+ ticks.asks.push([time, level1.ask > 0 ? level1.ask : null]);
67
+ ticks.lasts.push([time, level1.last > 0 ? level1.last : null]);
68
+
69
+ // strip off older ones
70
+ while (ticks.bids.length > 0 && ticks.bids[0][0] < time - NumPeriods) {
71
+ ticks.bids.shift();
72
+ }
73
+ while (ticks.asks.length > 0 && ticks.asks[0][0] < time - NumPeriods) {
74
+ ticks.asks.shift();
75
+ }
76
+ while (ticks.lasts.length > 0 && ticks.lasts[0][0] < time - NumPeriods) {
77
+ ticks.lasts.shift();
78
+ }
79
+
80
+ plot.setData(getData());
81
+ plot.setupGrid();
49
82
  plot.draw();
50
- }, 1000);
83
+ }, UpdateInterval);
51
84
  });
@@ -16,9 +16,11 @@ class OrderServer < EM::Connection
16
16
 
17
17
  def unbind
18
18
  # cancel the orders for this client
19
- @my_orders.each do |order|
19
+ puts "Connection closed from #{@account.name}, killing orders"
20
+ @my_orders.values.each do |order|
20
21
  @@exchange.cancel_order order
21
22
  end
23
+ @@parent.tick @@exchange
22
24
  end
23
25
 
24
26
  def update action, *args
@@ -96,7 +98,8 @@ class OrderServer < EM::Connection
96
98
  send_data_f({
97
99
  action: "order_accept",
98
100
  local_id: data["local_id"],
99
- id: order.id
101
+ id: order.id,
102
+ price: order.price
100
103
  }.to_json)
101
104
 
102
105
  @my_orders[order.id] = order
@@ -104,8 +107,14 @@ class OrderServer < EM::Connection
104
107
  @@parent.tick @@exchange
105
108
  end
106
109
  when "cancel_order"
107
- puts "cancelling #{data["id"]}"
108
- @@exchange.cancel_order data["id"]
110
+ order = @my_orders[data["id"]]
111
+ if order
112
+ puts "cancelling #{data["id"]}"
113
+ @@exchange.cancel_order order
114
+ @@parent.tick @@exchange
115
+ else
116
+ puts "No order with ID #{data["id"]}"
117
+ end
109
118
  end
110
119
  end
111
120
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-trade
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -20,6 +20,7 @@ files:
20
20
  - .gitignore
21
21
  - README.md
22
22
  - examples/market_maker.rb
23
+ - examples/slammer.rb
23
24
  - lib/Gemfile
24
25
  - lib/Gemfile.lock
25
26
  - lib/client.rb