bitshares 0.1.4.pre → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d78dd854953b6dd4587d6325f78ed684d00cc73a
4
- data.tar.gz: 398253f2043e57805839cc50f5a6c1c7be05dc22
3
+ metadata.gz: f764f9e5559cb963c49a566f4467d52af3885c91
4
+ data.tar.gz: 396b6b1131c8cbb7bcc05c12cc4d76602b472423
5
5
  SHA512:
6
- metadata.gz: f847a6172160020215ebc108a63b6cd7b1ab9d0b48e07535996a328f17ec09cfc39ce062ec248c341d449ec2153a7a6cb8062208d9fcc658b0b752228c43d8d6
7
- data.tar.gz: 16d6cdebae1a700b0c9dc6d7708d550d7a1c9611fae623ed395ed3010d9610e4ad37b5b9b4483f10913a8052251cab24a7c682ebcd7abc169d7639a6e6edd244
6
+ metadata.gz: 84208a411b47edd4c940f20db8ed53e3c0f184180579c1d84cb5308ed752599a6622557ec13e5a55dfaabfe109b80159e3dbf6ca0b714505b22a4bd109680a17
7
+ data.tar.gz: b4d3420d3ca5d92200f0f3d67b9f0bdbd54d54a38eb8dcd2a7dabc5e3644f1903389f55806e28ddadf77422d177cfd8ea491ef9025ffd6da5d7fa0d0aa165417
data/README.md CHANGED
@@ -40,8 +40,8 @@ To use this functionality - i.e. with the Wallet class (see below) wallet names
40
40
 
41
41
  **Via a hash**
42
42
  ```Ruby
43
- Bitshares.configure(:wallet => {:name => 'wallet1', :password => 'password1'})
44
- Bitshares.configure(:wallet => {:name => 'wallet2', :password => 'password2'})
43
+ Bitshares.configure(:wallet => {'wallet1' => 'password1'})
44
+ Bitshares.configure(:wallet => {'wallet2' => 'password2'})
45
45
  ...
46
46
  ```
47
47
 
@@ -61,7 +61,8 @@ Bitshares.config # returns the configuration hash
61
61
  ```ruby
62
62
  require 'bitshares'
63
63
 
64
- client = Bitshares::Client.init # The object BitShares RPC client calls are routed to.
64
+ client = CLIENT.init # CLIENT = Bitshares::Client -the object BitShares RPC client calls are routed to.
65
+ client.synced? # if you wish to check whether you are synced with the p2p network.
65
66
  ```
66
67
  Any valid client command can then be issued via a method call with relevant parameters - e.g.
67
68
 
@@ -81,7 +82,7 @@ Data is returned as a hash
81
82
 
82
83
  The blockchain is implemented as a class purely for convenience when calling 'blockchain_' methods:
83
84
  ```Ruby
84
- chain = Bitshares::Blockchain
85
+ chain = Bitshares::Blockchain # CHAIN may be used
85
86
  count = chain.get_block_count # equivalent to client.blockchain_get_block_count
86
87
  ```
87
88
 
@@ -133,42 +134,69 @@ wallet.account_balance # lists the balances for all accounts for this wallet (c.
133
134
 
134
135
  The market class represents the trading (order book and history) for a given an asset-pair - e.g. CNY/BTS. It is instantiated like this:
135
136
  ```Ruby
136
- market = Bitshares::Market.new('CNY', 'BTS')
137
+ cny_bts = Bitshares::Market.new('CNY', 'BTS')
137
138
  ```
138
- The following 'blockchain_market_' client methods may then be used without specifying the quote and base assets again, but with any other optional params the client accepts:
139
+ _Note that the BitShares market convention is that quote asset_id > base asset_id. Reversing the symbols in the above example results in the client returning an 'Invalid Market' error._ An asset's id can be found from the asset hash by using:
139
140
  ```Ruby
140
- market.list_asks # equivalent to blockchain_market_list_asks(quote, base) [limit]
141
- market.list_bids
142
- market.list_covers
143
- market.order_book
144
- market.order_history
145
- market.price_history # required params are: <start time> <duration> optional: [granularity]
141
+ Bitshares::Blockchain.get_asset 'CNY' # for example
142
+ ```
143
+
144
+ The following 'blockchain_market_' client methods may then be used without specifying the quote and base assets again, but with any other optional args the client accepts:
145
+ ```Ruby
146
+ cny_bts.list_asks # equivalent to blockchain_market_list_asks(quote, base) [limit]
147
+ cny_bts.list_bids
148
+ cny_bts.list_covers
149
+ cny_bts.order_book
150
+ cny_bts.order_history
151
+ cny_bts.price_history # required params are: <start time> <duration> optional: [granularity]
146
152
 
147
- market.list_shorts # requires no params and ignores the base asset
148
- get_asset_collateral # requires no params and returns the collateral for the quote asset (ignores the base asset)
153
+ cny_bts.list_shorts # requires no params and ignores the base asset
154
+ cny_bts.get_asset_collateral # requires no params and returns the collateral for the quote asset (ignores the base asset)
149
155
  ```
150
156
 
151
157
  Additionally, the following methods are available:
152
158
  ```Ruby
153
- market.lowest_ask
154
- market.highest_bid
155
- market.mid_price # mean of the above
156
- market.last_fill # price of the last filled order
159
+ cny_bts.lowest_ask
160
+ cny_bts.highest_bid
161
+ cny_bts.mid_price # mean of the above
162
+ cny_bts.last_fill # price of the last filled order
163
+ ```
164
+
165
+ **Trading**
166
+
167
+ So, once we have an account and a market, what do we need to trade - why a Trader of course!
168
+
169
+ ```Ruby
170
+ cny_bts_trader = Bitshares::Trader.new(account, cny_bts) # using examples above
171
+ ```
172
+
173
+ You can now do this:
174
+ ```Ruby
175
+ cny_bts_trader.order_list # lists orders for the account and market - optional limit arg. Returns orders array
176
+
177
+ cny_bts_trader.submit_bid(quantity, price) # buy <quantity> of Market base (BTS here) at <price> (quote/base)
178
+ cny_bts_trader.submit_ask(quantity, price) # sell <quantity> of Market base (BTS here) at <price> (quote/base)
179
+ # both return respective order id
180
+
181
+ cny_bts_trader.cancel_orders(*order_ids) # cancels one or more orders for the account and market
182
+ # returns array of memo's e.g. 'cancel ASK-90189b6e'
157
183
  ```
158
184
 
159
185
  ## Specification & tests
160
186
 
161
- For the full specification please clone this repo and run:
187
+ For the full specification clone this repo and run:
162
188
 
163
189
  `rake spec`
164
190
 
165
- _Important:_ There is currently no sandbox, so the test suite runs on your live client. If this concerns you - and it should :scream: - feel free to browse the code. In particular, the following client 'fixtures' are required for the full test suite to run and pass:
191
+ **Test Requirements**
192
+
193
+ There is currently no test blockchain, so the test suite runs on the live one - orders and all. If this concerns you - and it should :scream: - feel free to browse the test code first. The following client 'fixtures' are required for the full test suite to run and pass:
166
194
 
167
- An empty wallet 'test1', with password 'password1' and an account called 'account-test' (please don't register this account!)
195
+ An empty wallet 'test1', with password 'password1' and an account called 'account-test' *Please don't register this account!*. The account will also need funding with a few BTS as trades/cancellations are 0.5 BTS each. 10 BTS (circa 2.5 cents right now) should be more than enough to run the suite a few times.
168
196
 
169
197
  ## Contributing
170
198
 
171
- Bug reports and pull requests (and feature requests) are welcome on GitHub at https://github.com/MatzFan/bitshares-ruby.
199
+ Bug reports, pull requests (and feature requests) are welcome on GitHub at https://github.com/MatzFan/bitshares-ruby.
172
200
 
173
201
 
174
202
  ## License
@@ -10,10 +10,15 @@ require 'bitshares/blockchain'
10
10
  require 'bitshares/wallet'
11
11
  require 'bitshares/account'
12
12
  require 'bitshares/market'
13
+ require 'bitshares/trader'
14
+
15
+ CLIENT = Bitshares::Client
16
+ CHAIN = Bitshares::Blockchain
13
17
 
14
18
  # stackoverflow.com/questions/6233124/where-to-place-access-config-file-in-gem
15
19
  module Bitshares
16
- @config = {:wallets => {nil => nil}} # name/password key/value pairs
20
+
21
+ @config = {:wallet => {nil => nil}} # name/password key/value pairs
17
22
 
18
23
  @valid_keys = @config.keys
19
24
 
@@ -2,8 +2,6 @@ module Bitshares
2
2
 
3
3
  class Account
4
4
 
5
- CLIENT = Bitshares::Client
6
-
7
5
  attr_reader :wallet, :name
8
6
 
9
7
  def initialize(wallet, name)
@@ -12,7 +10,7 @@ module Bitshares
12
10
  end
13
11
 
14
12
  def method_missing(m, *args)
15
- CLIENT::rpc.request('wallet_account_' + m.to_s, [name] + args)
13
+ CLIENT.request('wallet_account_' + m.to_s, [name] + args)
16
14
  end
17
15
 
18
16
  end
@@ -3,7 +3,7 @@ module Bitshares
3
3
  class Blockchain
4
4
 
5
5
  def self.method_missing(name, *args)
6
- Bitshares::Client::rpc.request('blockchain_' + name.to_s, args)
6
+ CLIENT.request('blockchain_' + name.to_s, args)
7
7
  end
8
8
 
9
9
  end
@@ -2,66 +2,54 @@ module Bitshares
2
2
 
3
3
  class Client
4
4
 
5
+ class Err < RuntimeError; end
6
+
5
7
  def self.init
6
- @user = ENV['BITSHARES_ACCOUNT']
7
- @password = ENV['BITSHARES_PASSWORD']
8
- @@rpc_instance = Bitshares::Client::Rpc.new(@user, @password)
8
+ bitshares_running?
9
+ user = ENV['BITSHARES_ACCOUNT']
10
+ password = ENV['BITSHARES_PASSWORD']
11
+ @uri = URI("http://localhost:#{rpc_http_port}/rpc")
12
+ @req = Net::HTTP::Post.new(@uri)
13
+ @req.content_type = 'application/json'
14
+ @req.basic_auth user, password
9
15
  return self
10
16
  end
11
17
 
12
- def self.rpc
13
- @@rpc_instance
14
- end
15
-
16
18
  def self.synced?
17
19
  blockchain_get_block_count >= self.get_info['blockchain_head_block_num']
18
20
  end
19
21
 
20
22
  def self.method_missing(m, *args)
21
- @@rpc_instance.request(m, args)
23
+ self.request(m, args)
22
24
  end
23
25
 
24
- class Rpc
25
-
26
- class Err < RuntimeError; end
27
-
28
- def initialize(user, password)
29
- bitshares_running?
30
- @uri = URI("http://localhost:#{rpc_http_port}/rpc")
31
- @req = Net::HTTP::Post.new(@uri)
32
- @req.content_type = 'application/json'
33
- @req.basic_auth user, password
34
- end
35
-
36
- def request(m, args = [])
37
- resp = nil
38
- Net::HTTP.start(@uri.hostname, @uri.port) do |http|
39
- @req.body = { method: m, params: args, jsonrpc: '2.0', id: 0 }.to_json
40
- resp = http.request(@req)
41
- end
42
- raise Err, 'Bad credentials' if resp.class == Net::HTTPUnauthorized
43
- result = JSON.parse(resp.body)
44
- e = result['error']
45
- raise Err, JSON.pretty_generate(e), "#{m} #{args.join(' ') if args}" if e
46
- return result['result']
26
+ def self.request(m, args = [])
27
+ resp = nil
28
+ Net::HTTP.start(@uri.hostname, @uri.port) do |http|
29
+ @req.body = { method: m, params: args, jsonrpc: '2.0', id: 0 }.to_json
30
+ resp = http.request(@req)
47
31
  end
32
+ raise Err, 'Bad credentials' if resp.class == Net::HTTPUnauthorized
33
+ result = JSON.parse(resp.body)
34
+ e = result['error']
35
+ raise Err, JSON.pretty_generate(e), "#{m} #{args.join(' ') if args}" if e
36
+ return result['result']
37
+ end
48
38
 
49
- private
50
-
51
- def bitshares_running?
52
- raise Err, 'Server not running!' unless rpc_ports.count == 2
53
- end
39
+ private
54
40
 
55
- def rpc_http_port
56
- rpc_ports.each do |port| # only http RPC port raises a non-empty response
57
- return port unless `curl -s -I -L http://localhost:#{port}`.empty?
58
- end
59
- end
41
+ def self.bitshares_running?
42
+ raise Err, 'Server not running!' unless rpc_ports.count == 2
43
+ end
60
44
 
61
- def rpc_ports # returns bitshares HTTP JSON RPC and JSON RPC server ports
62
- `lsof -iTCP@localhost | grep bitshares`.scan(/:(\d+) \(LISTEN\)/).flatten
45
+ def self.rpc_http_port
46
+ rpc_ports.each do |port| # only http RPC port raises a non-empty response
47
+ return port unless `curl -s -I -L http://localhost:#{port}`.empty?
63
48
  end
49
+ end
64
50
 
51
+ def self.rpc_ports # returns bitshares HTTP JSON RPC and JSON RPC server ports
52
+ `lsof -iTCP@localhost | grep bitshares`.scan(/:(\d+) \(LISTEN\)/).flatten
65
53
  end
66
54
 
67
55
  end
@@ -2,10 +2,7 @@ module Bitshares
2
2
 
3
3
  class Market
4
4
 
5
- class AssetError < RuntimeError; end
6
-
7
- CHAIN = Bitshares::Blockchain
8
- CLIENT = Bitshares::Client
5
+ class Error < RuntimeError; end
9
6
 
10
7
  attr_reader :quote, :base
11
8
 
@@ -13,6 +10,7 @@ module Bitshares
13
10
  [quote, base].each &:upcase!
14
11
  @quote_hash, @base_hash = asset(quote), asset(base)
15
12
  @quote, @base = @quote_hash['symbol'], @base_hash['symbol']
13
+ valid_market?(@quote_hash['id'], @base_hash['id'])
16
14
  @multiplier = multiplier
17
15
  end
18
16
 
@@ -37,21 +35,25 @@ module Bitshares
37
35
  end
38
36
 
39
37
  def list_shorts(limit = nil) # uses quote only, not base
40
- CLIENT::rpc.request('blockchain_market_list_shorts', [quote] + [limit])
38
+ CLIENT.request('blockchain_market_list_shorts', [quote] + [limit])
41
39
  end
42
40
 
43
41
  def get_asset_collateral # uses quote only, not base
44
- CLIENT::rpc.request('blockchain_market_get_asset_collateral', [quote])
42
+ CLIENT.request('blockchain_market_get_asset_collateral', [quote])
45
43
  end
46
44
 
47
45
  def method_missing(m, *args)
48
- CLIENT::rpc.request('blockchain_market_' + m.to_s, [quote, base] + args)
46
+ CLIENT.request('blockchain_market_' + m.to_s, [quote, base] + args)
49
47
  end
50
48
 
51
49
  private
52
50
 
53
51
  def asset(symbol) # returns hash
54
- CHAIN.get_asset(symbol) || (raise AssetError, "Invalid asset: #{symbol}")
52
+ CHAIN.get_asset(symbol) || (raise Error, "Invalid asset: #{symbol}")
53
+ end
54
+
55
+ def valid_market?(quote_id, base_id)
56
+ raise Error, 'Invalid market; quote ID <= base ID' if quote_id <= base_id
55
57
  end
56
58
 
57
59
  def order_hist
@@ -0,0 +1,57 @@
1
+ module Bitshares
2
+
3
+ class Trader
4
+
5
+ class Error < RuntimeError; end
6
+
7
+ WMSA = 'wallet_market_submit_ask'
8
+ WMSB = 'wallet_market_submit_bid'
9
+ WMOL = 'wallet_market_order_list'
10
+ WMCO = 'wallet_market_cancel_orders'
11
+
12
+ attr_reader :account, :market
13
+
14
+ def initialize(account, market)
15
+ @account = account
16
+ @wallet = account.wallet
17
+ @name = account.name
18
+ @market = market
19
+ @base = market.base
20
+ @quote = market.quote
21
+ end
22
+
23
+ def submit_ask(quantity, price, stupid = false)
24
+ @wallet.unlock if @wallet.locked?
25
+ o = CLIENT.request(WMSA, [@name, quantity, @base, price, @quote, stupid])
26
+ o['record_id'] # return order id
27
+ end
28
+
29
+ def submit_bid(quantity, price, stupid = false)
30
+ @wallet.unlock if @wallet.locked?
31
+ o = CLIENT.request(WMSB, [@name, quantity, @base, price, @quote, stupid])
32
+ o['record_id'] # return order id
33
+ end
34
+
35
+ def order_list(limit = '-1')
36
+ CLIENT.request(WMOL, [@quote, @base, limit, @name])
37
+ end
38
+
39
+ def cancel_orders(*id_list)
40
+ @wallet.unlock if @wallet.locked?
41
+ confirm = CLIENT.request(WMCO, id_list)
42
+ confirm['ledger_entries'].map { |e| e['memo'] } # returns 'cancel ASK-xxxxxxxx' first 8 chars of order id
43
+ end
44
+
45
+ def cancel_all
46
+ cancel_orders all_order_ids
47
+ end
48
+
49
+ private
50
+
51
+ def all_order_ids
52
+ order_list.map &:first
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -1,3 +1,3 @@
1
1
  module Bitshares
2
- VERSION = '0.1.4.pre'
2
+ VERSION = '0.1.5'
3
3
  end
@@ -7,7 +7,7 @@ module Bitshares
7
7
  def initialize(name)
8
8
  @name = name
9
9
  @account = nil
10
- @password = Bitshares.config[:wallets][@name.to_sym]
10
+ @password = Bitshares.config[:wallet][@name]
11
11
  end
12
12
 
13
13
  def account(name)
@@ -15,17 +15,17 @@ module Bitshares
15
15
  end
16
16
 
17
17
  def open
18
- Bitshares::Client::rpc.request('wallet_open', [@name])
18
+ CLIENT.request('wallet_open', [@name])
19
19
  end
20
20
 
21
21
  def lock
22
22
  open # must be opened first
23
- Bitshares::Client::rpc.request 'wallet_lock'
23
+ CLIENT.request 'wallet_lock'
24
24
  end
25
25
 
26
26
  def unlock(timeout = 1776)
27
27
  open # must be opened first
28
- Bitshares::Client::rpc.request('wallet_unlock', [timeout, @password])
28
+ CLIENT.request('wallet_unlock', [timeout, @password])
29
29
  end
30
30
 
31
31
  def open?
@@ -46,7 +46,7 @@ module Bitshares
46
46
  end
47
47
 
48
48
  def method_missing(m, *args)
49
- Bitshares::Client::rpc.request('wallet_' + m.to_s, args)
49
+ CLIENT.request('wallet_' + m.to_s, args)
50
50
  end
51
51
 
52
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitshares
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4.pre
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruce Steedman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-08-12 00:00:00.000000000 Z
11
+ date: 2015-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -102,6 +102,7 @@ files:
102
102
  - lib/bitshares/blockchain.rb
103
103
  - lib/bitshares/client.rb
104
104
  - lib/bitshares/market.rb
105
+ - lib/bitshares/trader.rb
105
106
  - lib/bitshares/version.rb
106
107
  - lib/bitshares/wallet.rb
107
108
  homepage:
@@ -119,9 +120,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
120
  version: '0'
120
121
  required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  requirements:
122
- - - ">"
123
+ - - ">="
123
124
  - !ruby/object:Gem::Version
124
- version: 1.3.1
125
+ version: '0'
125
126
  requirements: []
126
127
  rubyforge_project:
127
128
  rubygems_version: 2.4.8