trade-o-matic 0.3.0 → 0.3.1
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/game_backend/cancel_order.rb +46 -0
- data/lib/trade-o-matic/adapters/game_backend/configuration.rb +15 -0
- data/lib/trade-o-matic/adapters/game_backend/execute_order.rb +190 -0
- data/lib/trade-o-matic/adapters/game_backend/sfm.rb +23 -0
- data/lib/trade-o-matic/adapters/game_backend/state.rb +84 -0
- data/lib/trade-o-matic/adapters/game_backend.rb +168 -0
- data/lib/trade-o-matic/command.rb +25 -0
- data/lib/trade-o-matic/services/backend_factory.rb +5 -0
- data/lib/trade-o-matic/structs/currency_pair.rb +4 -0
- data/lib/trade-o-matic/version.rb +1 -1
- data/lib/trade-o-matic.rb +11 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e3ca1570f412bde6d4c5f68a6645e6d9fed6cb3
|
4
|
+
data.tar.gz: 1c070414b51612d93db781c98ffd4d6893de7f7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf4fea6ae9e13c29f3df4546a8ffdceadcff26575acec5f01dd30a181e91780652d2a39f4c244d6bea56773142477555a10d339bc01cd322c879c5dbaa408c8d
|
7
|
+
data.tar.gz: d80321f50158a5c28878754b0395ed6eb162cd1c0d867293c5cad64d238107177a497d9785b7d9a7b9ca43ccce99f2619b2ba3a1b13e508bb8c7f726b6ffbffc
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Trader
|
2
|
+
class GameBackend
|
3
|
+
class CancelOrder < Command.new(:state, :account, :order_id)
|
4
|
+
def perform
|
5
|
+
raise ArgumentError, 'order belong to another account' if order['account'] != account
|
6
|
+
raise ArgumentError, 'order already closed' if order['status'] != 'open'
|
7
|
+
|
8
|
+
market['open'].delete order
|
9
|
+
market['closed'] << order
|
10
|
+
order['status'] = 'canceled'
|
11
|
+
|
12
|
+
base_balance = state.balance_for account, market['base']
|
13
|
+
quote_balance = state.balance_for account, market['quote']
|
14
|
+
|
15
|
+
if bid?
|
16
|
+
quote = SFM.quote order['volume'], order['limit']
|
17
|
+
quote_balance['available'] += quote
|
18
|
+
quote_balance['frozen'] -= quote
|
19
|
+
else
|
20
|
+
base_balance['available'] += order['volume']
|
21
|
+
base_balance['frozen'] -= order['volume']
|
22
|
+
end
|
23
|
+
|
24
|
+
GameOrder.new order
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def bid?
|
30
|
+
order['instruction'] == 'bid'
|
31
|
+
end
|
32
|
+
|
33
|
+
def order
|
34
|
+
result[:order]
|
35
|
+
end
|
36
|
+
|
37
|
+
def market
|
38
|
+
result[:market]
|
39
|
+
end
|
40
|
+
|
41
|
+
def result
|
42
|
+
@result ||= state.find_order_by_id order_id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Trader
|
2
|
+
class GameBackend
|
3
|
+
module Configuration
|
4
|
+
extend self
|
5
|
+
|
6
|
+
attr_accessor :db_file
|
7
|
+
attr_reader :markets
|
8
|
+
|
9
|
+
def setup_market(_base, _quote, _options = {})
|
10
|
+
@markets ||= []
|
11
|
+
@markets << CurrencyPair.for_code(_base, _quote)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Trader
|
2
|
+
class GameBackend
|
3
|
+
class ExecuteOrder < Command.new(:state, :account, :pair, :volume, :limit, :instruction)
|
4
|
+
def perform
|
5
|
+
@market = state.market_for pair
|
6
|
+
@base_balance = state.balance_for account, @market['base']
|
7
|
+
@quote_balance = state.balance_for account, @market['quote']
|
8
|
+
|
9
|
+
if bid?
|
10
|
+
raise 'Not enough funds' if limit_order? and !enough_funds_for_bid?
|
11
|
+
load_bid_candidates
|
12
|
+
match_candidates
|
13
|
+
raise 'Not enough funds' if market_order? and !can_execute_market_bid?
|
14
|
+
execute_bid_txs
|
15
|
+
update_balance_on_bid unless order_fully_consumed?
|
16
|
+
store_order 'bid'
|
17
|
+
else
|
18
|
+
raise 'Not enough funds' unless enough_funds_for_ask?
|
19
|
+
load_ask_candidates
|
20
|
+
match_candidates
|
21
|
+
execute_ask_txs
|
22
|
+
update_balance_on_ask unless order_fully_consumed?
|
23
|
+
store_order 'ask'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def bid?
|
30
|
+
instruction == Order::BID
|
31
|
+
end
|
32
|
+
|
33
|
+
def market_order?
|
34
|
+
limit.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def limit_order?
|
38
|
+
!limit.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def enough_funds_for_bid?
|
42
|
+
@quote_balance['available'] >= safe_quote(volume, limit)
|
43
|
+
end
|
44
|
+
|
45
|
+
def enough_funds_for_ask?
|
46
|
+
@base_balance['available'] >= volume
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_bid_candidates
|
50
|
+
@candidates = @market['open'].select do |order|
|
51
|
+
next false if order['instruction'] != 'ask'
|
52
|
+
next true if limit.nil?
|
53
|
+
order['limit'] <= limit
|
54
|
+
end
|
55
|
+
|
56
|
+
@candidates.sort! do |a, b|
|
57
|
+
r = a['limit'] <=> b['limit']
|
58
|
+
r = a['ts'] <=> b['ts'] if r == 0
|
59
|
+
r
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_ask_candidates
|
64
|
+
@candidates = @market['open'].select do |order|
|
65
|
+
next false if order['instruction'] != 'bid'
|
66
|
+
next true if limit.nil?
|
67
|
+
order['limit'] >= limit
|
68
|
+
end
|
69
|
+
|
70
|
+
@candidates.sort! do |a, b|
|
71
|
+
r = b['limit'] <=> a['limit']
|
72
|
+
r = a['ts'] <=> b['ts'] if r == 0
|
73
|
+
r
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def match_candidates
|
78
|
+
@txs = []
|
79
|
+
@remaining = volume
|
80
|
+
@candidates.each do |other|
|
81
|
+
if @remaining >= other['volume']
|
82
|
+
@remaining -= other['volume']
|
83
|
+
@txs << { order: other, volume: other['volume'] }
|
84
|
+
else
|
85
|
+
@txs << { order: other, volume: @remaining }
|
86
|
+
@remaining = 0.0
|
87
|
+
break
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def can_execute_market_bid?
|
93
|
+
available = @quote_balance['available']
|
94
|
+
@txs.each do |tx|
|
95
|
+
quote = safe_quote(tx[:volume], tx[:order]['limit'])
|
96
|
+
return false if quote > available
|
97
|
+
available -= quote
|
98
|
+
end
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def execute_bid_txs
|
103
|
+
process_each_tx do |tx|
|
104
|
+
@base_balance['available'] -= tx[:volume]
|
105
|
+
@quote_balance['available'] += tx_quote(tx)
|
106
|
+
order_base_balance(tx[:order])['available'] += tx[:volume]
|
107
|
+
order_quote_balance(tx[:order])['frozen'] -= tx_quote(tx)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def execute_ask_txs
|
112
|
+
process_each_tx do |tx|
|
113
|
+
@base_balance['available'] -= tx[:volume]
|
114
|
+
@quote_balance['available'] += tx_quote(tx)
|
115
|
+
order_base_balance(tx[:order])['available'] += tx[:volume]
|
116
|
+
order_quote_balance(tx[:order])['frozen'] -= tx_quote(tx)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def store_order(_instruction)
|
121
|
+
order = {
|
122
|
+
'id' => state.unique_id,
|
123
|
+
'account' => account,
|
124
|
+
'pair' => pair.to_s, # just a little denorm to simplify
|
125
|
+
'instruction' => _instruction,
|
126
|
+
'limit' => limit,
|
127
|
+
'volume' => @remaining,
|
128
|
+
'original_volume' => volume
|
129
|
+
}
|
130
|
+
|
131
|
+
if order_fully_consumed?
|
132
|
+
order['status'] = 'closed'
|
133
|
+
@market['closed'] << order
|
134
|
+
else
|
135
|
+
order['status'] = 'open'
|
136
|
+
@market['open'] << order
|
137
|
+
end
|
138
|
+
|
139
|
+
GameOrder.new order
|
140
|
+
end
|
141
|
+
|
142
|
+
def update_balance_on_bid
|
143
|
+
remaining_quote = safe_quote(@remaining, limit)
|
144
|
+
@quote_balance['available'] -= remaining_quote
|
145
|
+
@quote_balance['frozen'] += remaining_quote
|
146
|
+
end
|
147
|
+
|
148
|
+
def update_balance_on_ask
|
149
|
+
@base_balance['available'] -= @remaining
|
150
|
+
@base_balance['frozen'] += @remaining
|
151
|
+
end
|
152
|
+
|
153
|
+
def order_fully_consumed?
|
154
|
+
market_order? or @remaining <= 0
|
155
|
+
end
|
156
|
+
|
157
|
+
def process_each_tx
|
158
|
+
@txs.each do |tx|
|
159
|
+
yield tx
|
160
|
+
tx[:order]['volume'] -= tx[:volume]
|
161
|
+
update_status tx[:order]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def order_base_balance(_order)
|
166
|
+
state.balance_for _order['account'], @market['base']
|
167
|
+
end
|
168
|
+
|
169
|
+
def order_quote_balance(_order)
|
170
|
+
state.balance_for _order['account'], @market['quote']
|
171
|
+
end
|
172
|
+
|
173
|
+
def tx_quote(_tx)
|
174
|
+
safe_quote(_tx[:volume], _tx[:order]['limit'])
|
175
|
+
end
|
176
|
+
|
177
|
+
def update_status(_order)
|
178
|
+
if _order['volume'] == 0
|
179
|
+
_order['status'] = 'closed'
|
180
|
+
@market['open'].delete _order
|
181
|
+
@market['closed'] << _order
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def safe_quote(_volume, _price)
|
186
|
+
SFM.quote(_volume, _price)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Trader
|
2
|
+
class GameBackend
|
3
|
+
module SFM # Safe Float Multiplier (wtf)
|
4
|
+
extend self
|
5
|
+
|
6
|
+
FACTOR = 1_000_000
|
7
|
+
|
8
|
+
def quote(_volume, _price)
|
9
|
+
(_volume * _price) / FACTOR
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode(_amount)
|
13
|
+
return nil if _amount.nil?
|
14
|
+
(_amount * FACTOR).to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def decode(_amount)
|
18
|
+
return nil if _amount.nil?
|
19
|
+
_amount.to_f / FACTOR
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Trader
|
4
|
+
class GameBackend
|
5
|
+
class State
|
6
|
+
def initialize
|
7
|
+
reset
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_from_file(_path)
|
11
|
+
if File.exists? _path
|
12
|
+
@state = YAML.load File.read(_path)
|
13
|
+
else
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def store_to_file(_path)
|
19
|
+
File.write _path, YAML.dump(@state)
|
20
|
+
end
|
21
|
+
|
22
|
+
def all_markets
|
23
|
+
state['markets'].values.map { |m| CurrencyPair.new(m['base'], m['quote']) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def unique_id
|
27
|
+
state['next_id'] += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def account_for(_account_name, create: true)
|
31
|
+
account = state['accounts'][_account_name]
|
32
|
+
account = state['accounts'][_account_name] = {} if account.nil? && create
|
33
|
+
account
|
34
|
+
end
|
35
|
+
|
36
|
+
def balance_for(_account_name, _currency, create: true)
|
37
|
+
account = account_for(_account_name)
|
38
|
+
balance = account[_currency.to_s]
|
39
|
+
balance = account[_currency.to_s] = new_balance if balance.nil? && create
|
40
|
+
balance
|
41
|
+
end
|
42
|
+
|
43
|
+
def market_for(_pair, create: true)
|
44
|
+
market = state['markets'][_pair.to_s]
|
45
|
+
market = state['markets'][_pair.to_s] = new_market_for(_pair) if market.nil? && create
|
46
|
+
market
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_order_by_id(_id)
|
50
|
+
state['markets'].values.each do |market|
|
51
|
+
order = market['open'].find { |o| o['id'] == _id } ||
|
52
|
+
market['closed'].find { |o| o['id'] == _id }
|
53
|
+
return { market: market, order: order } unless order.nil?
|
54
|
+
end
|
55
|
+
raise ArgumentError, 'order id not found'
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_reader :state
|
61
|
+
|
62
|
+
def new_market_for(_pair)
|
63
|
+
{
|
64
|
+
'base' => _pair.base.to_s,
|
65
|
+
'quote' => _pair.quote.to_s,
|
66
|
+
'open' => [],
|
67
|
+
'closed' => []
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_balance
|
72
|
+
{ 'available' => 0, 'frozen' => 0 }
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset
|
76
|
+
@state = {
|
77
|
+
'next_id' => 0,
|
78
|
+
'accounts' => {},
|
79
|
+
'markets' => {}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rainbow'
|
3
|
+
require 'rainbow/ext/string'
|
4
|
+
require 'trade-o-matic/adapters/base/raw_account_order'
|
5
|
+
require 'trade-o-matic/adapters/base/raw_balance'
|
6
|
+
|
7
|
+
require 'trade-o-matic/adapters/game_backend/configuration'
|
8
|
+
require 'trade-o-matic/adapters/game_backend/sfm'
|
9
|
+
require 'trade-o-matic/adapters/game_backend/state'
|
10
|
+
require 'trade-o-matic/adapters/game_backend/execute_order'
|
11
|
+
require 'trade-o-matic/adapters/game_backend/cancel_order'
|
12
|
+
|
13
|
+
module Trader
|
14
|
+
class GameBackend
|
15
|
+
STATUS_MAP = {
|
16
|
+
'open' => AccountOrder::OPEN,
|
17
|
+
'closed' => AccountOrder::CLOSED,
|
18
|
+
'canceled' => AccountOrder::CANCELED
|
19
|
+
}
|
20
|
+
|
21
|
+
class GameBalance < RawBalance
|
22
|
+
attr_mapped(:amount) { |r| SFM.decode(r['available'] + r['frozen']) }
|
23
|
+
attr_mapped(:available_amount) { |r| SFM.decode r['available'] }
|
24
|
+
end
|
25
|
+
|
26
|
+
class GameOrder < RawAccountOrder
|
27
|
+
attr_mapped(:id)
|
28
|
+
attr_mapped(:pair) { |r| CurrencyPair.parse r['pair'] }
|
29
|
+
attr_mapped(:price) { |r| SFM.decode r['limit'] }
|
30
|
+
attr_mapped(:volume) { |r| SFM.decode r['original_volume'] }
|
31
|
+
attr_mapped(:executed_volume) { |r| SFM.decode(r['original_volume'] - r['volume']) }
|
32
|
+
attr_mapped(:instruction)
|
33
|
+
attr_mapped(:status) { |r| STATUS_MAP[r['status']] }
|
34
|
+
|
35
|
+
def limit?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :verbose, :state
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
@state = State.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def increment_balance(_account_name, _currency, _amount = nil)
|
47
|
+
if _amount.nil?
|
48
|
+
_amount = _currency.amount
|
49
|
+
_currency = _currency.currency
|
50
|
+
end
|
51
|
+
|
52
|
+
with_session do
|
53
|
+
balance = state.balance_for(_account_name, Currency.for_code(_currency))
|
54
|
+
balance['available'] += SFM.encode(_amount)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def decrement_balance(_account_name, _currency, _amount = nil)
|
59
|
+
if _amount.nil?
|
60
|
+
_amount = _currency.amount
|
61
|
+
_currency = _currency.currency
|
62
|
+
end
|
63
|
+
|
64
|
+
with_session do
|
65
|
+
balance = state.balance_for(_account_name, Currency.for_code(_currency))
|
66
|
+
balance['available'] -= SFM.encode(_amount)
|
67
|
+
balance['available'] = 0 if balance['available'] < 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# backend implementation:
|
72
|
+
|
73
|
+
def get_available_markets
|
74
|
+
with_session(true) do
|
75
|
+
state.all_markets
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def fill_book(_book)
|
80
|
+
# TODO
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_session(_config)
|
84
|
+
raise ArgumentError, 'must provide login information' if _config.nil? or _config[:name].nil?
|
85
|
+
_config[:name]
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_balance(_session, _currency)
|
89
|
+
with_session(true) do
|
90
|
+
balance = state.balance_for(_session, _currency)
|
91
|
+
GameBalance.new balance
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_orders(_session, _pair)
|
96
|
+
with_session(true) do
|
97
|
+
market = state.market_for(_pair)
|
98
|
+
open = market['open'].select { |o| o['account'] = _session }
|
99
|
+
closed = market['closed'].select { |o| o['account'] = _session }
|
100
|
+
(open + closed).map { |o| GameOrder.new o }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_order(_session, _order_id)
|
105
|
+
with_session(true) do
|
106
|
+
GameOrder.new state.find_order_by_id(_order_id.to_i)[:order]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_order(_session, _pair, _volume, _price, _instruction)
|
111
|
+
with_session do
|
112
|
+
execute_order = ExecuteOrder.new
|
113
|
+
execute_order.state = state
|
114
|
+
execute_order.account = _session
|
115
|
+
execute_order.pair = _pair
|
116
|
+
execute_order.volume = SFM.encode _volume
|
117
|
+
execute_order.limit = SFM.encode _price
|
118
|
+
execute_order.instruction = _instruction
|
119
|
+
execute_order.perform
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def cancel_order(_session, _order_id)
|
124
|
+
with_session do
|
125
|
+
cancel_order = CancelOrder.new
|
126
|
+
cancel_order.state = state
|
127
|
+
cancel_order.account = _session
|
128
|
+
cancel_order.order_id = _order_id.to_i
|
129
|
+
cancel_order.perform
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def with_session(_idemponent = false)
|
136
|
+
restore_state_from_file if use_state_file?
|
137
|
+
ensure_required_markets
|
138
|
+
result = yield
|
139
|
+
save_state_to_file if !_idemponent && use_state_file?
|
140
|
+
return result
|
141
|
+
end
|
142
|
+
|
143
|
+
def use_state_file?
|
144
|
+
!config.db_file.nil?
|
145
|
+
end
|
146
|
+
|
147
|
+
def restore_state_from_file
|
148
|
+
state.load_from_file config.db_file
|
149
|
+
end
|
150
|
+
|
151
|
+
def save_state_to_file
|
152
|
+
state.store_to_file config.db_file
|
153
|
+
end
|
154
|
+
|
155
|
+
def ensure_required_markets
|
156
|
+
return false unless config.markets
|
157
|
+
config.markets.each { |mc| state.market_for(mc) }
|
158
|
+
end
|
159
|
+
|
160
|
+
def config
|
161
|
+
Configuration
|
162
|
+
end
|
163
|
+
|
164
|
+
def info(_msg, _color = :white)
|
165
|
+
puts "GameEx: #{_msg}".color(_color) if verbose
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Command
|
2
|
+
def self.new(*_attributes)
|
3
|
+
attr_names = []
|
4
|
+
attr_defaults = {}
|
5
|
+
|
6
|
+
_attributes.each do |att|
|
7
|
+
if att.is_a? Hash
|
8
|
+
attr_defaults.merge att
|
9
|
+
attr_names += att.keys
|
10
|
+
else
|
11
|
+
attr_names << att
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Struct.new(*attr_names) do
|
16
|
+
define_method(:initialize) do |kwargs={}|
|
17
|
+
kwargs = attr_defaults.merge kwargs
|
18
|
+
attr_values = attr_names.map { |a| kwargs[a] }
|
19
|
+
super(*attr_values)
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method(:perform) {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/trade-o-matic.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "trade-o-matic/version"
|
2
2
|
require 'trade-o-matic/standard'
|
3
|
+
require 'trade-o-matic/command'
|
3
4
|
|
4
5
|
require 'trade-o-matic/structs/currency'
|
5
6
|
require 'trade-o-matic/structs/currency_pair'
|
@@ -28,7 +29,6 @@ require 'trade-o-matic/converters/inverse_converter'
|
|
28
29
|
require 'trade-o-matic/converters/compound_converter'
|
29
30
|
|
30
31
|
module Trader
|
31
|
-
|
32
32
|
# Default conversions
|
33
33
|
Currency.register_conversion :BTC, :SATOSHI, 100_000_000
|
34
34
|
Currency.register_conversion :SATOSHI, :BTC, 0.00000001
|
@@ -45,4 +45,14 @@ module Trader
|
|
45
45
|
exchange(_backend).login(_credentials)
|
46
46
|
end
|
47
47
|
|
48
|
+
def self.setup_game_backend(&_block)
|
49
|
+
require 'trade-o-matic/adapters/game_backend'
|
50
|
+
GameBackend::Configuration.tap &_block
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.game
|
55
|
+
require 'trade-o-matic/adapters/game_backend'
|
56
|
+
GameBackend.new
|
57
|
+
end
|
48
58
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.1
|
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-
|
11
|
+
date: 2016-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -241,9 +241,16 @@ files:
|
|
241
241
|
- lib/trade-o-matic/adapters/base/raw_resource.rb
|
242
242
|
- lib/trade-o-matic/adapters/bitstamp_backend.rb
|
243
243
|
- lib/trade-o-matic/adapters/fake_backend.rb
|
244
|
+
- lib/trade-o-matic/adapters/game_backend.rb
|
245
|
+
- lib/trade-o-matic/adapters/game_backend/cancel_order.rb
|
246
|
+
- lib/trade-o-matic/adapters/game_backend/configuration.rb
|
247
|
+
- lib/trade-o-matic/adapters/game_backend/execute_order.rb
|
248
|
+
- lib/trade-o-matic/adapters/game_backend/sfm.rb
|
249
|
+
- lib/trade-o-matic/adapters/game_backend/state.rb
|
244
250
|
- lib/trade-o-matic/adapters/itbit_backend.rb
|
245
251
|
- lib/trade-o-matic/adapters/surbtc_backend.rb
|
246
252
|
- lib/trade-o-matic/cli.rb
|
253
|
+
- lib/trade-o-matic/command.rb
|
247
254
|
- lib/trade-o-matic/converters/compound_converter.rb
|
248
255
|
- lib/trade-o-matic/converters/fixed_converter.rb
|
249
256
|
- lib/trade-o-matic/converters/inverse_converter.rb
|