rbtc_arbitrage 0.0.2 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
- metadata.gz: e874d7529495e986f3fbbc823f984fed0e20c58b
4
- data.tar.gz: def649cf14a6228879ff94970e93ae5b6bd80dbf
3
+ metadata.gz: 8b73f2bb929386db4483f985d6e4ad1e1e630c0f
4
+ data.tar.gz: 09877353240e3fad0adaa5a14f7a9d9b15951f76
5
5
  !binary "U0hBNTEy":
6
- metadata.gz: 676c016c98bbb4ae2b982c5bdbb67be81def5f671f2bba8d0c26c3b496deeb1e2fed0f121b8ec88b32dd628206321141e63c742e2a7fbfff5dbe000cf562fdf2
7
- data.tar.gz: fd15694a96a4cd93abe39768988f2ef444671deadeccb4cb4e36202473b6e93993e4e674bc0f22435a5ff979278faa42e7498b2891e3b8afb6d8841ffa2ccd5a
6
+ metadata.gz: 3ea1ba33fd9e0cefc968e9af9c4a96b71af16d0ae2d8a8952b9928ba065f83f5f489576e902b20c2b55e2658f446b089fadf16d558e7a3574e52e0dc0d20d422
7
+ data.tar.gz: 3267b9bf783cec825d6564b86910ee67715ab5c6aeca56d1fb3d9a90afe1d0a0e17c7f54659495a36f2188e4c92bd67220d1e1c95267533c7e17efc582810cc2
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 1.9.2
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run specs"
5
+ RSpec::Core::RakeTask.new do |t|
6
+ # t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
7
+ # Put spec opts in a file named .rspec in root
8
+ end
9
+
10
+ task default: :spec
@@ -1,8 +1,7 @@
1
- require "rbtc_arbitrage/version"
2
1
  require 'thor'
3
- require 'rbtc_arbitrage/cli'
4
2
  require 'mtgox'
5
3
  require 'bitstamp'
4
+ Dir[File.expand_path('../rbtc_arbitrage/*', __FILE__)].each { |f| require f }
6
5
 
7
6
  module RbtcArbitrage
8
7
  end
@@ -1,101 +1,16 @@
1
1
  module RbtcArbitrage
2
2
  class CLI < Thor
3
- attr_accessor :stamp, :mtgox
4
3
 
5
4
  desc "arbitrage", "Get information about the current arbitrage levels."
6
5
  option :live, type: :boolean, default: false, desc: "Execute live trades."
7
6
  option :cutoff, type: :numeric, default: 2, desc: "The minimum profit level required to execute a trade."
8
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
9
  def trade
10
- @stamp = {}; @mtgox = {}
11
-
12
- if options[:live]
13
- validate_env
14
- get_balance
15
- end
16
-
17
- # puts "#{Time.now.strftime("%m/%d/%Y at %I:%M%p")}"
18
- # puts "Retrieving market information and balances"
19
- amount_to_buy = options[:volume]
20
- stamp[:price] = Bitstamp.ticker.ask.to_f
21
- mtgox[:price] = MtGox.ticker.buy
22
- prices = [stamp[:price], mtgox[:price]]
23
- paid = prices.min * 1.005 * amount_to_buy
24
- received = prices.max * 0.994 * amount_to_buy
25
-
26
- puts "Bitstamp: $#{stamp[:price].round(2)}"
27
- puts "MtGox: $#{mtgox[:price].round(2)}"
28
- lower_ex, higher_ex = stamp[:price] < mtgox[:price] ? [Bitstamp, MtGox] : [MtGox, Bitstamp]
29
- puts "buying #{amount_to_buy} btc from #{lower_ex} for $#{paid.round(2)}"
30
- puts "selling #{amount_to_buy} btc on #{higher_ex} for $#{received.round(2)}"
31
- percent = ((received/paid - 1) * 100).round(2)
32
- puts "profit: $#{(received - paid).round(2)} (#{percent.round(2)}%)"
33
-
34
- if options[:cutoff] > percent
35
- puts "Exiting because real profit (#{percent.round(2)}%) is less than cutoff (#{options[:cutoff].round(2)}%)"
36
- return 0
37
- end
38
-
39
- if options[:live]
40
- puts "Balances:"
41
- puts "stamp usd: $#{stamp[:usd].round(2)} btc: #{stamp[:usd].round(2)}"
42
- puts "mtgox usd: $#{mtgox[:usd].round(2)} btc: #{mtgox[:usd].round(2)}"
43
- if stamp[:price] < mtgox[:price]
44
- if paid > stamp[:usd] || amount_to_buy > mtgox[:btc]
45
- puts "Not enough funds. Exiting."
46
- else
47
- puts "Trading live!"
48
- Bitstamp.orders.buy amount_to_buy, stamp[:price] + 0.001
49
- MtGox.sell! amount_to_buy, :market
50
- Bitstamp.transfer amount_to_buy, ENV['MTGOX_ADDRESS']
51
- end
52
- else
53
- if paid > mtgox[:usd] || amount_to_buy > stamp[:btc]
54
- puts "Not enough funds. Exiting."
55
- else
56
- puts "Trading live!"
57
- MtGox.buy! amount_to_buy, :market
58
- Bitstamp.orders.sell amount_to_buy, stamp[:price] - 0.001
59
- MtGox.withdraw amount_to_buy, ENV['BITSTAMP_ADDRESS']
60
- end
61
- end
62
- end
63
- 0
10
+ RbtcArbitrage::Trader.new(options).trade
64
11
  end
65
12
 
66
13
  default_task :trade
67
-
68
- private
69
-
70
- def validate_env
71
- ["KEY", "SECRET", "ADDRESS"].each do |suffix|
72
- ["MTGOX", "BITSTAMP"].each do |prefix|
73
- key = "#{prefix}_#{suffix}"
74
- if ENV[key].blank?
75
- puts "Exiting because missing required ENV variable $#{key}."
76
- return 0
77
- end
78
- end
79
- end
80
-
81
- MtGox.configure do |config|
82
- config.key = ENV["MTGOX_KEY"]
83
- config.secret = ENV["MTGOX_SECRET"]
84
- end
85
-
86
- Bitstamp.setup do |config|
87
- config.key = ENV["BITSTAMP_KEY"]
88
- config.secret = ENV["BITSTAMP_SECRET"]
89
- end
90
- end
91
-
92
- def get_balance
93
- balances = MtGox.balance
94
- @mtgox[:btc] = balances[0].amount.to_f
95
- @mtgox[:usd] = balances[1].amount.to_f
96
- balances = Bitstamp.balance if options[:live]
97
- @stamp[:usd] = balances["usd_available"].to_f
98
- @stamp[:btc] = balances["btc_available"].to_f
99
- end
14
+
100
15
  end
101
16
  end
@@ -0,0 +1,107 @@
1
+ module RbtcArbitrage
2
+ class Trader
3
+ attr_accessor :stamp, :mtgox, :paid, :received, :options, :percent, :amount_to_buy
4
+
5
+ def initialize options={}
6
+ @stamp = {}
7
+ @mtgox = {}
8
+ @options = options
9
+ @options[:volume] ||= 0.01
10
+ @options[:cutoff] ||= 2
11
+
12
+ self
13
+ end
14
+
15
+ def trade
16
+ fetch_prices
17
+ log_info if options[:verbose]
18
+
19
+ if options[:cutoff] > percent
20
+ raise SecurityError, "Exiting because real profit (#{percent.round(2)}%) is less than cutoff (#{options[:cutoff].round(2)}%)"
21
+ end
22
+
23
+ execute_trade if options[:live]
24
+
25
+ self
26
+ end
27
+
28
+ def execute_trade
29
+ validate_env
30
+ get_balance
31
+ raise SecurityError, "--live flag is false. Not executing trade." unless options[:live]
32
+ if options[:verbose]
33
+ puts "Balances:"
34
+ puts "stamp usd: $#{stamp[:usd].round(2)} btc: #{stamp[:usd].round(2)}"
35
+ puts "mtgox usd: $#{mtgox[:usd].round(2)} btc: #{mtgox[:usd].round(2)}"
36
+ end
37
+ if stamp[:price] < mtgox[:price]
38
+ if paid > stamp[:usd] || amount_to_buy > mtgox[:btc]
39
+ raise SecurityError, "Not enough funds. Exiting."
40
+ else
41
+ puts "Trading live!" if options[:verbose]
42
+ Bitstamp.orders.buy amount_to_buy, stamp[:price] + 0.001
43
+ MtGox.sell! amount_to_buy, :market
44
+ Bitstamp.transfer amount_to_buy, ENV['MTGOX_ADDRESS']
45
+ end
46
+ else
47
+ if paid > mtgox[:usd] || amount_to_buy > stamp[:btc]
48
+ raise SecurityError, "Not enough funds. Exiting."
49
+ else
50
+ puts "Trading live!" if options[:verbose]
51
+ MtGox.buy! amount_to_buy, :market
52
+ Bitstamp.orders.sell amount_to_buy, stamp[:price] - 0.001
53
+ MtGox.withdraw amount_to_buy, ENV['BITSTAMP_ADDRESS']
54
+ end
55
+ end
56
+ end
57
+
58
+ def fetch_prices
59
+ @amount_to_buy = options[:volume]
60
+ stamp[:price] = Bitstamp.ticker.ask.to_f
61
+ mtgox[:price] = MtGox.ticker.buy
62
+ prices = [stamp[:price], mtgox[:price]]
63
+ @paid = prices.min * 1.005 * amount_to_buy
64
+ @received = prices.max * 0.994 * amount_to_buy
65
+ @percent = ((received/paid - 1) * 100).round(2)
66
+ end
67
+
68
+ def log_info
69
+ puts "Bitstamp: $#{stamp[:price].round(2)}"
70
+ puts "MtGox: $#{mtgox[:price].round(2)}"
71
+ lower_ex, higher_ex = stamp[:price] < mtgox[:price] ? %w{Bitstamp, MtGox} : %w{MtGox, Bitstamp}
72
+ puts "buying #{amount_to_buy} btc from #{lower_ex} for $#{paid.round(2)}"
73
+ puts "selling #{amount_to_buy} btc on #{higher_ex} for $#{received.round(2)}"
74
+ puts "profit: $#{(received - paid).round(2)} (#{percent.round(2)}%)"
75
+ end
76
+
77
+ def validate_env
78
+ ["KEY", "SECRET", "ADDRESS"].each do |suffix|
79
+ ["MTGOX", "BITSTAMP"].each do |prefix|
80
+ key = "#{prefix}_#{suffix}"
81
+ if ENV[key].blank?
82
+ raise ArgumentError, "Exiting because missing required ENV variable $#{key}."
83
+ end
84
+ end
85
+ end
86
+
87
+ MtGox.configure do |config|
88
+ config.key = ENV["MTGOX_KEY"]
89
+ config.secret = ENV["MTGOX_SECRET"]
90
+ end
91
+
92
+ Bitstamp.setup do |config|
93
+ config.key = ENV["BITSTAMP_KEY"]
94
+ config.secret = ENV["BITSTAMP_SECRET"]
95
+ end
96
+ end
97
+
98
+ def get_balance
99
+ balances = MtGox.balance
100
+ @mtgox[:btc] = balances[0].amount.to_f
101
+ @mtgox[:usd] = balances[1].amount.to_f
102
+ balances = Bitstamp.balance if options[:live]
103
+ @stamp[:usd] = balances["usd_available"].to_f
104
+ @stamp[:btc] = balances["btc_available"].to_f
105
+ end
106
+ end
107
+ end
@@ -1,3 +1,3 @@
1
1
  module RbtcArbitrage
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'rbtc_arbitrage'
8
+
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ describe RbtcArbitrage::Trader do
3
+ before :each do
4
+ @trader = RbtcArbitrage::Trader.new
5
+
6
+ #clear env variables
7
+ ["KEY", "SECRET", "ADDRESS"].each do |suffix|
8
+ ["MTGOX", "BITSTAMP"].each do |prefix|
9
+ key = "#{prefix}_#{suffix}"
10
+ ENV[key] = nil
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "#validate_env" do
16
+ it "should raise errors when missing env variable" do
17
+ ["KEY", "SECRET", "ADDRESS"].each do |suffix|
18
+ ["MTGOX", "BITSTAMP"].each do |prefix|
19
+ key = "#{prefix}_#{suffix}"
20
+ expect { @trader.validate_env }.to raise_error(ArgumentError, "Exiting because missing required ENV variable $#{key}.")
21
+ ENV[key] = "some value"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "#fetch_prices" do
28
+ it "gets the right price set" do
29
+ stamp_price = Bitstamp.ticker.ask.to_f
30
+ mtgox_price = MtGox.ticker.buy
31
+
32
+ @trader.fetch_prices
33
+
34
+ #allow for recent price changes
35
+ @trader.mtgox[:price].should be_within(0.02).of(mtgox_price)
36
+ @trader.stamp[:price].should be_within(0.02).of(stamp_price)
37
+ end
38
+ end
39
+
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtc_arbitrage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hank Stoever
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-08 00:00:00.000000000 Z
11
+ date: 2013-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -117,6 +117,8 @@ extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - .gitignore
120
+ - .rspec
121
+ - .travis.yml
120
122
  - Gemfile
121
123
  - LICENSE.txt
122
124
  - README.md
@@ -124,8 +126,11 @@ files:
124
126
  - bin/rbtc
125
127
  - lib/rbtc_arbitrage.rb
126
128
  - lib/rbtc_arbitrage/cli.rb
129
+ - lib/rbtc_arbitrage/trader.rb
127
130
  - lib/rbtc_arbitrage/version.rb
128
131
  - rbtc_arbitrage.gemspec
132
+ - spec/spec_helper.rb
133
+ - spec/trader_spec.rb
129
134
  homepage: ''
130
135
  licenses:
131
136
  - MIT
@@ -150,5 +155,7 @@ rubygems_version: 2.0.3
150
155
  signing_key:
151
156
  specification_version: 4
152
157
  summary: A gem for conducting arbitrage with Bitcoin.
153
- test_files: []
158
+ test_files:
159
+ - spec/spec_helper.rb
160
+ - spec/trader_spec.rb
154
161
  has_rdoc: