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 +1 -0
- data/examples/market_maker.rb +28 -6
- data/examples/slammer.rb +58 -0
- data/lib/client.rb +6 -5
- data/lib/order.rb +3 -4
- data/rubytrade.gemspec +1 -1
- data/server/exchange.rb +1 -2
- data/server/order.rb +3 -0
- data/server/public/app.js +43 -10
- data/server/server.rb +13 -4
- metadata +2 -1
data/.gitignore
CHANGED
data/examples/market_maker.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require "ruby-trade"
|
2
2
|
|
3
|
-
TradeAmount =
|
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
|
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[
|
30
|
-
last
|
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
|
-
|
37
|
-
|
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
|
data/examples/slammer.rb
ADDED
@@ -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
|
data/lib/client.rb
CHANGED
@@ -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
|
data/lib/order.rb
CHANGED
@@ -3,17 +3,16 @@ require 'observer'
|
|
3
3
|
class Order
|
4
4
|
include Observable
|
5
5
|
|
6
|
-
attr_reader :id, :local_id, :side, :
|
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
|
-
|
16
|
-
@status = new_status
|
15
|
+
@price = @price.round 2
|
17
16
|
end
|
18
17
|
|
19
18
|
def <=> order
|
data/rubytrade.gemspec
CHANGED
data/server/exchange.rb
CHANGED
data/server/order.rb
CHANGED
data/server/public/app.js
CHANGED
@@ -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
|
-
|
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
|
46
|
-
ticks
|
47
|
-
ticks
|
48
|
-
|
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
|
-
},
|
83
|
+
}, UpdateInterval);
|
51
84
|
});
|
data/server/server.rb
CHANGED
@@ -16,9 +16,11 @@ class OrderServer < EM::Connection
|
|
16
16
|
|
17
17
|
def unbind
|
18
18
|
# cancel the orders for this client
|
19
|
-
@
|
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
|
-
|
108
|
-
|
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.
|
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
|