ruby-trade 0.1 → 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.
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