rbtc_arbitrage_simple 1.2.3
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +23 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +156 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/Procfile +1 -0
- data/README.md +88 -0
- data/Rakefile +10 -0
- data/bin/rbtc_simple +4 -0
- data/dummy_web_server.rb +9 -0
- data/lib/rbtc_arbitrage.rb +15 -0
- data/lib/rbtc_arbitrage/campbx.rb +98 -0
- data/lib/rbtc_arbitrage/cli.rb +19 -0
- data/lib/rbtc_arbitrage/client.rb +48 -0
- data/lib/rbtc_arbitrage/clients/btce_client.rb +62 -0
- data/lib/rbtc_arbitrage/clients/campbx_client.rb +48 -0
- data/lib/rbtc_arbitrage/clients/client.rb.example +46 -0
- data/lib/rbtc_arbitrage/clients/coinbase_client.rb +63 -0
- data/lib/rbtc_arbitrage/clients/mtgox_client.rb +56 -0
- data/lib/rbtc_arbitrage/trader.rb +123 -0
- data/lib/rbtc_arbitrage/version.rb +3 -0
- data/rbtc_arbitrage.gemspec +30 -0
- data/spec/cli_spec.rb +8 -0
- data/spec/client_spec.rb +33 -0
- data/spec/clients/btce_client_spec.rb +93 -0
- data/spec/clients/campbx_client_spec.rb +66 -0
- data/spec/clients/coinbase_client_spec.rb +66 -0
- data/spec/clients/mtgox_client_spec.rb +53 -0
- data/spec/rbtc_arbitrage_spec.rb +11 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BitstampClient/_balance/fetches_the_balance_correctly.yml +96 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BitstampClient/_price/fetches_price_for_buy_correctly.yml +52 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BitstampClient/_price/fetches_price_for_sell_correctly.yml +52 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BitstampClient/_price/fetches_prices_correctly.yml +44 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BtceClient/_balance/should_raise_if_bad_API_keys.yml +53 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BtceClient/_price/calls_btc-e.yml +91 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BtceClient/_price/fetches_price_for_buy_correctly.yml +47 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_BtceClient/_price/fetches_price_for_sell_correctly.yml +47 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CampbxClient/_balance/fetches_the_balance_correctly.yml +95 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CampbxClient/_price/fetches_price_for_buy_correctly.yml +49 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CampbxClient/_price/fetches_price_for_sell_correctly.yml +49 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CoinbaseClient/_balance/fetches_the_balance_correctly.yml +109 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CoinbaseClient/_price/calls_coinbase.yml +215 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CoinbaseClient/_price/fetches_price_for_buy_correctly.yml +56 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_CoinbaseClient/_price/fetches_price_for_sell_correctly.yml +56 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_balance/fetches_the_balance_correctly.yml +77 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_price_for_buy_correctly.yml +44 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_price_for_sell_correctly.yml +44 -0
- data/spec/support/cassettes/RbtcArbitrage_Clients_MtGoxClient/_price/fetches_prices_correctly.yml +85 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/should_raise_SecurityError_if_not_live.yml +88 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/raises_SecurityError_if_not_enough_BTC.yml +81 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/raises_SecurityError_if_not_enough_USD.yml +81 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/should_fetch_balance.yml +88 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_execute_trade/when_live/shouldn_t_raise_security_error.yml +166 -0
- data/spec/support/cassettes/RbtcArbitrage_Trader/_fetch_prices/gets_the_right_price_set.yml +173 -0
- data/spec/trader_spec.rb +228 -0
- metadata +249 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
class CLI < Thor
|
|
3
|
+
|
|
4
|
+
desc "arbitrage", "Get information about the current arbitrage levels."
|
|
5
|
+
option :live, type: :boolean, default: false, desc: "Execute live trades."
|
|
6
|
+
option :cutoff, type: :numeric, default: 2, desc: "The minimum profit level required to execute a trade."
|
|
7
|
+
option :volume, type: :numeric, default: 0.01, desc: "The amount of bitcoins to trade per transaction."
|
|
8
|
+
option :verbose, type: :boolean, default: true, desc: "Whether you wish to log information."
|
|
9
|
+
option :buyer, type: :string, default: "campbx"
|
|
10
|
+
option :seller, type: :string, default: "mtgox"
|
|
11
|
+
option :repeat, type: :numeric, default: nil
|
|
12
|
+
def trade
|
|
13
|
+
RbtcArbitrage::Trader.new(options).trade
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
default_task :trade
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Client
|
|
3
|
+
attr_accessor :options
|
|
4
|
+
attr_writer :balance
|
|
5
|
+
|
|
6
|
+
def initialize config={}
|
|
7
|
+
@options = config
|
|
8
|
+
@options = {}
|
|
9
|
+
set_key config, :volume, 0.01
|
|
10
|
+
set_key config, :cutoff, 2
|
|
11
|
+
set_key config, :logger, Logger.new(STDOUT)
|
|
12
|
+
set_key config, :verbose, true
|
|
13
|
+
set_key config, :live, false
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate_keys *args
|
|
18
|
+
args.each do |key|
|
|
19
|
+
key = key.to_s.upcase
|
|
20
|
+
if ENV[key].blank?
|
|
21
|
+
raise ArgumentError, "Exiting because missing required ENV variable $#{key}."
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def buy
|
|
27
|
+
trade :buy
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def sell
|
|
31
|
+
trade :sell
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def address
|
|
35
|
+
ENV["#{exchange.to_s.upcase}_ADDRESS"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def logger
|
|
39
|
+
@options[:logger]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def set_key config, key, default
|
|
45
|
+
@options[key] = config.has_key?(key) ? config[key] : default
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Clients
|
|
3
|
+
class BtceClient
|
|
4
|
+
include RbtcArbitrage::Client
|
|
5
|
+
|
|
6
|
+
def exchange
|
|
7
|
+
:btce
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def balance
|
|
11
|
+
return @balance if @balance
|
|
12
|
+
begin
|
|
13
|
+
balances = interface.get_info["return"]["funds"]
|
|
14
|
+
@balance = [balances["btc"], balances["usd"]]
|
|
15
|
+
rescue NoMethodError => e
|
|
16
|
+
raise SecurityError, "Invalid API key for BTC-e"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def interface
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate_env
|
|
24
|
+
validate_keys :btce_key, :btce_secret, :btce_address
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def trade action
|
|
28
|
+
warning = "BTC-E does not support API bitcoin transfer. "
|
|
29
|
+
warning << "If you really want to trade, you will have "
|
|
30
|
+
warning << "to manually send bitcoin. Enter 'accept' to continue. \n> "
|
|
31
|
+
@options[:logger].warn warning if @options[:verbose]
|
|
32
|
+
return false unless gets.chomp == "accept"
|
|
33
|
+
opts = {
|
|
34
|
+
type: action,
|
|
35
|
+
rate: price(action),
|
|
36
|
+
amount: @options[:volume],
|
|
37
|
+
pair: "btc_usd"
|
|
38
|
+
}
|
|
39
|
+
interface.trade opts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def price action
|
|
43
|
+
return @ticker[action.to_s] if @ticker
|
|
44
|
+
@ticker = Btce::Ticker.new("btc_usd").json["ticker"]
|
|
45
|
+
@ticker[action.to_s]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def transfer client
|
|
49
|
+
if @options[:verbose]
|
|
50
|
+
error = "BTC-E does not have a 'transfer' API.\n"
|
|
51
|
+
error << "You must transfer bitcoin manually."
|
|
52
|
+
@options[:logger].error error
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def interface
|
|
57
|
+
opts = {key: ENV['BTCE_KEY'], secret: ENV['BTCE_SECRET']}
|
|
58
|
+
@interface ||= Btce::TradeAPI.new(opts)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Clients
|
|
3
|
+
class CampbxClient
|
|
4
|
+
include RbtcArbitrage::Client
|
|
5
|
+
|
|
6
|
+
def exchange
|
|
7
|
+
:campbx
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def balance
|
|
11
|
+
return @balance if @balance
|
|
12
|
+
funds = interface.my_funds
|
|
13
|
+
[funds["Total BTC"].to_f, funds["Total USD"].to_f]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def interface
|
|
17
|
+
@interface ||= CampBX::API.new(ENV['CAMPBX_KEY'],ENV['CAMPBX_SECRET'])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def validate_env
|
|
21
|
+
validate_keys :campbx_key, :campbx_secret
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def trade action
|
|
26
|
+
trade_mode = "Quick#{action.to_s.capitalize}"
|
|
27
|
+
interface.trade_enter trade_mode, @options[:volume], price(action)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def price action
|
|
31
|
+
return @price if @price
|
|
32
|
+
action = {
|
|
33
|
+
buy: "Best Ask",
|
|
34
|
+
sell: "Best Bid",
|
|
35
|
+
}[action]
|
|
36
|
+
@price = interface.xticker[action].to_f
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def transfer client
|
|
40
|
+
interface.send_btc client.address, @options[:volume]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def address
|
|
44
|
+
@address ||= interface.get_btc_address
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Clients
|
|
3
|
+
class ExchangeClient
|
|
4
|
+
include RbtcArbitrage::Client
|
|
5
|
+
|
|
6
|
+
# return a symbol as the name
|
|
7
|
+
# of this exchange
|
|
8
|
+
def exchange
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Returns an array of Floats.
|
|
12
|
+
# The first element is the balance in BTC;
|
|
13
|
+
# The second is in USD.
|
|
14
|
+
def balance
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def interface
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Configures the client's API keys.
|
|
21
|
+
def validate_env
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# `action` is :buy or :sell
|
|
25
|
+
def trade action
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# `action` is :buy or :sell
|
|
29
|
+
# Returns a Numeric type.
|
|
30
|
+
def price action
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Transfers BTC to the address of a different
|
|
34
|
+
# exchange.
|
|
35
|
+
def transfer client
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# If there is an API method to fetch your
|
|
39
|
+
# BTC address, implement this, otherwise
|
|
40
|
+
# remove this method and set the ENV
|
|
41
|
+
# variable [this-exchange-name-in-caps]_ADDRESS
|
|
42
|
+
def address
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Clients
|
|
3
|
+
class CoinbaseClient
|
|
4
|
+
include RbtcArbitrage::Client
|
|
5
|
+
|
|
6
|
+
# return a symbol as the name
|
|
7
|
+
# of this exchange
|
|
8
|
+
def exchange
|
|
9
|
+
:coinbase
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Returns an array of Floats.
|
|
13
|
+
# The first element is the balance in BTC;
|
|
14
|
+
# The second is in USD.
|
|
15
|
+
def balance
|
|
16
|
+
if @options[:verbose]
|
|
17
|
+
warning = "Coinbase doesn't provide a USD balance because"
|
|
18
|
+
warning << " it connects to your bank account. Be careful, "
|
|
19
|
+
warning << "because this will withdraw directly from your accounts."
|
|
20
|
+
logger.warn warning
|
|
21
|
+
end
|
|
22
|
+
@balance ||= [interface.balance.to_f, max_float]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Configures the client's API keys.
|
|
26
|
+
def validate_env
|
|
27
|
+
validate_keys :coinbase_key, :coinbase_address
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# `action` is :buy or :sell
|
|
31
|
+
def trade action
|
|
32
|
+
interface.send("#{action}!".to_sym, @options[:volume])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# `action` is :buy or :sell
|
|
36
|
+
# Returns a Numeric type.
|
|
37
|
+
def price action
|
|
38
|
+
method = "#{action}_price".to_sym
|
|
39
|
+
@price ||= interface.send(method).to_f
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Transfers BTC to the address of a different
|
|
43
|
+
# exchange.
|
|
44
|
+
def transfer client
|
|
45
|
+
interface.send_money client.address, @options[:volume]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def interface
|
|
49
|
+
@interface ||= Coinbase::Client.new(ENV['COINBASE_KEY'])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def address
|
|
53
|
+
@address ||= interface.receive_address.address
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def max_float
|
|
59
|
+
Float::MAX
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
module Clients
|
|
3
|
+
class MtGoxClient
|
|
4
|
+
include RbtcArbitrage::Client
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Returns an array of Floats.
|
|
8
|
+
# The first element is the balance in BTC;
|
|
9
|
+
# The second is in USD.
|
|
10
|
+
def balance
|
|
11
|
+
return @balance if @balance
|
|
12
|
+
balances = MtGox.balance
|
|
13
|
+
@balance = [balances[0].amount.to_f, balances[1].amount.to_f]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Configures the MtGox
|
|
18
|
+
# client's API keys.
|
|
19
|
+
def validate_env
|
|
20
|
+
validate_keys :mtgox_key, :mtgox_secret, :mtgox_address
|
|
21
|
+
MtGox.configure do |config|
|
|
22
|
+
config.key = ENV["MTGOX_KEY"]
|
|
23
|
+
config.secret = ENV["MTGOX_SECRET"]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def exchange
|
|
28
|
+
:mtgox
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# `action` is :buy or :sell
|
|
32
|
+
# Returns a Numeric type.
|
|
33
|
+
def price action
|
|
34
|
+
return @price if @price
|
|
35
|
+
action = {
|
|
36
|
+
buy: :sell,
|
|
37
|
+
sell: :buy,
|
|
38
|
+
}[action]
|
|
39
|
+
@price = MtGox.ticker.send(action)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# `action` is :buy or :sell
|
|
43
|
+
def trade action
|
|
44
|
+
action = "#{action.to_s}!".to_sym
|
|
45
|
+
MtGox.send(action, @options[:volume], :market)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Transfers BTC to the address of a different
|
|
50
|
+
# exchange.
|
|
51
|
+
def transfer other_client
|
|
52
|
+
MtGox.withdraw! @options[:volume], other_client.address
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module RbtcArbitrage
|
|
2
|
+
class Trader
|
|
3
|
+
attr_reader :buy_client, :sell_client, :received
|
|
4
|
+
attr_accessor :buyer, :seller, :options
|
|
5
|
+
|
|
6
|
+
def initialize config={}
|
|
7
|
+
opts = {}
|
|
8
|
+
config.each do |key, val|
|
|
9
|
+
opts[(key.to_sym rescue key) || key] = val
|
|
10
|
+
end
|
|
11
|
+
@buyer = {}
|
|
12
|
+
@seller = {}
|
|
13
|
+
@options = {}
|
|
14
|
+
set_key opts, :volume, 0.01
|
|
15
|
+
set_key opts, :cutoff, 2
|
|
16
|
+
set_key opts, :logger, Logger.new(STDOUT)
|
|
17
|
+
set_key opts, :verbose, true
|
|
18
|
+
set_key opts, :live, false
|
|
19
|
+
set_key opts, :repeat, nil
|
|
20
|
+
exchange = opts[:buyer] || :campbx
|
|
21
|
+
@buy_client = client_for_exchange(exchange)
|
|
22
|
+
exchange = opts[:seller] || :mtgox
|
|
23
|
+
@sell_client = client_for_exchange(exchange)
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_key config, key, default
|
|
28
|
+
@options[key] = config.has_key?(key) ? config[key] : default
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def trade
|
|
32
|
+
fetch_prices
|
|
33
|
+
log_info if options[:verbose]
|
|
34
|
+
|
|
35
|
+
if options[:live] && options[:cutoff] > @percent
|
|
36
|
+
raise SecurityError, "Exiting because real profit (#{@percent.round(2)}%) is less than cutoff (#{options[:cutoff].round(2)}%)"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
execute_trade if options[:live]
|
|
40
|
+
|
|
41
|
+
if @options[:repeat]
|
|
42
|
+
trade_again
|
|
43
|
+
end
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def trade_again
|
|
48
|
+
sleep @options[:repeat]
|
|
49
|
+
@logger.info " - " if @options[:verbose]
|
|
50
|
+
@buy_client = @buy_client.class.new(@options)
|
|
51
|
+
@sell_client = @sell_client.class.new(@options)
|
|
52
|
+
trade
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def execute_trade
|
|
56
|
+
fetch_prices unless @paid
|
|
57
|
+
validate_env
|
|
58
|
+
raise SecurityError, "--live flag is false. Not executing trade." unless options[:live]
|
|
59
|
+
get_balance
|
|
60
|
+
if @percent > @options[:cutoff]
|
|
61
|
+
if @paid > buyer[:usd] || @options[:volume] > seller[:btc]
|
|
62
|
+
raise SecurityError, "Not enough funds. Exiting."
|
|
63
|
+
else
|
|
64
|
+
logger.info "Trading live!" if options[:verbose]
|
|
65
|
+
@buy_client.buy
|
|
66
|
+
@sell_client.sell
|
|
67
|
+
@buy_client.transfer @sell_client
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
logger.info "Not trading live because cutoff is higher than profit." if @options[:verbose]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def fetch_prices
|
|
75
|
+
logger.info "Fetching exchange rates" if @options[:verbose]
|
|
76
|
+
buyer[:price] = @buy_client.price(:buy)
|
|
77
|
+
seller[:price] = @sell_client.price(:sell)
|
|
78
|
+
prices = [buyer[:price], seller[:price]]
|
|
79
|
+
@paid = buyer[:price] * 1.006 * @options[:volume]
|
|
80
|
+
@received = seller[:price] * 0.994 * @options[:volume]
|
|
81
|
+
@percent = ((received/@paid - 1) * 100).round(2)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def log_info
|
|
85
|
+
lower_ex = @buy_client.exchange.to_s.capitalize
|
|
86
|
+
higher_ex = @sell_client.exchange.to_s.capitalize
|
|
87
|
+
logger.info "#{lower_ex}: $#{buyer[:price].round(2)}"
|
|
88
|
+
logger.info "#{higher_ex}: $#{seller[:price].round(2)}"
|
|
89
|
+
logger.info "buying #{@options[:volume]} btc from #{lower_ex} for $#{@paid.round(2)}"
|
|
90
|
+
logger.info "selling #{@options[:volume]} btc on #{higher_ex} for $#{@received.round(2)}"
|
|
91
|
+
logger.info "profit: $#{(@received - @paid).round(2)} (#{@percent.round(2)}%)"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def get_balance
|
|
95
|
+
@seller[:btc], @seller[:usd] = @sell_client.balance
|
|
96
|
+
@buyer[:btc], @buyer[:usd] = @buy_client.balance
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def logger
|
|
100
|
+
@options[:logger]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_env
|
|
104
|
+
[@sell_client, @buy_client].each do |client|
|
|
105
|
+
client.validate_env
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def client_for_exchange market
|
|
110
|
+
market = market.to_sym unless market.is_a?(Symbol)
|
|
111
|
+
clazz = RbtcArbitrage::Clients.constants.find do |c|
|
|
112
|
+
clazz = RbtcArbitrage::Clients.const_get(c)
|
|
113
|
+
clazz.new.exchange == market
|
|
114
|
+
end
|
|
115
|
+
begin
|
|
116
|
+
clazz = RbtcArbitrage::Clients.const_get(clazz)
|
|
117
|
+
clazz.new @options
|
|
118
|
+
rescue TypeError => e
|
|
119
|
+
raise ArgumentError, "Invalid exchange - '#{market}'"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|