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