rbtc_arbitrage_simple 1.4.4 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +16 -14
- data/Gemfile.lock +17 -21
- data/lib/rbtc_arbitrage.rb +3 -1
- data/lib/rbtc_arbitrage/campbx.rb +3 -4
- data/lib/rbtc_arbitrage/cli.rb +2 -2
- data/lib/rbtc_arbitrage/clients/coinbase_client.rb +3 -2
- data/lib/rbtc_arbitrage/trader.rb +21 -75
- data/lib/rbtc_arbitrage/trader/logger.rb +31 -0
- data/lib/rbtc_arbitrage/trader/notifier.rb +60 -0
- data/lib/rbtc_arbitrage/version.rb +1 -1
- data/rbtc_arbitrage.gemspec +1 -1
- data/spec/clients/coinbase_client_spec.rb +2 -3
- data/spec/rbtc_arbitrage_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/should_raise_SecurityError_if_not_live.yml +30 -25
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/raises_SecurityError_if_not_enough_BTC.yml +38 -24
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/raises_SecurityError_if_not_enough_USD.yml +40 -26
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/should_fetch_balance.yml +30 -25
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/shouldn_t_raise_security_error.yml +72 -53
- data/spec/support/cassettes/RbtcArbitrage_Trader/_fetch_prices/gets_the_right_price_set.yml +60 -50
- data/spec/trader_spec.rb +8 -8
- metadata +10 -19
- data/lib/rbtc_arbitrage/clients/mtgox_client.rb +0 -56
- data/spec/clients/mtgox_client_spec.rb +0 -53
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_balance/fetches_the_balance_correctly.yml +0 -77
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_price_for_buy_correctly.yml +0 -44
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_price_for_sell_correctly.yml +0 -44
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_prices_correctly.yml +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4cfee1b5de308aa0ec6007b8173a7220b4d4501
|
4
|
+
data.tar.gz: b67691a517aa55e2d8fe337f493acc172de76f68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 827f92b92f8728f3802e0a8c341d5f2d0958a3b659841934cd1c15f790ea5dd9ddc1130831af239fa44d1f9e6b06564e680c922894cb12f9a60f2ef69e269aaa
|
7
|
+
data.tar.gz: 303826e5db0cf919370ed927c60930747b99b6d00962d18547a465743a819c8b38d72907d532f22ff783dc1e88c4b66fdcc8f8e4bb03c1147f246c2fbdd36557
|
data/Gemfile
CHANGED
@@ -2,17 +2,19 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in rbtc_arbitrage.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
gem
|
7
|
-
gem "
|
8
|
-
gem
|
9
|
-
gem '
|
10
|
-
|
11
|
-
gem '
|
12
|
-
gem '
|
13
|
-
gem '
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem '
|
17
|
-
gem '
|
18
|
-
gem '
|
5
|
+
group :development do
|
6
|
+
gem 'rspec'
|
7
|
+
gem "activemodel", ">= 3.1"
|
8
|
+
gem "activesupport", ">= 3.1"
|
9
|
+
gem 'guard'
|
10
|
+
gem 'ruby_gntp'
|
11
|
+
# gem 'awesome_print'
|
12
|
+
gem 'guard-rspec'
|
13
|
+
gem 'simplecov'
|
14
|
+
gem 'coveralls', require: false
|
15
|
+
gem "webmock"
|
16
|
+
gem 'vcr'
|
17
|
+
gem 'codeclimate-test-reporter'
|
18
|
+
gem 'hashie'
|
19
|
+
gem 'sinatra'
|
20
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rbtc_arbitrage_simple (
|
4
|
+
rbtc_arbitrage_simple (2.0.1)
|
5
5
|
activemodel (>= 3.1)
|
6
6
|
activesupport (>= 3.1)
|
7
7
|
btce (= 0.2.4)
|
8
8
|
coinbase (= 1.2.6)
|
9
|
-
|
9
|
+
faraday (= 0.8.8)
|
10
10
|
pony
|
11
11
|
thor
|
12
12
|
|
13
13
|
GEM
|
14
14
|
remote: https://rubygems.org/
|
15
15
|
specs:
|
16
|
-
activemodel (4.0.
|
17
|
-
activesupport (= 4.0.
|
16
|
+
activemodel (4.0.4)
|
17
|
+
activesupport (= 4.0.4)
|
18
18
|
builder (~> 3.1.0)
|
19
|
-
activesupport (4.0.
|
20
|
-
i18n (~> 0.6, >= 0.6.
|
19
|
+
activesupport (4.0.4)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
21
|
minitest (~> 4.2)
|
22
22
|
multi_json (~> 1.3)
|
23
23
|
thread_safe (~> 0.1)
|
24
24
|
tzinfo (~> 0.3.37)
|
25
25
|
addressable (2.3.5)
|
26
|
-
atomic (1.1.
|
26
|
+
atomic (1.1.16)
|
27
27
|
btce (0.2.4)
|
28
28
|
monkey-patch
|
29
29
|
builder (3.1.4)
|
@@ -61,7 +61,7 @@ GEM
|
|
61
61
|
guard (>= 2.1.1)
|
62
62
|
rspec (~> 3.0.0.beta, >= 2.14, < 4.0)
|
63
63
|
hashie (2.0.5)
|
64
|
-
httparty (0.
|
64
|
+
httparty (0.13.0)
|
65
65
|
json (~> 1.8)
|
66
66
|
multi_xml (>= 0.5.2)
|
67
67
|
i18n (0.6.9)
|
@@ -80,16 +80,13 @@ GEM
|
|
80
80
|
minitest (4.7.5)
|
81
81
|
money (5.1.1)
|
82
82
|
i18n (~> 0.6.0)
|
83
|
-
monkey-patch (0.0.
|
83
|
+
monkey-patch (0.0.16)
|
84
84
|
activesupport
|
85
|
-
|
86
|
-
faraday (~> 0.8, < 0.10)
|
87
|
-
json (~> 1.7, >= 1.7.7)
|
88
|
-
multi_json (1.8.2)
|
85
|
+
multi_json (1.9.2)
|
89
86
|
multi_xml (0.5.5)
|
90
87
|
multipart-post (1.2.0)
|
91
|
-
polyglot (0.3.
|
92
|
-
pony (1.
|
88
|
+
polyglot (0.3.4)
|
89
|
+
pony (1.8)
|
93
90
|
mail (>= 2.0)
|
94
91
|
pry (0.9.12.4)
|
95
92
|
coderay (~> 1.0)
|
@@ -131,15 +128,14 @@ GEM
|
|
131
128
|
term-ansicolor (1.2.2)
|
132
129
|
tins (~> 0.8)
|
133
130
|
thor (0.18.1)
|
134
|
-
thread_safe (0.1
|
135
|
-
atomic
|
131
|
+
thread_safe (0.3.1)
|
132
|
+
atomic (>= 1.1.7, < 2)
|
136
133
|
tilt (1.4.1)
|
137
134
|
timers (1.1.0)
|
138
135
|
tins (0.13.1)
|
139
|
-
treetop (1.
|
140
|
-
polyglot
|
141
|
-
|
142
|
-
tzinfo (0.3.38)
|
136
|
+
treetop (1.5.3)
|
137
|
+
polyglot (~> 0.3)
|
138
|
+
tzinfo (0.3.39)
|
143
139
|
vcr (2.8.0)
|
144
140
|
webmock (1.16.1)
|
145
141
|
addressable (>= 2.2.7)
|
data/lib/rbtc_arbitrage.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require(:default)
|
1
3
|
require 'thor'
|
2
|
-
require 'mtgox'
|
3
4
|
require_relative 'rbtc_arbitrage/campbx.rb'
|
4
5
|
require 'btce'
|
5
6
|
require 'coinbase'
|
6
7
|
require 'pony'
|
7
8
|
require_relative 'rbtc_arbitrage/client.rb'
|
9
|
+
Dir["#{File.dirname(__FILE__)}/rbtc_arbitrage/trader/*.rb"].each { |f| require(f) }
|
8
10
|
Dir["#{File.dirname(__FILE__)}/rbtc_arbitrage/**/*.rb"].each { |f| require(f) }
|
9
11
|
|
10
12
|
module RbtcArbitrage
|
@@ -70,10 +70,10 @@ module CampBX
|
|
70
70
|
request.set_form_data( post_data )
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
#puts "Post Data: #{post_data}"
|
73
|
+
make_request(http, request)
|
74
|
+
end
|
76
75
|
|
76
|
+
def make_request http, request
|
77
77
|
# CampBX API: max 1 request per 500ms
|
78
78
|
delta = Time.now - @@last
|
79
79
|
#puts delta*1000
|
@@ -90,7 +90,6 @@ module CampBX
|
|
90
90
|
else # HTTP ERROR
|
91
91
|
warn "HTTP Error: + #{res.code}"
|
92
92
|
end
|
93
|
-
|
94
93
|
end
|
95
94
|
|
96
95
|
end
|
data/lib/rbtc_arbitrage/cli.rb
CHANGED
@@ -6,8 +6,8 @@ module RbtcArbitrage
|
|
6
6
|
option :cutoff, type: :numeric, default: 2, desc: "The minimum profit level required to execute a trade."
|
7
7
|
option :volume, type: :numeric, default: 0.01, desc: "The amount of bitcoins to trade per transaction."
|
8
8
|
option :verbose, type: :boolean, default: true, desc: "Whether you wish to log information."
|
9
|
-
option :buyer, type: :string, default: "
|
10
|
-
option :seller, type: :string, default: "
|
9
|
+
option :buyer, type: :string, default: "btce"
|
10
|
+
option :seller, type: :string, default: "campbx"
|
11
11
|
option :repeat, type: :numeric, default: nil
|
12
12
|
option :notify, type: :boolean, default: false
|
13
13
|
def trade
|
@@ -16,10 +16,11 @@ module RbtcArbitrage
|
|
16
16
|
if @options[:verbose]
|
17
17
|
warning = "Coinbase doesn't provide a USD balance because"
|
18
18
|
warning << " it connects to your bank account. Be careful, "
|
19
|
-
warning << "because this will withdraw directly from your accounts
|
19
|
+
warning << "because this will withdraw directly from your accounts"
|
20
|
+
warning << "when you trade live."
|
20
21
|
logger.warn warning
|
21
22
|
end
|
22
|
-
@balance ||= [
|
23
|
+
@balance ||= [max_float, max_float]
|
23
24
|
end
|
24
25
|
|
25
26
|
# Configures the client's API keys.
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module RbtcArbitrage
|
2
2
|
class Trader
|
3
|
+
include RbtcArbitrage::TraderHelpers::Notifier
|
4
|
+
include RbtcArbitrage::TraderHelpers::Logger
|
5
|
+
|
3
6
|
attr_reader :buy_client, :sell_client, :received
|
4
7
|
attr_accessor :buyer, :seller, :options
|
5
8
|
|
@@ -19,10 +22,10 @@ module RbtcArbitrage
|
|
19
22
|
set_key opts, :verbose, true
|
20
23
|
set_key opts, :live, false
|
21
24
|
set_key opts, :repeat, nil
|
22
|
-
exchange = opts[:buyer] || :
|
25
|
+
exchange = opts[:buyer] || :btce
|
23
26
|
set_key opts, :notify, false
|
24
27
|
@buy_client = client_for_exchange(exchange)
|
25
|
-
exchange = opts[:seller] || :
|
28
|
+
exchange = opts[:seller] || :campbx
|
26
29
|
@sell_client = client_for_exchange(exchange)
|
27
30
|
self
|
28
31
|
end
|
@@ -64,14 +67,7 @@ module RbtcArbitrage
|
|
64
67
|
raise SecurityError, "--live flag is false. Not executing trade." unless options[:live]
|
65
68
|
get_balance
|
66
69
|
if @percent > @options[:cutoff]
|
67
|
-
|
68
|
-
raise SecurityError, "Not enough funds. Exiting."
|
69
|
-
else
|
70
|
-
logger.info "Trading live!" if options[:verbose]
|
71
|
-
@buy_client.buy
|
72
|
-
@sell_client.sell
|
73
|
-
@buy_client.transfer @sell_client
|
74
|
-
end
|
70
|
+
buy_and_transfer!
|
75
71
|
else
|
76
72
|
logger.info "Not trading live because cutoff is higher than profit." if @options[:verbose]
|
77
73
|
end
|
@@ -82,19 +78,8 @@ module RbtcArbitrage
|
|
82
78
|
buyer[:price] = @buy_client.price(:buy)
|
83
79
|
seller[:price] = @sell_client.price(:sell)
|
84
80
|
prices = [buyer[:price], seller[:price]]
|
85
|
-
@paid = buyer[:price] * 1.006 * @options[:volume]
|
86
|
-
@received = seller[:price] * 0.994 * @options[:volume]
|
87
|
-
@percent = ((received/@paid - 1) * 100).round(2)
|
88
|
-
end
|
89
81
|
|
90
|
-
|
91
|
-
lower_ex = @buy_client.exchange.to_s.capitalize
|
92
|
-
higher_ex = @sell_client.exchange.to_s.capitalize
|
93
|
-
logger.info "#{lower_ex}: $#{buyer[:price].round(2)}"
|
94
|
-
logger.info "#{higher_ex}: $#{seller[:price].round(2)}"
|
95
|
-
logger.info "buying #{@options[:volume]} btc from #{lower_ex} for $#{@paid.round(2)}"
|
96
|
-
logger.info "selling #{@options[:volume]} btc on #{higher_ex} for $#{@received.round(2)}"
|
97
|
-
logger.info "profit: $#{(@received - @paid).round(2)} (#{@percent.round(2)}%)"
|
82
|
+
calculate_profit
|
98
83
|
end
|
99
84
|
|
100
85
|
def get_balance
|
@@ -102,10 +87,6 @@ module RbtcArbitrage
|
|
102
87
|
@buyer[:btc], @buyer[:usd] = @buy_client.balance
|
103
88
|
end
|
104
89
|
|
105
|
-
def logger
|
106
|
-
@options[:logger]
|
107
|
-
end
|
108
|
-
|
109
90
|
def validate_env
|
110
91
|
[@sell_client, @buy_client].each do |client|
|
111
92
|
client.validate_env
|
@@ -135,59 +116,24 @@ module RbtcArbitrage
|
|
135
116
|
end
|
136
117
|
end
|
137
118
|
|
138
|
-
|
139
|
-
return false unless options[:notify]
|
140
|
-
return false unless @percent > options[:cutoff]
|
141
|
-
setup_pony
|
119
|
+
private
|
142
120
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
})
|
121
|
+
def calculate_profit
|
122
|
+
@paid = buyer[:price] * 1.006 * @options[:volume]
|
123
|
+
@received = seller[:price] * 0.994 * @options[:volume]
|
124
|
+
@percent = ((received/@paid - 1) * 100).round(2)
|
148
125
|
end
|
149
126
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
user_name: ENV['SENDGRID_USERNAME'],
|
160
|
-
password: ENV['SENDGRID_PASSWORD'],
|
161
|
-
authentication: :plain,
|
162
|
-
enable_starttls_auto: true
|
163
|
-
}
|
164
|
-
}
|
127
|
+
def buy_and_transfer!
|
128
|
+
if @paid > buyer[:usd] || @options[:volume] > seller[:btc]
|
129
|
+
raise SecurityError, "Not enough funds. Exiting."
|
130
|
+
else
|
131
|
+
logger.info "Trading live!" if options[:verbose]
|
132
|
+
@buy_client.buy
|
133
|
+
@sell_client.sell
|
134
|
+
@buy_client.transfer @sell_client
|
135
|
+
end
|
165
136
|
end
|
166
137
|
|
167
|
-
def notification
|
168
|
-
lower_ex = @buy_client.exchange.to_s.capitalize
|
169
|
-
higher_ex = @sell_client.exchange.to_s.capitalize
|
170
|
-
<<-eos
|
171
|
-
Update from your friendly rbtc_arbitrage trader:
|
172
|
-
|
173
|
-
-------------------
|
174
|
-
|
175
|
-
#{lower_ex}: $#{buyer[:price].round(2)}
|
176
|
-
#{higher_ex}: $#{seller[:price].round(2)}
|
177
|
-
buying #{@options[:volume]} btc from #{lower_ex} for $#{@paid.round(2)}
|
178
|
-
selling #{@options[:volume]} btc on #{higher_ex} for $#{@received.round(2)}
|
179
|
-
profit: $#{(@received - @paid).round(2)} (#{@percent.round(2)}%)
|
180
|
-
|
181
|
-
-------------------
|
182
|
-
|
183
|
-
options:
|
184
|
-
|
185
|
-
#{options.reject{|k,v| v.is_a?(Logger)}.collect{|k,v| "#{k}: #{v.to_s}"}.join(" | ")}
|
186
|
-
|
187
|
-
-------------------
|
188
|
-
|
189
|
-
https://github.com/hstove/rbtc_arbitrage
|
190
|
-
eos
|
191
|
-
end
|
192
138
|
end
|
193
139
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RbtcArbitrage
|
2
|
+
module TraderHelpers
|
3
|
+
module Logger
|
4
|
+
def logger
|
5
|
+
@options[:logger]
|
6
|
+
end
|
7
|
+
|
8
|
+
def log_info
|
9
|
+
lower_ex = @buy_client.exchange.to_s.capitalize
|
10
|
+
higher_ex = @sell_client.exchange.to_s.capitalize
|
11
|
+
logger.info "#{lower_ex}: $#{buyer[:price].round(2)}"
|
12
|
+
logger.info "#{higher_ex}: $#{seller[:price].round(2)}"
|
13
|
+
logger.info "buying #{@options[:volume]} btc from #{lower_ex} for $#{@paid.round(2)}"
|
14
|
+
logger.info "selling #{@options[:volume]} btc on #{higher_ex} for $#{@received.round(2)}"
|
15
|
+
|
16
|
+
log_profit
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def log_profit
|
22
|
+
profit_msg = "profit: $#{(@received - @paid).round(2)} (#{@percent.round(2)}%)"
|
23
|
+
if cutoff = @options[:cutoff]
|
24
|
+
profit_msg << " is #{@percent < cutoff ? 'below' : 'above'} cutoff"
|
25
|
+
profit_msg << " of #{cutoff}%."
|
26
|
+
end
|
27
|
+
logger.info profit_msg
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RbtcArbitrage
|
2
|
+
module TraderHelpers
|
3
|
+
module Notifier
|
4
|
+
def notify
|
5
|
+
return false unless options[:notify]
|
6
|
+
return false unless @percent > options[:cutoff]
|
7
|
+
setup_pony
|
8
|
+
|
9
|
+
options[:logger].info "Sending email to #{ENV['SENDGRID_EMAIL']}"
|
10
|
+
Pony.mail({
|
11
|
+
body: notification,
|
12
|
+
to: ENV['SENDGRID_EMAIL'],
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_pony
|
17
|
+
Pony.options = {
|
18
|
+
from: ENV['FROM_EMAIL'] || "info@example.org",
|
19
|
+
subject: "rbtc_arbitrage notification",
|
20
|
+
via: :smtp,
|
21
|
+
via_options: {
|
22
|
+
address: 'smtp.sendgrid.net',
|
23
|
+
port: '587',
|
24
|
+
domain: 'heroku.com',
|
25
|
+
user_name: ENV['SENDGRID_USERNAME'],
|
26
|
+
password: ENV['SENDGRID_PASSWORD'],
|
27
|
+
authentication: :plain,
|
28
|
+
enable_starttls_auto: true
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def notification
|
34
|
+
lower_ex = @buy_client.exchange.to_s.capitalize
|
35
|
+
higher_ex = @sell_client.exchange.to_s.capitalize
|
36
|
+
<<-eos
|
37
|
+
Update from your friendly rbtc_arbitrage trader:
|
38
|
+
|
39
|
+
-------------------
|
40
|
+
|
41
|
+
#{lower_ex}: $#{buyer[:price].round(2)}
|
42
|
+
#{higher_ex}: $#{seller[:price].round(2)}
|
43
|
+
buying #{@options[:volume]} btc from #{lower_ex} for $#{@paid.round(2)}
|
44
|
+
selling #{@options[:volume]} btc on #{higher_ex} for $#{@received.round(2)}
|
45
|
+
profit: $#{(@received - @paid).round(2)} (#{@percent.round(2)}%)
|
46
|
+
|
47
|
+
-------------------
|
48
|
+
|
49
|
+
options:
|
50
|
+
|
51
|
+
#{options.reject{|k,v| v.is_a?(Logger)}.collect{|k,v| "#{k}: #{v.to_s}"}.join(" | ")}
|
52
|
+
|
53
|
+
-------------------
|
54
|
+
|
55
|
+
https://github.com/hstove/rbtc_arbitrage
|
56
|
+
eos
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/rbtc_arbitrage.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
|
24
|
-
spec.add_dependency "
|
24
|
+
spec.add_dependency "faraday", "0.8.8"
|
25
25
|
spec.add_dependency "activemodel", ">= 3.1"
|
26
26
|
spec.add_dependency "activesupport", ">= 3.1"
|
27
27
|
spec.add_dependency "thor"
|