trade-o-matic 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trade-o-matic/adapters/bitfinex_backend.rb +162 -0
- data/lib/trade-o-matic/converters/sync_converter.rb +2 -2
- data/lib/trade-o-matic/converters/web_converter.rb +20 -0
- data/lib/trade-o-matic/services/backend_factory.rb +5 -0
- data/lib/trade-o-matic/support/converter_configurator.rb +5 -0
- data/lib/trade-o-matic/version.rb +1 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffa9eaa9a1e1e4a7ae37faf694d4882fb95a356c
|
4
|
+
data.tar.gz: c184b0499a3414f1155ef69ddd22717456093075
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f8ba8f453091a648e7f953f9f7375e55c1c223ea15508867122371786b414aa0c03c1c2b467aba83f5136a37dfdffa17c201b172b2d25012d12be8a67939ede
|
7
|
+
data.tar.gz: c4caec6d3f480b81e0f81bc2e692cb63d4c8e0af80bc2883247ad826bbc8f5e35cb9ffe4fbcc294a9e1a1097e0ba8a40bf7ad2084dbb746d8815bd90bbf60371
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
require 'trade-o-matic/adapters/base/raw_account_order'
|
5
|
+
require 'trade-o-matic/adapters/base/raw_balance'
|
6
|
+
|
7
|
+
module Trader
|
8
|
+
class BitfinexBackend
|
9
|
+
BASE_CUR = Currency.for_code(:BTC)
|
10
|
+
QUOTE_CUR = Currency.for_code(:BITFINEX_USD)
|
11
|
+
MAIN_MARKET = CurrencyPair.new BASE_CUR, QUOTE_CUR
|
12
|
+
|
13
|
+
CUR_EQUIV = {
|
14
|
+
'usd' => QUOTE_CUR,
|
15
|
+
'btc' => BASE_CUR
|
16
|
+
}
|
17
|
+
|
18
|
+
class BalanceAdaptor < RawBalance
|
19
|
+
attr_mapped(:amount)
|
20
|
+
attr_mapped(:available_amount, 'available')
|
21
|
+
end
|
22
|
+
|
23
|
+
class OrderAdaptor < RawAccountOrder
|
24
|
+
attr_mapped(:id)
|
25
|
+
attr_mapped(:pair) { |r| MAIN_MARKET }
|
26
|
+
attr_mapped(:price)
|
27
|
+
attr_mapped(:volume, 'original_amount')
|
28
|
+
attr_mapped(:executed_volume, 'executed_amount')
|
29
|
+
attr_mapped(:instruction) { |r| r['side'] == 'buy' ? Order::BID : Order::ASK }
|
30
|
+
attr_mapped(:status) do |r|
|
31
|
+
if r['is_live']
|
32
|
+
AccountOrder::OPEN
|
33
|
+
elsif r['is_cancelled']
|
34
|
+
AccountOrder::CANCELED
|
35
|
+
else
|
36
|
+
AccountOrder::CLOSED
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def limit?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(_session=nil)
|
46
|
+
@session = _session
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_available_markets
|
50
|
+
[MAIN_MARKET]
|
51
|
+
end
|
52
|
+
|
53
|
+
def fill_book(_book)
|
54
|
+
# TODO: consider book pair
|
55
|
+
|
56
|
+
_book.prepare Time.now
|
57
|
+
|
58
|
+
ob = public_get('book/BTCUSD')
|
59
|
+
ob['bids'].each { |o| _book.add_bid(o['price'], o['amount']) }
|
60
|
+
ob['asks'].each { |o| _book.add_ask(o['price'], o['amount']) }
|
61
|
+
|
62
|
+
tx = public_get('trades/BTCUSD', { timestamp: one_hour_ago })
|
63
|
+
tx.each do |t|
|
64
|
+
_book.add_transaction t['price'], t['amount'], Time.at(t['timestamp'])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_session(_credentials)
|
69
|
+
_credentials
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_balance(_session, _currency)
|
73
|
+
raise "#{_currency} not supported" unless _currency == BASE_CUR || _currency == QUOTE_CUR
|
74
|
+
|
75
|
+
raw = auth_post(_session, 'balances')
|
76
|
+
wrap_balance raw, _currency
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_orders(_session, _pair)
|
80
|
+
raise 'market not supported' unless _pair == MAIN_MARKET
|
81
|
+
|
82
|
+
raw = auth_post(_session, 'orders')
|
83
|
+
raw = raw.select { |o| o['type'].include? 'limit' }
|
84
|
+
raw.map { |o| wrap_order o }
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_order(_session, _pair, _volume, _price, _type)
|
88
|
+
raise 'market not supported' unless _pair == MAIN_MARKET
|
89
|
+
raise 'market orders not supported' if _price.nil?
|
90
|
+
|
91
|
+
wrap_order auth_post(_session, 'order/new', {
|
92
|
+
exchange: "bitfinex",
|
93
|
+
symbol: "BTCUSD",
|
94
|
+
amount: _volume.to_s('F'),
|
95
|
+
price: _price.to_s('F'),
|
96
|
+
side: _type == Order::BID ? 'buy' : 'sell',
|
97
|
+
type: "exchange limit"
|
98
|
+
})
|
99
|
+
end
|
100
|
+
|
101
|
+
def fetch_order(_session, _id)
|
102
|
+
wrap_order auth_post(_session, 'order/status', { order_id: _id })
|
103
|
+
end
|
104
|
+
|
105
|
+
def cancel_order(_session, _id)
|
106
|
+
order = wrap_order auth_post(_session, 'order/cancel', { order_id: _id })
|
107
|
+
order = fetch_order(_session, _id) while order.status == AccountOrder::OPEN
|
108
|
+
order
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
attr_reader :session
|
114
|
+
|
115
|
+
def public_get(_url, _query = {})
|
116
|
+
resp = RestClient.get "https://api.bitfinex.com/v1/#{_url}/", params: _query
|
117
|
+
JSON.parse resp
|
118
|
+
end
|
119
|
+
|
120
|
+
def auth_post(_creds, _url, _data = {})
|
121
|
+
raise ArgumentError if !_creds.key?(:api_key) or !_creds.key?(:secret)
|
122
|
+
|
123
|
+
payload = build_payload("/v1/#{_url}", _data)
|
124
|
+
headers = {
|
125
|
+
'Content-Type' => 'application/json',
|
126
|
+
'Accept' => 'application/json',
|
127
|
+
'X-BFX-PAYLOAD' => payload,
|
128
|
+
'X-BFX-SIGNATURE' => sign_payload(_creds, payload),
|
129
|
+
'X-BFX-APIKEY' => _creds[:api_key],
|
130
|
+
}
|
131
|
+
|
132
|
+
resp = RestClient.post "https://api.bitfinex.com/v1/#{_url}/", nil, headers
|
133
|
+
JSON.parse resp
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_payload(_url, _params = {})
|
137
|
+
payload = {}
|
138
|
+
payload['nonce'] = (Time.now.to_f * 10_000).to_i.to_s
|
139
|
+
payload['request'] = _url
|
140
|
+
payload.merge!(_params) if _params
|
141
|
+
Base64.strict_encode64(payload.to_json)
|
142
|
+
end
|
143
|
+
|
144
|
+
def sign_payload(_creds, _payload)
|
145
|
+
OpenSSL::HMAC.hexdigest('sha384', _creds[:secret], _payload)
|
146
|
+
end
|
147
|
+
|
148
|
+
def one_hour_ago
|
149
|
+
Time.now.to_i - 3600
|
150
|
+
end
|
151
|
+
|
152
|
+
def wrap_balance(_raw, _currency)
|
153
|
+
_raw = _raw.find { |b| b["type"] == "exchange" && CUR_EQUIV[b["currency"]] == _currency }
|
154
|
+
_raw = { "amount"=> "0.0", "available" => "0.0" } if _raw.nil?
|
155
|
+
BalanceAdaptor.new _raw
|
156
|
+
end
|
157
|
+
|
158
|
+
def wrap_order(_raw)
|
159
|
+
OrderAdaptor.new _raw
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pincers'
|
2
|
+
|
3
|
+
module Trader
|
4
|
+
class WebConverter < SyncConverter
|
5
|
+
attr_reader :url, :path
|
6
|
+
|
7
|
+
def initialize(_url, _path, _ttl=30)
|
8
|
+
@url = _url
|
9
|
+
@path = _path
|
10
|
+
|
11
|
+
super(_ttl) do
|
12
|
+
pincers = Pincers.for_chenso
|
13
|
+
pincers.goto _url
|
14
|
+
el = pincers.search(_path).first
|
15
|
+
raise "No element found at #{_path}" if el.nil?
|
16
|
+
Standard.amount el.text.gsub(/[^\d\s\.\,]+/,'').strip
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -23,6 +23,11 @@ module Trader
|
|
23
23
|
JsonApiConverter.new(_params['url'], _params['path'].split('.'), _params['ttl'] || 30)
|
24
24
|
end
|
25
25
|
|
26
|
+
def configure_yaml_web(_params)
|
27
|
+
require 'trade-o-matic/converters/web_converter'
|
28
|
+
WebConverter.new(_params['url'], _params['path'], _params['ttl'] || 30)
|
29
|
+
end
|
30
|
+
|
26
31
|
def invert(_converter)
|
27
32
|
InverseConverter.new _converter
|
28
33
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trade-o-matic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ignacio Baixas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: gli
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pincers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.7'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.7'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -239,6 +253,7 @@ files:
|
|
239
253
|
- lib/trade-o-matic/adapters/base/raw_account_order.rb
|
240
254
|
- lib/trade-o-matic/adapters/base/raw_balance.rb
|
241
255
|
- lib/trade-o-matic/adapters/base/raw_resource.rb
|
256
|
+
- lib/trade-o-matic/adapters/bitfinex_backend.rb
|
242
257
|
- lib/trade-o-matic/adapters/bitstamp_backend.rb
|
243
258
|
- lib/trade-o-matic/adapters/fake_backend.rb
|
244
259
|
- lib/trade-o-matic/adapters/game_backend.rb
|
@@ -256,6 +271,7 @@ files:
|
|
256
271
|
- lib/trade-o-matic/converters/inverse_converter.rb
|
257
272
|
- lib/trade-o-matic/converters/json_api_converter.rb
|
258
273
|
- lib/trade-o-matic/converters/sync_converter.rb
|
274
|
+
- lib/trade-o-matic/converters/web_converter.rb
|
259
275
|
- lib/trade-o-matic/core/account.rb
|
260
276
|
- lib/trade-o-matic/core/account_order.rb
|
261
277
|
- lib/trade-o-matic/core/account_proxy.rb
|