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 +4 -4
- data/README.md +50 -22
- data/lib/bitshares.rb +6 -1
- data/lib/bitshares/account.rb +1 -3
- data/lib/bitshares/blockchain.rb +1 -1
- data/lib/bitshares/client.rb +31 -43
- data/lib/bitshares/market.rb +10 -8
- data/lib/bitshares/trader.rb +57 -0
- data/lib/bitshares/version.rb +1 -1
- data/lib/bitshares/wallet.rb +5 -5
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f764f9e5559cb963c49a566f4467d52af3885c91
|
4
|
+
data.tar.gz: 396b6b1131c8cbb7bcc05c12cc4d76602b472423
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 => {
|
44
|
-
Bitshares.configure(:wallet => {
|
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 =
|
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
|
-
|
137
|
+
cny_bts = Bitshares::Market.new('CNY', 'BTS')
|
137
138
|
```
|
138
|
-
|
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
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
187
|
+
For the full specification clone this repo and run:
|
162
188
|
|
163
189
|
`rake spec`
|
164
190
|
|
165
|
-
|
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'
|
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
|
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
|
data/lib/bitshares.rb
CHANGED
@@ -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
|
-
|
20
|
+
|
21
|
+
@config = {:wallet => {nil => nil}} # name/password key/value pairs
|
17
22
|
|
18
23
|
@valid_keys = @config.keys
|
19
24
|
|
data/lib/bitshares/account.rb
CHANGED
@@ -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
|
13
|
+
CLIENT.request('wallet_account_' + m.to_s, [name] + args)
|
16
14
|
end
|
17
15
|
|
18
16
|
end
|
data/lib/bitshares/blockchain.rb
CHANGED
data/lib/bitshares/client.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
23
|
+
self.request(m, args)
|
22
24
|
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
50
|
-
|
51
|
-
def bitshares_running?
|
52
|
-
raise Err, 'Server not running!' unless rpc_ports.count == 2
|
53
|
-
end
|
39
|
+
private
|
54
40
|
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
62
|
-
|
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
|
data/lib/bitshares/market.rb
CHANGED
@@ -2,10 +2,7 @@ module Bitshares
|
|
2
2
|
|
3
3
|
class Market
|
4
4
|
|
5
|
-
class
|
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
|
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
|
42
|
+
CLIENT.request('blockchain_market_get_asset_collateral', [quote])
|
45
43
|
end
|
46
44
|
|
47
45
|
def method_missing(m, *args)
|
48
|
-
CLIENT
|
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
|
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
|
data/lib/bitshares/version.rb
CHANGED
data/lib/bitshares/wallet.rb
CHANGED
@@ -7,7 +7,7 @@ module Bitshares
|
|
7
7
|
def initialize(name)
|
8
8
|
@name = name
|
9
9
|
@account = nil
|
10
|
-
@password = Bitshares.config[:
|
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
|
-
|
18
|
+
CLIENT.request('wallet_open', [@name])
|
19
19
|
end
|
20
20
|
|
21
21
|
def lock
|
22
22
|
open # must be opened first
|
23
|
-
|
23
|
+
CLIENT.request 'wallet_lock'
|
24
24
|
end
|
25
25
|
|
26
26
|
def unlock(timeout = 1776)
|
27
27
|
open # must be opened first
|
28
|
-
|
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
|
-
|
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
|
+
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-
|
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:
|
125
|
+
version: '0'
|
125
126
|
requirements: []
|
126
127
|
rubyforge_project:
|
127
128
|
rubygems_version: 2.4.8
|