bitshares 0.1.4.pre → 0.1.5
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 +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
|