trade-o-matic 0.1.0
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 +7 -0
- data/bin/trader +4 -0
- data/lib/trade-o-matic/adapters/bitstamp_account.rb +63 -0
- data/lib/trade-o-matic/cli.rb +49 -0
- data/lib/trade-o-matic/converters/fixed_converter.rb +16 -0
- data/lib/trade-o-matic/converters/sync_converter.rb +33 -0
- data/lib/trade-o-matic/flows/ask_replicator_flow.rb +50 -0
- data/lib/trade-o-matic/generators/linear_generator.rb +37 -0
- data/lib/trade-o-matic/structs/ask_slope.rb +10 -0
- data/lib/trade-o-matic/structs/bid_slope.rb +10 -0
- data/lib/trade-o-matic/structs/converter.rb +8 -0
- data/lib/trade-o-matic/structs/currency.rb +63 -0
- data/lib/trade-o-matic/structs/currency_pair.rb +14 -0
- data/lib/trade-o-matic/structs/market.rb +28 -0
- data/lib/trade-o-matic/structs/order.rb +3 -0
- data/lib/trade-o-matic/structs/price.rb +41 -0
- data/lib/trade-o-matic/structs/slope.rb +147 -0
- data/lib/trade-o-matic/structs/transaction.rb +3 -0
- data/lib/trade-o-matic/version.rb +3 -0
- data/lib/trade-o-matic.rb +19 -0
- metadata +281 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 9300715275fb3f78ca15a6d534d493f61c75ea7e
|
|
4
|
+
data.tar.gz: b3f66fd9b2f31736c86260f45c817ea8dc85dc01
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5208ae28f7c8d4da98d1a65bfdef0f1f9b2b1a09e4744ee79b87c32f104a6c9e71ceca7ea4045af8cba160c7b4ffca7f2b338b694cb514310957791e3fd24897
|
|
7
|
+
data.tar.gz: 4efe51fef441d0a483e38a766743268f87bf4fb9a968496763c85befd879aa6e05902b5732e24e6e525199e83c4e2435019ca16130341f5ed25f9e18cd7e0b5a
|
data/bin/trader
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'rest-client'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Trader
|
|
5
|
+
|
|
6
|
+
# TODO: separate market information from account
|
|
7
|
+
|
|
8
|
+
class BitstampAccount
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def market_snapshot
|
|
14
|
+
market = Market.new :BTC, :Bitstamp_USD
|
|
15
|
+
unpack_order_book market, fetch_raw_order_book
|
|
16
|
+
unpack_transactions market, fetch_raw_transactions
|
|
17
|
+
market
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def refresh
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def base_balance # XBT in an XBT/USD pair
|
|
24
|
+
Price.new :BTC, 0.0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def quote_balance # USD in an XBT/USD pair
|
|
28
|
+
Price.new :Bitstamp_USD, 20000.0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_order
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def cancel_order
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def orders
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def fetch_raw_order_book
|
|
43
|
+
JSON.parse(RestClient.get "https://www.bitstamp.net/api/order_book/")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def fetch_raw_transactions
|
|
47
|
+
JSON.parse(RestClient.get "https://www.bitstamp.net/api/transactions/")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def unpack_order_book(_market, _ob)
|
|
51
|
+
_ob['bids'].each { |o| _market.add_bid(o[0].to_f, o[1].to_f) }
|
|
52
|
+
_ob['asks'].each { |o| _market.add_ask(o[0].to_f, o[1].to_f) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def unpack_transactions(_market, _tx)
|
|
56
|
+
_tx.each do |t|
|
|
57
|
+
time = Time.at(t['date'].to_i)
|
|
58
|
+
market.add_buy(t['price'].to_f, t['amount'].to_f, time) if ['type'] == 0
|
|
59
|
+
market.add_sell(t['price'].to_f, t['amount'].to_f, time) if ['type'] == 1
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'gli'
|
|
2
|
+
|
|
3
|
+
module Trader
|
|
4
|
+
class CLI
|
|
5
|
+
extend GLI::App
|
|
6
|
+
|
|
7
|
+
program_desc 'Trader command line interface'
|
|
8
|
+
|
|
9
|
+
desc "Executes trade-o-matic using provided settings"
|
|
10
|
+
command [:run, :r] do |c|
|
|
11
|
+
|
|
12
|
+
c.desc "Specifies the configuration file"
|
|
13
|
+
c.flag [:c, :config]
|
|
14
|
+
|
|
15
|
+
c.desc "Start the server in verbose mode"
|
|
16
|
+
c.switch :dry, :default_value => false
|
|
17
|
+
|
|
18
|
+
c.action do |global_options,options,args|
|
|
19
|
+
|
|
20
|
+
require 'trade-o-matic/adapters/bitstamp_account'
|
|
21
|
+
require 'trade-o-matic/generators/linear_generator'
|
|
22
|
+
require 'trade-o-matic/flows/ask_replicator_flow'
|
|
23
|
+
|
|
24
|
+
Currency.register_conversion(:CLP, :Bitstamp_USD, 1.0/710.0)
|
|
25
|
+
|
|
26
|
+
source = Trader::BitstampAccount.new
|
|
27
|
+
target = nil
|
|
28
|
+
generator = LinearGenerator.new(spread: Price.new(:CLP, 10000.0), step: Price.new(:CLP, 1000.0))
|
|
29
|
+
|
|
30
|
+
flow = Trader::AskReplicatorFlow.new source, target, generator
|
|
31
|
+
|
|
32
|
+
puts 'Running ask replicator'
|
|
33
|
+
orders = flow.run options[:dry]
|
|
34
|
+
|
|
35
|
+
puts 'Generated the following orders :'
|
|
36
|
+
orders.each { |o| puts "#{o.price} [#{o.volume}]" }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
on_error do |exc|
|
|
41
|
+
puts exc.to_s
|
|
42
|
+
puts exc.backtrace
|
|
43
|
+
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
exit run(ARGV)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
|
|
3
|
+
class SyncConverter < Converter
|
|
4
|
+
|
|
5
|
+
attr_reader :ttl
|
|
6
|
+
|
|
7
|
+
def initialize(_ttl, &_block)
|
|
8
|
+
@ttl = _ttl
|
|
9
|
+
@block = _block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def expired?
|
|
13
|
+
@last_sync < Time.new - @ttl
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def convert(_value, _from, _to)
|
|
17
|
+
rate = sync_rate
|
|
18
|
+
_value * rate
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def sync_rate
|
|
24
|
+
if @last_sync.nil? or expired?
|
|
25
|
+
@rate = @block.call
|
|
26
|
+
@last_sync = Time.now
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
@rate
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
|
|
3
|
+
class AskReplicatorFlow
|
|
4
|
+
|
|
5
|
+
def initialize(_source, _target, _generator)
|
|
6
|
+
@source = _source
|
|
7
|
+
@target = _target
|
|
8
|
+
@generator = _generator
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run(_dry)
|
|
12
|
+
|
|
13
|
+
# TODO: assert that both source and target base currencies are the same.
|
|
14
|
+
|
|
15
|
+
market = source.market_snapshot
|
|
16
|
+
|
|
17
|
+
skip_volume = market.ask_slope.volume 60
|
|
18
|
+
|
|
19
|
+
start_price = market.ask_slope.price skip_volume
|
|
20
|
+
|
|
21
|
+
available_volume = market.ask_slope.assess source.quote_balance, skip_volume # TODO: check
|
|
22
|
+
|
|
23
|
+
# available_volume = target.base_balance if target.base_balance < available_volume
|
|
24
|
+
|
|
25
|
+
orders = generator.ask start_price, available_volume
|
|
26
|
+
|
|
27
|
+
orders = adjust_orders_to_slope orders, market.ask_slope
|
|
28
|
+
|
|
29
|
+
# orders.each { |o| o.price.convert(target.quote_currency) }
|
|
30
|
+
|
|
31
|
+
# executed = target_position.sync orders
|
|
32
|
+
# executed.each do |order|
|
|
33
|
+
|
|
34
|
+
# end
|
|
35
|
+
|
|
36
|
+
# TODO: transform orders price to target currency
|
|
37
|
+
|
|
38
|
+
# TODO: sync orders
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
attr_reader :source, :target, :generator
|
|
45
|
+
|
|
46
|
+
def adjust_orders_to_slope(_orders, _slope)
|
|
47
|
+
_orders
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class LinearGenerator
|
|
3
|
+
|
|
4
|
+
def initialize(_options)
|
|
5
|
+
@price_spread = _options[:spread]
|
|
6
|
+
@price_step = _options[:step]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def bid(_start_price, _volume)
|
|
10
|
+
generate _start_price, _volume, -1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ask(_start_price, _volume)
|
|
14
|
+
generate _start_price, _volume, 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def generate(_start_price, _volume, _mult)
|
|
20
|
+
buckets = (@price_spread.amount / @price_step.amount).floor
|
|
21
|
+
|
|
22
|
+
# make sure price step is using the same currency as generated prices
|
|
23
|
+
conv_price_step = @price_step.convert(_start_price.currency).amount
|
|
24
|
+
|
|
25
|
+
buckets.floor.times.map do |i|
|
|
26
|
+
price = _start_price.amount + _mult * conv_price_step * i
|
|
27
|
+
volume = _volume.amount / buckets
|
|
28
|
+
|
|
29
|
+
Order.new(
|
|
30
|
+
Price.new(_start_price.currency, price),
|
|
31
|
+
Price.new(_volume.currency, volume)
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class Currency
|
|
3
|
+
|
|
4
|
+
def self.register_conversion(_to, _from, _converter=nil, &_block)
|
|
5
|
+
_to = _to.code if _to.is_a? Currency
|
|
6
|
+
_from = _from.code if _from.is_a? Currency
|
|
7
|
+
|
|
8
|
+
if !_block.nil?
|
|
9
|
+
_converter = SyncConverter.new(300, &_block)
|
|
10
|
+
elsif _converter.is_a? Integer or _converter.is_a? Float
|
|
11
|
+
_converter = FixedConverter.new(_converter)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
converters["#{_to}-#{_from}"] = _converter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.for_code(_code)
|
|
18
|
+
registry[_code]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :code
|
|
22
|
+
|
|
23
|
+
def initialize(_code)
|
|
24
|
+
@code = _code
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def convert(_value, _currency)
|
|
28
|
+
_currency = _currency.code if _currency.is_a? Currency
|
|
29
|
+
converter_for(code, _currency).convert(_value, code, _currency)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def unpack(_value, _options={})
|
|
33
|
+
_options = { default: _options } unless _options.is_a? Hash
|
|
34
|
+
|
|
35
|
+
if _value.is_a? Price
|
|
36
|
+
raise StandardError, "Expected value currency to be #{code} (was #{_value.currency.code})" if _value.currency != self
|
|
37
|
+
_value.amount
|
|
38
|
+
elsif _value.nil?
|
|
39
|
+
_options[:default]
|
|
40
|
+
elsif _options.fetch(:strict, true)
|
|
41
|
+
raise StandardError, "Must provide a currency bound price"
|
|
42
|
+
else
|
|
43
|
+
_value
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def self.registry
|
|
50
|
+
@@registry ||= Hash.new { |h,k| h[k] = self.new(k.to_sym) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.converters
|
|
54
|
+
@@converters ||= Hash.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def converter_for(_from, _to)
|
|
58
|
+
cv = self.class.converters["#{_from}-#{_to}"]
|
|
59
|
+
raise StandardError, "No converter registered for #{_from}/#{_to}" if cv.nil?
|
|
60
|
+
cv
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class CurrencyPair
|
|
3
|
+
|
|
4
|
+
attr_reader :base, :quote
|
|
5
|
+
|
|
6
|
+
def initialize(_base, _quote)
|
|
7
|
+
_base = Currency.for_code _base unless _base.is_a? Currency
|
|
8
|
+
_quote = Currency.for_code _quote unless _quote.is_a? Currency
|
|
9
|
+
|
|
10
|
+
@base = _base
|
|
11
|
+
@quote = _quote
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class Market
|
|
3
|
+
|
|
4
|
+
attr_reader :pair, :ask_slope, :bid_slope
|
|
5
|
+
|
|
6
|
+
def initialize(_base, _quote)
|
|
7
|
+
@pair = CurrencyPair.new _base, _quote
|
|
8
|
+
@ask_slope = AskSlope.new @pair
|
|
9
|
+
@bid_slope = BidSlope.new @pair
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_bid(_price, _volume)
|
|
13
|
+
@bid_slope.add_order(_price, _volume)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_ask(_price, _volume)
|
|
17
|
+
@ask_slope.add_order(_price, _volume)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_sell(_price, _volume, _time)
|
|
21
|
+
@bid_slope.add_transaction(_price, _volume, _time)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_buy(_price, _volume, _time)
|
|
25
|
+
@bid_slope.add_transaction(_price, _volume, _time)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class Price
|
|
3
|
+
|
|
4
|
+
attr_reader :currency, :amount
|
|
5
|
+
|
|
6
|
+
def initialize(_currency, _amount)
|
|
7
|
+
_currency = Currency.for_code(_currency) unless _currency.is_a? Currency
|
|
8
|
+
|
|
9
|
+
@currency = _currency
|
|
10
|
+
@amount = _amount
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def convert(_other_currency)
|
|
14
|
+
_other_currency = Currency.for _other_currency unless _other_currency.is_a? Currency
|
|
15
|
+
|
|
16
|
+
return self if _other_currency == currency
|
|
17
|
+
|
|
18
|
+
new_amount = @currency.convert amount, _other_currency
|
|
19
|
+
Price.new _other_currency, new_amount
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_s
|
|
23
|
+
"#{amount} #{currency.code}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
[:<, :<=, :>=, :>, :==, '<=>'].each do |op|
|
|
27
|
+
define_method(op) do |_other|
|
|
28
|
+
_other = currency.unpack(_other)
|
|
29
|
+
amount.public_send(op, _other)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
[:+, :-, :*, :/].each do |op|
|
|
34
|
+
define_method(op) do |_other|
|
|
35
|
+
_other = currency.unpack(_other)
|
|
36
|
+
Price.new currency, amount.public_send(op, _other)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module Trader
|
|
2
|
+
class Slope
|
|
3
|
+
|
|
4
|
+
attr_reader :pair
|
|
5
|
+
|
|
6
|
+
def initialize(_pair, _ref_time=nil)
|
|
7
|
+
@pair = _pair
|
|
8
|
+
@orders = []
|
|
9
|
+
@transactions = []
|
|
10
|
+
@orders_sorted = false
|
|
11
|
+
@ref_time = _ref_time || Time.now
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add_order(_price, _volume)
|
|
15
|
+
@orders_sorted = false
|
|
16
|
+
@orders << Order.new(
|
|
17
|
+
Price.new(pair.quote, _price),
|
|
18
|
+
Price.new(pair.base, _volume),
|
|
19
|
+
)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_transaction(_price, _volume, _time)
|
|
24
|
+
@transactions << Transaction.new(
|
|
25
|
+
Price.new(pair.quote, _price),
|
|
26
|
+
Price.new(pair.base, _volume),
|
|
27
|
+
_time
|
|
28
|
+
)
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def volume(_period_seconds)
|
|
33
|
+
ref_time = @ref_time - _period_seconds
|
|
34
|
+
amount = @transactions.inject(0.0) do |r, trx|
|
|
35
|
+
if trx.time >= ref_time
|
|
36
|
+
r + trx.volume.amount
|
|
37
|
+
else
|
|
38
|
+
r
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Price.new pair.base, amount
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def price(_skip_volume=nil)
|
|
46
|
+
_skip_volume = pair.base.unpack _skip_volume, 0.0
|
|
47
|
+
|
|
48
|
+
slice(_skip_volume).first[0].price
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def volume_for_price(_price, _skip_volume=nil)
|
|
52
|
+
_price = pair.quote.unpack _price
|
|
53
|
+
_skip_volume = pair.base.unpack _skip_volume, 0.0
|
|
54
|
+
|
|
55
|
+
wt_price = 0.0
|
|
56
|
+
r_volume = 0.0
|
|
57
|
+
|
|
58
|
+
slice(_skip_volume).each do |order, volume|
|
|
59
|
+
if compare(order.price.amount, _price) == -1
|
|
60
|
+
x = (_price - wt_price) * r_volume / (order.price.amount - _price)
|
|
61
|
+
if x < volume
|
|
62
|
+
volume = x
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
wt_price = (wt_price * r_volume + order.price.amount * volume) / (r_volume + volume)
|
|
67
|
+
r_volume += volume
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Price.new pair.base, r_volume
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def quote(_volume, _skip_volume=nil)
|
|
74
|
+
_volume = pair.base.unpack _volume, 0.0
|
|
75
|
+
_skip_volume = pair.base.unpack _skip_volume, 0.0
|
|
76
|
+
|
|
77
|
+
total_price = 0.0
|
|
78
|
+
slice(_skip_volume, _volume).each do |order, order_volume|
|
|
79
|
+
total_price += order_volume * order.price.amount
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Price.new pair.quote, total_price
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def assess(_amount, _skip_volume=nil)
|
|
86
|
+
_amount = pair.quote.unpack _amount
|
|
87
|
+
_skip_volume = pair.base.unpack _skip_volume, 0.0
|
|
88
|
+
|
|
89
|
+
total_volume = 0.0
|
|
90
|
+
slice(_skip_volume).each do |order, volume|
|
|
91
|
+
order_quote = order.price.amount * volume
|
|
92
|
+
if order_quote > _amount
|
|
93
|
+
total_volume += _amount / order.price.amount
|
|
94
|
+
break
|
|
95
|
+
else
|
|
96
|
+
total_volume += volume
|
|
97
|
+
_amount -= order_quote
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
Price.new pair.base, total_volume
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
attr_reader :transactions
|
|
107
|
+
|
|
108
|
+
def orders
|
|
109
|
+
unless @orders_sorted
|
|
110
|
+
sort_by_relevance @orders
|
|
111
|
+
@orders_sorted = true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
@orders
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def slice(_skip_volume, _volume=nil)
|
|
118
|
+
return to_enum(:slice, _skip_volume, _volume) unless block_given?
|
|
119
|
+
|
|
120
|
+
orders.each do |order|
|
|
121
|
+
if order.volume.amount >= _skip_volume
|
|
122
|
+
volume = order.volume.amount - _skip_volume
|
|
123
|
+
|
|
124
|
+
unless _volume.nil?
|
|
125
|
+
volume = _volume if _volume < volume
|
|
126
|
+
_volume -= volume
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
yield order, volume
|
|
130
|
+
break if _volume == 0.0
|
|
131
|
+
_skip_volume = 0.0
|
|
132
|
+
else
|
|
133
|
+
_skip_volume -= order.volume.amount
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def sort_by_relevance(_orders)
|
|
139
|
+
_orders.sort! { |a, b| compare a.price.amount, b.price.amount }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def compare(_price_a, _price_b)
|
|
143
|
+
# abstract
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "trade-o-matic/version"
|
|
2
|
+
|
|
3
|
+
require 'trade-o-matic/structs/currency'
|
|
4
|
+
require 'trade-o-matic/structs/currency_pair'
|
|
5
|
+
require 'trade-o-matic/structs/converter'
|
|
6
|
+
require 'trade-o-matic/structs/price'
|
|
7
|
+
require 'trade-o-matic/structs/order'
|
|
8
|
+
require 'trade-o-matic/structs/transaction'
|
|
9
|
+
require 'trade-o-matic/structs/market'
|
|
10
|
+
require 'trade-o-matic/structs/slope'
|
|
11
|
+
require 'trade-o-matic/structs/ask_slope'
|
|
12
|
+
require 'trade-o-matic/structs/bid_slope'
|
|
13
|
+
|
|
14
|
+
require 'trade-o-matic/converters/fixed_converter'
|
|
15
|
+
require 'trade-o-matic/converters/sync_converter'
|
|
16
|
+
|
|
17
|
+
module Trader
|
|
18
|
+
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: trade-o-matic
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ignacio Baixas
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-12-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: gli
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.12'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.12'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: inquirer
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ~>
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.2'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ~>
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.2'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rainbow
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: pry-byebug
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.2'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ~>
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.2'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rest-client
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - '>='
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - '>='
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: bundler
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ~>
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.6'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ~>
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.6'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rake
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ~>
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '10.4'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ~>
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '10.4'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rspec
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ~>
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.1'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ~>
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '3.1'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: rspec-nc
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ~>
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0.2'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ~>
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0.2'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: guard
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ~>
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '2.11'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ~>
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '2.11'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: guard-rspec
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ~>
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '4.5'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ~>
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '4.5'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: terminal-notifier-guard
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ~>
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '1.6'
|
|
174
|
+
- - '>='
|
|
175
|
+
- !ruby/object:Gem::Version
|
|
176
|
+
version: 1.6.1
|
|
177
|
+
type: :development
|
|
178
|
+
prerelease: false
|
|
179
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ~>
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '1.6'
|
|
184
|
+
- - '>='
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: 1.6.1
|
|
187
|
+
- !ruby/object:Gem::Dependency
|
|
188
|
+
name: pry
|
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - ~>
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '0.10'
|
|
194
|
+
type: :development
|
|
195
|
+
prerelease: false
|
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
197
|
+
requirements:
|
|
198
|
+
- - ~>
|
|
199
|
+
- !ruby/object:Gem::Version
|
|
200
|
+
version: '0.10'
|
|
201
|
+
- !ruby/object:Gem::Dependency
|
|
202
|
+
name: pry-remote
|
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
|
204
|
+
requirements:
|
|
205
|
+
- - ~>
|
|
206
|
+
- !ruby/object:Gem::Version
|
|
207
|
+
version: '0.1'
|
|
208
|
+
type: :development
|
|
209
|
+
prerelease: false
|
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
211
|
+
requirements:
|
|
212
|
+
- - ~>
|
|
213
|
+
- !ruby/object:Gem::Version
|
|
214
|
+
version: '0.1'
|
|
215
|
+
- !ruby/object:Gem::Dependency
|
|
216
|
+
name: pry-nav
|
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - ~>
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: '0.2'
|
|
222
|
+
type: :development
|
|
223
|
+
prerelease: false
|
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
225
|
+
requirements:
|
|
226
|
+
- - ~>
|
|
227
|
+
- !ruby/object:Gem::Version
|
|
228
|
+
version: '0.2'
|
|
229
|
+
description: Yep
|
|
230
|
+
email:
|
|
231
|
+
- ignacio@platan.us
|
|
232
|
+
executables:
|
|
233
|
+
- trader
|
|
234
|
+
extensions: []
|
|
235
|
+
extra_rdoc_files: []
|
|
236
|
+
files:
|
|
237
|
+
- bin/trader
|
|
238
|
+
- lib/trade-o-matic.rb
|
|
239
|
+
- lib/trade-o-matic/adapters/bitstamp_account.rb
|
|
240
|
+
- lib/trade-o-matic/cli.rb
|
|
241
|
+
- lib/trade-o-matic/converters/fixed_converter.rb
|
|
242
|
+
- lib/trade-o-matic/converters/sync_converter.rb
|
|
243
|
+
- lib/trade-o-matic/flows/ask_replicator_flow.rb
|
|
244
|
+
- lib/trade-o-matic/generators/linear_generator.rb
|
|
245
|
+
- lib/trade-o-matic/structs/ask_slope.rb
|
|
246
|
+
- lib/trade-o-matic/structs/bid_slope.rb
|
|
247
|
+
- lib/trade-o-matic/structs/converter.rb
|
|
248
|
+
- lib/trade-o-matic/structs/currency.rb
|
|
249
|
+
- lib/trade-o-matic/structs/currency_pair.rb
|
|
250
|
+
- lib/trade-o-matic/structs/market.rb
|
|
251
|
+
- lib/trade-o-matic/structs/order.rb
|
|
252
|
+
- lib/trade-o-matic/structs/price.rb
|
|
253
|
+
- lib/trade-o-matic/structs/slope.rb
|
|
254
|
+
- lib/trade-o-matic/structs/transaction.rb
|
|
255
|
+
- lib/trade-o-matic/version.rb
|
|
256
|
+
homepage: https://github.com/surbtc/trade-o-matic
|
|
257
|
+
licenses:
|
|
258
|
+
- MIT
|
|
259
|
+
metadata: {}
|
|
260
|
+
post_install_message:
|
|
261
|
+
rdoc_options: []
|
|
262
|
+
require_paths:
|
|
263
|
+
- lib
|
|
264
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
265
|
+
requirements:
|
|
266
|
+
- - '>='
|
|
267
|
+
- !ruby/object:Gem::Version
|
|
268
|
+
version: '0'
|
|
269
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
|
+
requirements:
|
|
271
|
+
- - '>='
|
|
272
|
+
- !ruby/object:Gem::Version
|
|
273
|
+
version: '0'
|
|
274
|
+
requirements: []
|
|
275
|
+
rubyforge_project:
|
|
276
|
+
rubygems_version: 2.4.8
|
|
277
|
+
signing_key:
|
|
278
|
+
specification_version: 4
|
|
279
|
+
summary: Trade-O-Matic is a trade automation framework
|
|
280
|
+
test_files: []
|
|
281
|
+
has_rdoc:
|