bitstampede 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.3@bitstampede --create
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bitstampede.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'coveralls', require: false
8
+ end
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Josh Adams
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Bitstampede
2
+ [![Build Status](https://travis-ci.org/isotope11/bitstampede.png?branch=master)](https://travis-ci.org/isotope11/bitstampede) [![Coverage Status](https://coveralls.io/repos/isotope11/bitstampede/badge.png?branch=master)](https://coveralls.io/r/isotope11/bitstampede?branch=master) [![Code Climate](https://codeclimate.com/github/isotope11/bitstampede.png)](https://codeclimate.com/github/isotope11/bitstampede)
3
+
4
+
5
+ Bitstampede is a gem for accessing the Bitstamp API
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'bitstampede'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bitstampede
20
+
21
+ ## Usage
22
+
23
+ First, look at this picture:
24
+
25
+ ![Legitimate Concern](./doc/legitimate_concern.png)
26
+
27
+ Second, stop writing api clients that are configured with class ivars ಠ_ಠ
28
+
29
+ Third, this stuff:
30
+
31
+ ## Actual Usage Without Silly Pictures
32
+
33
+ ```ruby
34
+ client = Bitstampede::Client.new
35
+
36
+ # I am sad for the following, but such is Bitstamp at present :-\
37
+ client.key = 'YOUR_USER_ID'
38
+ client.secret = 'YOUR_PASSWORD'
39
+
40
+ # Fetch your balance
41
+ client.balance
42
+ # => #<Bitstampede::Entities::Balance:0x0000000259f338 @usd_balance=#<BigDecimal:259e898,'0.0',9(9)>, @btc_balance=#<BigDecimal:2726698,'0.0',9(9)>, @usd_reserved=#<BigDecimal:2726328,'0.0',9(9)>, @btc_reserved=#<BigDecimal:2725fb8,'0.0',9(9)>, @usd_available=#<BigDecimal:2725c48,'0.0',9(9)>, @btc_available=#<BigDecimal:27258b0,'0.0',9(9)>, @fee=#<BigDecimal:2725540,'0.0',9(9)>>
43
+
44
+ client.orders
45
+ #=> [
46
+ #<Bitstampede::Entities::Order:0x000000027302d8 @id=0, @datetime=0, @type=:buy, @price=#<BigDecimal:272f428,'0.0',9(9)>, @amount=#<BigDecimal:272f130,'0.0',9(9)>>
47
+ ]
48
+
49
+ # Place a limit order to buy one bitcoin for $100.00 USD
50
+ client.buy!(BigDecimal('1'), BigDecimal('100'))
51
+
52
+ # Place a limit order to sell one bitcoin for $101.00 USD
53
+ client.sell!(BigDecimal('1'), BigDecimal('101'))
54
+
55
+ # Cancel order #1234
56
+ client.cancel 1234
57
+ ```
58
+
59
+ ## Examples
60
+
61
+ You can run any of the examples in the `example` dir by just executing the script (except `example/example.rb`, which is the base example class). For instance, to see your balance, do the following:
62
+
63
+ ```ruby
64
+ ruby example/balance.rb
65
+ ```
66
+
67
+ ## Contributing
68
+
69
+ 1. Fork it
70
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
71
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
72
+ 4. Push to the branch (`git push origin my-new-feature`)
73
+ 5. Create new Pull Request
74
+
75
+ ## License
76
+
77
+ This software is licensed under [the MIT License.](./LICENSE.md)
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ require 'rspec/core/rake_task'
10
+
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bitstampede/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bitstampede"
8
+ spec.version = Bitstampede::VERSION
9
+ spec.authors = ["Josh Adams"]
10
+ spec.email = ["josh@isotope11.com"]
11
+ spec.description = %q{This is a client library for the Bitstamp API that supports instantiating multiple clients in the same process.}
12
+ spec.summary = %q{Fantastic bitstamp library.}
13
+ spec.homepage = "http://github.com/isotope11/bitstampede"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'httparty'
22
+ spec.add_dependency 'multi_json'
23
+ spec.add_dependency 'yajl-ruby'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec", '2.14.0.rc1'
28
+ spec.add_development_dependency "fakeweb"
29
+ spec.add_development_dependency "pry"
30
+ end
Binary file
@@ -0,0 +1,5 @@
1
+ require_relative './example'
2
+
3
+ Example.new do |client|
4
+ client.balance
5
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../lib/bitstampede'
2
+ require 'pry'
3
+
4
+ class Example
5
+ def initialize &block
6
+ validate_env_vars
7
+ client = Bitstampede::Client.new
8
+ client.key = key
9
+ client.secret = secret
10
+ output block.call(client).inspect
11
+ end
12
+
13
+ private
14
+ def validate_env_vars
15
+ print_env_var_message if missing_either_var?
16
+ end
17
+
18
+ def missing_either_var?
19
+ key.nil? || secret.nil?
20
+ end
21
+
22
+ def key
23
+ ENV["BITSTAMP_KEY"]
24
+ end
25
+
26
+ def secret
27
+ ENV["BITSTAMP_SECRET"]
28
+ end
29
+
30
+ def print_env_var_message
31
+ output <<-MSG
32
+ These examples assume that you have two env vars defined:
33
+
34
+ BITSTAMP_KEY (which would be your user id)
35
+
36
+ and
37
+
38
+ BITSTAMP_SECRET (which would be your password)
39
+
40
+ You don't appear to. Rectify that, mnkay?
41
+ MSG
42
+ exit(1)
43
+ end
44
+
45
+ def output(message)
46
+ STDOUT.puts message
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ require_relative './example'
2
+
3
+ Example.new do |client|
4
+ client.orders
5
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'net'
2
+ require_relative 'mapper'
3
+ require 'bigdecimal/util'
4
+
5
+ module Bitstampede
6
+ class Client
7
+ attr_accessor :key
8
+ attr_accessor :secret
9
+
10
+ def initialize
11
+ end
12
+
13
+ def balance
14
+ mapper.map_balance(net.post("balance"))
15
+ end
16
+
17
+ def orders
18
+ mapper.map_orders(net.post("open_orders"))
19
+ end
20
+
21
+ def buy!(price, amount)
22
+ trade!("buy", price, amount)
23
+ end
24
+
25
+ def sell!(price, amount)
26
+ trade!("sell", price, amount)
27
+ end
28
+
29
+ def cancel(id)
30
+ mapper.map_cancel(net.post("cancel_order", { id: id.to_s }))
31
+ end
32
+
33
+ private
34
+ def net
35
+ @net ||= Net.new(self)
36
+ end
37
+
38
+ def mapper
39
+ @mapper ||= Mapper.new
40
+ end
41
+
42
+ def trade!(type, price, amount)
43
+ mapper.map_order(net.post(type, { price: price.to_digits, amount: amount.to_digits }))
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,21 @@
1
+ require_relative './base'
2
+
3
+ module Bitstampede
4
+ module Entities
5
+ class Balance < Base
6
+ def self.mappings
7
+ {
8
+ usd_balance: map_decimal,
9
+ btc_balance: map_decimal,
10
+ usd_reserved: map_decimal,
11
+ btc_reserved: map_decimal,
12
+ usd_available: map_decimal,
13
+ btc_available: map_decimal,
14
+ fee: map_decimal
15
+ }
16
+ end
17
+
18
+ setup_readers
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module Bitstampede
2
+ module Entities
3
+ class Base
4
+ def self.setup_readers
5
+ keys.each {|k| attr_reader k.to_sym }
6
+ end
7
+
8
+ def self.keys
9
+ self.mappings.keys
10
+ end
11
+
12
+ def initialize(balance_hash)
13
+ self.class.keys.each do |key|
14
+ instance_variable_set("@#{key}", self.class.mappings[key].call(balance_hash[key.to_s].to_s))
15
+ end
16
+ end
17
+
18
+ def inspect
19
+ inspect_string = "#<#{self.class}:#{self.object_id} "
20
+ self.class.keys.each do |key|
21
+ inspect_string << "#{key}: #{send(key).inspect} "
22
+ end
23
+ inspect_string << " >"
24
+ inspect_string
25
+ end
26
+
27
+ def self.map_int
28
+ ->(val) { val.to_i }
29
+ end
30
+
31
+ def self.map_decimal
32
+ ->(val) { BigDecimal(val) }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require_relative './base'
2
+
3
+ module Bitstampede
4
+ module Entities
5
+ class Order < Base
6
+ class InvalidTypeError < StandardError; end
7
+
8
+ def self.map_type
9
+ ->(val) do
10
+ case val.to_s
11
+ when '0'
12
+ :buy
13
+ when '1'
14
+ :sell
15
+ else
16
+ raise InvalidTypeError
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.mappings
22
+ {
23
+ id: map_int,
24
+ datetime: map_int,
25
+ type: map_type,
26
+ price: map_decimal,
27
+ amount: map_decimal
28
+ }
29
+ end
30
+
31
+ setup_readers
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ require_relative './entities/balance'
2
+ require_relative './entities/order'
3
+
4
+ module Bitstampede
5
+ class Mapper
6
+ def initialize
7
+ end
8
+
9
+ def map_balance(json)
10
+ Entities::Balance.new(parsed(json))
11
+ end
12
+
13
+ def map_orders(json)
14
+ parsed(json).map{|o| map_order(o) }
15
+ end
16
+
17
+ def map_order(order)
18
+ Entities::Order.new(parsed(order))
19
+ end
20
+
21
+ def map_cancel(result)
22
+ parsed(result) == 'true'
23
+ end
24
+
25
+ private
26
+ # Allow passing either a String or anything else in. If it's not a string,
27
+ # we assume we've already parsed it and just give it back to you. This
28
+ # allows us to handle things like collections more easily.
29
+ def parsed(json)
30
+ if(json.is_a?(String))
31
+ Bitstampede::Helpers.json_parse(json)
32
+ else
33
+ json
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require 'httparty'
2
+
3
+ module Bitstampede
4
+ class Net
5
+ attr_reader :client
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def secret
12
+ client.secret
13
+ end
14
+
15
+ def key
16
+ client.key
17
+ end
18
+
19
+ def post(endpoint, options={})
20
+ map_response(raw_post(endpoint, options))
21
+ end
22
+
23
+ private
24
+ def url_for(endpoint)
25
+ base_url + endpoint + '/'
26
+ end
27
+
28
+ def base_url
29
+ 'https://www.bitstamp.net/api/'
30
+ end
31
+
32
+ def auth_options
33
+ {
34
+ user: key,
35
+ password: secret
36
+ }
37
+ end
38
+
39
+ # For some crazy reason, bitstamp is returning ruby hash strings rather than
40
+ # JSON objects right now ಠ_ಠ I'm just going to gsub '=>' to ':' to 'solve'
41
+ # it for now. Not thrilled with this.
42
+ def map_response(wish_this_were_reliably_json)
43
+ wish_this_were_reliably_json.gsub('=>', ':')
44
+ end
45
+
46
+ def raw_post(endpoint, options)
47
+ HTTParty.post(url_for(endpoint), body: options.merge(auth_options)).to_s
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,3 @@
1
+ module Bitstampede
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,12 @@
1
+ require_relative './bitstampede/version'
2
+ require_relative './bitstampede/client'
3
+ require 'multi_json'
4
+ require 'yajl'
5
+
6
+ module Bitstampede
7
+ module Helpers
8
+ def self.json_parse(string)
9
+ MultiJson.load(string)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe "Integrating a client" do
4
+ subject{ Client.new }
5
+
6
+ before do
7
+ subject.secret = '1'
8
+ subject.key = '2'
9
+ end
10
+
11
+ it "handles #balance" do
12
+ example_balance = <<-JSON
13
+ {
14
+ "usd_balance": "12.34",
15
+ "btc_balance": "23.45",
16
+ "usd_reserved": "1.11",
17
+ "btc_reserved": "2.22",
18
+ "usd_available": "11.23",
19
+ "btc_available": "21.23",
20
+ "fee": "0.5"
21
+ }
22
+ JSON
23
+
24
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/balance/", body: example_balance)
25
+
26
+ bal = subject.balance
27
+ expect(bal.usd_balance).to eq(BigDecimal('12.34'))
28
+ end
29
+
30
+ it "handles #orders" do
31
+ example_orders = <<-JSON
32
+ [
33
+ {
34
+ "id": "1",
35
+ "datetime": "1234567",
36
+ "type": 0,
37
+ "price": "12.34",
38
+ "amount": "100"
39
+ }
40
+ ]
41
+ JSON
42
+
43
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/open_orders/", body: example_orders)
44
+
45
+ orders = subject.orders
46
+ expect(orders[0].type).to eq(:buy)
47
+ end
48
+
49
+ it "handles #buy!" do
50
+ example_buy_response = <<-JSON
51
+ {
52
+ "id": "1",
53
+ "datetime": "1234567",
54
+ "type": 0,
55
+ "price": "12.34",
56
+ "amount": "100"
57
+ }
58
+ JSON
59
+
60
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/buy/", body: example_buy_response)
61
+
62
+ buy = subject.buy!(BigDecimal('1'), BigDecimal('100'))
63
+ expect(buy.type).to eq(:buy)
64
+ end
65
+
66
+ it "handles #sell!" do
67
+ example_sell_response = <<-JSON
68
+ {
69
+ "id": "1",
70
+ "datetime": "1234567",
71
+ "type": 1,
72
+ "price": "12.34",
73
+ "amount": "100"
74
+ }
75
+ JSON
76
+
77
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/sell/", body: example_sell_response)
78
+
79
+ sell = subject.sell!(BigDecimal('1'), BigDecimal('100'))
80
+ expect(sell.type).to eq(:sell)
81
+ end
82
+
83
+ it "handles #cancel" do
84
+ example_cancel_response = <<-JSON
85
+ "true"
86
+ JSON
87
+
88
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/cancel_order/", body: example_cancel_response)
89
+
90
+ cancel = subject.cancel(12345)
91
+ expect(cancel).to eq(true)
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'rspec'
5
+ require 'rspec/autorun'
6
+ require 'bundler'
7
+ Bundler.setup
8
+
9
+ require 'fakeweb'
10
+ FakeWeb.allow_net_connect = %r[coveralls\.io] # Only allow the coveralls api call through
11
+
12
+ require 'multi_json'
13
+ def json_parse(string)
14
+ MultiJson.load(string)
15
+ end
16
+
17
+ require 'pry'
18
+
19
+ require_relative '../lib/bitstampede'
20
+ include Bitstampede
@@ -0,0 +1,110 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Bitstampede::Client do
4
+ subject { described_class.new }
5
+ let(:net){ subject.send(:net) }
6
+ let(:mapper){ subject.send(:mapper) }
7
+
8
+ before do
9
+ subject.key = '1'
10
+ subject.secret = '2'
11
+ end
12
+
13
+ describe '#balance' do
14
+ let(:api_balance_response){ double }
15
+ let(:balance_object){ double }
16
+
17
+ before do
18
+ net.stub(:post).and_return(api_balance_response)
19
+ mapper.stub(:map_balance).and_return(balance_object)
20
+ subject.balance
21
+ end
22
+
23
+ it 'requests the balance from the API' do
24
+ expect(net).to have_received(:post).with('balance')
25
+ end
26
+
27
+ it 'maps the API response to a Balance object' do
28
+ expect(mapper).to have_received(:map_balance).with(api_balance_response)
29
+ end
30
+
31
+ it 'returns the mapped object' do
32
+ expect(subject.balance).to eq(balance_object)
33
+ end
34
+ end
35
+
36
+ describe '#orders' do
37
+ let(:api_orders_response){ double }
38
+ let(:order_object){ double }
39
+
40
+ before do
41
+ net.stub(:post).and_return(api_orders_response)
42
+ mapper.stub(:map_orders).and_return([order_object])
43
+ subject.orders
44
+ end
45
+
46
+ it 'requests open orders from the API' do
47
+ expect(net).to have_received(:post).with('open_orders')
48
+ end
49
+
50
+ it 'maps the API response to an array of Order objects' do
51
+ expect(mapper).to have_received(:map_orders).with(api_orders_response)
52
+ end
53
+ end
54
+
55
+ describe 'buy!' do
56
+ let(:api_buy_response){ double }
57
+ let(:order_object){ double }
58
+
59
+ before do
60
+ net.stub(:post).and_return(api_buy_response)
61
+ mapper.stub(:map_order).and_return(order_object)
62
+ subject.buy!(BigDecimal('1'), BigDecimal('100'))
63
+ end
64
+
65
+ it 'submits a buy order to the API' do
66
+ expect(net).to have_received(:post).with('buy', { amount: '100.0', price: '1.0' })
67
+ end
68
+
69
+ it 'maps the API response to an Order object' do
70
+ expect(mapper).to have_received(:map_order).with(api_buy_response)
71
+ end
72
+ end
73
+
74
+ describe 'sell!' do
75
+ let(:api_sell_response){ double }
76
+ let(:order_object){ double }
77
+
78
+ before do
79
+ net.stub(:post).and_return(api_sell_response)
80
+ mapper.stub(:map_order).and_return(order_object)
81
+ subject.sell!(BigDecimal('1'), BigDecimal('100'))
82
+ end
83
+
84
+ it 'submits a sell order to the API' do
85
+ expect(net).to have_received(:post).with('sell', { amount: '100.0', price: '1.0' })
86
+ end
87
+
88
+ it 'maps the API response to an Order object' do
89
+ expect(mapper).to have_received(:map_order).with(api_sell_response)
90
+ end
91
+ end
92
+
93
+ describe 'cancel' do
94
+ let(:api_cancel_response){ double }
95
+
96
+ before do
97
+ net.stub(:post).and_return(api_cancel_response)
98
+ mapper.stub(:map_cancel).and_return(true)
99
+ subject.cancel(1234)
100
+ end
101
+
102
+ it 'submits a cancel order to the API' do
103
+ expect(net).to have_received(:post).with('cancel_order', { id: '1234' })
104
+ end
105
+
106
+ it 'maps the API response to a boolean' do
107
+ expect(mapper).to have_received(:map_cancel).with(api_cancel_response)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ describe Bitstampede::Entities::Balance do
4
+ let(:balance_hash){
5
+ {
6
+ "usd_balance" => "111.12",
7
+ "btc_balance" => "211.23",
8
+ "usd_reserved" => "1.20",
9
+ "btc_reserved" => "2.30",
10
+ "usd_available" => "5.50",
11
+ "btc_available" => "6.60",
12
+ "fee" => "1.11"
13
+ }
14
+ }
15
+ subject{ described_class.new(balance_hash) }
16
+
17
+ it "has a usd_balance" do
18
+ expect(subject.usd_balance).to eq(BigDecimal('111.12'))
19
+ end
20
+
21
+ it "has a btc_balance" do
22
+ expect(subject.btc_balance).to eq(BigDecimal('211.23'))
23
+ end
24
+
25
+ it "has a usd_reserved" do
26
+ expect(subject.usd_reserved).to eq(BigDecimal('1.20'))
27
+ end
28
+
29
+ it "has a btc_reserved" do
30
+ expect(subject.btc_reserved).to eq(BigDecimal('2.30'))
31
+ end
32
+
33
+ it "has a usd_available" do
34
+ expect(subject.usd_available).to eq(BigDecimal('5.50'))
35
+ end
36
+
37
+ it "has a btc_available" do
38
+ expect(subject.btc_available).to eq(BigDecimal('6.60'))
39
+ end
40
+
41
+ it "has a fee" do
42
+ expect(subject.fee).to eq(BigDecimal('1.11'))
43
+ end
44
+
45
+ it "can be inspected" do
46
+ expect { subject.inspect }.to_not raise_error
47
+ end
48
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ describe Bitstampede::Entities::Order do
4
+ let(:order_hash){
5
+ {
6
+ "id" => "1",
7
+ "datetime" => 1234567,
8
+ "type" => 0,
9
+ "price" => "1.23",
10
+ "amount" => "10"
11
+ }
12
+ }
13
+ subject{ described_class.new(order_hash) }
14
+
15
+ it "has an id" do
16
+ expect(subject.id).to eq(1)
17
+ end
18
+
19
+ describe "type" do
20
+ it "maps 0 to :buy" do
21
+ expect(subject.type).to eq(:buy)
22
+ end
23
+
24
+ it "maps 1 to :sell" do
25
+ order = described_class.new(order_hash.merge({"type" => 1}))
26
+ expect(order.type).to eq(:sell)
27
+ end
28
+
29
+ it "raises InvalidTypeError for other values" do
30
+ expect { described_class.new(order_hash.merge({"type" => 2})) }.to raise_error(Bitstampede::Entities::Order::InvalidTypeError)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Bitstampede::Mapper do
4
+ subject { described_class.new }
5
+ let(:json_object){ '{"foo": "bar"}' }
6
+ let(:json_array){ '[{"foo": "bar"}]' }
7
+
8
+ describe '#map_balance' do
9
+ let(:balance) { double }
10
+
11
+ before do
12
+ Entities::Balance.stub(:new).and_return(balance)
13
+ end
14
+
15
+ it "maps a balance API response into a Balance entity" do
16
+ subject.map_balance(json_object)
17
+ expect(Entities::Balance).to have_received(:new).with(json_parse(json_object))
18
+ end
19
+
20
+ it "returns the mapped Balance entity" do
21
+ expect(subject.map_balance(json_object)).to eq(balance)
22
+ end
23
+ end
24
+
25
+ describe '#map_orders' do
26
+ let(:order) { double }
27
+
28
+ before do
29
+ Entities::Order.stub(:new).and_return(order)
30
+ end
31
+
32
+ it "maps an open_orders API response into an array of Order entities" do
33
+ subject.map_orders(json_array)
34
+ expect(Entities::Order).to have_received(:new).with(json_parse(json_array)[0])
35
+ end
36
+ end
37
+
38
+ describe '#map_cancel' do
39
+ it "maps a cancel API response to a boolean" do
40
+ expect(subject.map_cancel('"true"')).to eq(true)
41
+ expect(subject.map_cancel('"false"')).to eq(false)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Bitstampede::Net do
4
+ let(:client){ double }
5
+ subject { described_class.new(client) }
6
+
7
+ before do
8
+ client.stub(:secret).and_return(1)
9
+ client.stub(:key).and_return(2)
10
+ end
11
+
12
+ it 'gets instantiated with a client' do
13
+ expect(subject.client).to eq(client)
14
+ end
15
+
16
+ it 'defers to its client for secret' do
17
+ expect(subject.secret).to eq(1)
18
+ end
19
+
20
+ it 'defers to its client for key' do
21
+ expect(subject.key).to eq(2)
22
+ end
23
+
24
+ describe '#post' do
25
+ describe 'any_endpoint' do
26
+ let(:example_balance) do
27
+ <<-JSON
28
+ {
29
+ "foo": "bar"
30
+ }
31
+ JSON
32
+ end
33
+
34
+ before do
35
+ FakeWeb.register_uri(:post, "https://www.bitstamp.net/api/balance/", body: example_balance)
36
+ end
37
+
38
+ it "queries the appropriate endpoint and returns its body as a string" do
39
+ expect(json_parse(subject.post('balance'))).to eq(json_parse(example_balance))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Bitstampede do
4
+ it "ought to be an instance rather than a retarded singleton like every freaking btc-related gem in existence apparently" do
5
+ client = Bitstampede::Client.new
6
+ expect(client.key).to eq(nil)
7
+ expect(client.secret).to eq(nil)
8
+ client.key = '1'
9
+ client.secret = '2'
10
+ expect(client.key).to eq('1')
11
+ expect(client.secret).to eq('2')
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitstampede
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh Adams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yajl-ruby
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 2.14.0.rc1
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 2.14.0.rc1
110
+ - !ruby/object:Gem::Dependency
111
+ name: fakeweb
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: This is a client library for the Bitstamp API that supports instantiating
143
+ multiple clients in the same process.
144
+ email:
145
+ - josh@isotope11.com
146
+ executables: []
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - .gitignore
151
+ - .rspec
152
+ - .rvmrc
153
+ - .travis.yml
154
+ - Gemfile
155
+ - LICENSE.md
156
+ - README.md
157
+ - Rakefile
158
+ - bitstampede.gemspec
159
+ - doc/legitimate_concern.png
160
+ - example/balance.rb
161
+ - example/example.rb
162
+ - example/query_orders.rb
163
+ - lib/bitstampede.rb
164
+ - lib/bitstampede/client.rb
165
+ - lib/bitstampede/entities/balance.rb
166
+ - lib/bitstampede/entities/base.rb
167
+ - lib/bitstampede/entities/order.rb
168
+ - lib/bitstampede/mapper.rb
169
+ - lib/bitstampede/net.rb
170
+ - lib/bitstampede/version.rb
171
+ - spec/integration/client_spec.rb
172
+ - spec/spec_helper.rb
173
+ - spec/unit/bitstampede/client_spec.rb
174
+ - spec/unit/bitstampede/entities/balance_spec.rb
175
+ - spec/unit/bitstampede/entities/order_spec.rb
176
+ - spec/unit/bitstampede/mapper_spec.rb
177
+ - spec/unit/bitstampede/net_spec.rb
178
+ - spec/unit/bitstampede_spec.rb
179
+ homepage: http://github.com/isotope11/bitstampede
180
+ licenses:
181
+ - MIT
182
+ post_install_message:
183
+ rdoc_options: []
184
+ require_paths:
185
+ - lib
186
+ required_ruby_version: !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ! '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ segments:
193
+ - 0
194
+ hash: 4285444086114394616
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ none: false
197
+ requirements:
198
+ - - ! '>='
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ segments:
202
+ - 0
203
+ hash: 4285444086114394616
204
+ requirements: []
205
+ rubyforge_project:
206
+ rubygems_version: 1.8.25
207
+ signing_key:
208
+ specification_version: 3
209
+ summary: Fantastic bitstamp library.
210
+ test_files:
211
+ - spec/integration/client_spec.rb
212
+ - spec/spec_helper.rb
213
+ - spec/unit/bitstampede/client_spec.rb
214
+ - spec/unit/bitstampede/entities/balance_spec.rb
215
+ - spec/unit/bitstampede/entities/order_spec.rb
216
+ - spec/unit/bitstampede/mapper_spec.rb
217
+ - spec/unit/bitstampede/net_spec.rb
218
+ - spec/unit/bitstampede_spec.rb