moneymarket 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 431f681b6306e4f8f9bac8017b05eb444b8fdf2e
4
+ data.tar.gz: f27dfd43db2c3aa4eadb7e4f9e82f44760b35c1a
5
+ SHA512:
6
+ metadata.gz: c889833e9447fb731530f530aa588668e2d0d399d5730c8febaa935e7f5d8374dc88c3af6b40fa537238ef34b51527833c61235d982b768957d39bd95b98cac1
7
+ data.tar.gz: 9b9ecb7169ef94c1bb361e3a3c86f0bb85c5e633029c2bb716176594ac1646066bab861326227dacd89b26b02d3c823ddbac4cde512737ff47c8ccb52417934d
data/bin/moneymarket ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # TOOD: json-RPC mode
4
+ # require 'moneymarket'
5
+ # require 'moneymarket/cli'
@@ -0,0 +1,20 @@
1
+ require 'moneymarket/version'
2
+
3
+ require 'money'
4
+ require 'moneymarket/utils/calculator'
5
+ require 'moneymarket/utils/command'
6
+
7
+ require 'moneymarket/core/order'
8
+ require 'moneymarket/core/bid'
9
+ require 'moneymarket/core/ask'
10
+ require 'moneymarket/core/market'
11
+ require 'moneymarket/core/match'
12
+ require 'moneymarket/core/account'
13
+ require 'moneymarket/core/account_provider'
14
+ require 'moneymarket/core/slope'
15
+ require 'moneymarket/core/book'
16
+
17
+ module Moneymarket
18
+ def self.book()
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ class CheckOrderSolvent < Command.new(:market, :order)
2
+ def perform
3
+ return true if order.market_order?
4
+ source_available_amount >= order.required_amount
5
+ end
6
+
7
+ private
8
+
9
+ def source_available_amount
10
+ market.source_account_for(order).available
11
+ end
12
+ end
@@ -0,0 +1,109 @@
1
+ class ApplyMatch < Command.new(:market, :match)
2
+ def perform
3
+ cache_balances :before
4
+ execute_exchange
5
+ execute_fees unless same_user?
6
+ cache_balances :after
7
+ event
8
+ end
9
+
10
+ private
11
+
12
+ def execute_exchange
13
+ bid_quote_account.unfreeze quote unless bid.new?
14
+ ask_base_account.unfreeze volume unless ask.new?
15
+
16
+ bid.consume volume
17
+ ask.consume volume
18
+
19
+ transfer quote, from: bid_quote_account, to: ask_quote_account
20
+ transfer volume, from: ask_base_account, to: bid_base_account
21
+
22
+ event.quote = quote
23
+ end
24
+
25
+ def execute_fees
26
+ transfer bid_fee, from: bid_base_account, to: exchange_base_account
27
+ transfer ask_fee, from: ask_quote_account, to: exchange_quote_account
28
+
29
+ event.bid_fee = bid_fee
30
+ event.ask_fee = ask_fee
31
+ end
32
+
33
+ def cache_balances(_which)
34
+ event.public_send("bid_base_account_#{_which}=", bid_base_account.clone)
35
+ event.public_send("bid_quote_account_#{_which}=", bid_quote_account.clone)
36
+ event.public_send("ask_base_account_#{_which}=", ask_base_account.clone)
37
+ event.public_send("ask_quote_account_#{_which}=", ask_quote_account.clone)
38
+ event.public_send("ex_base_account_#{_which}=", exchange_base_account.clone)
39
+ event.public_send("ex_quote_account_#{_which}=", exchange_quote_account.clone)
40
+ end
41
+
42
+ def event
43
+ @event ||= Events::TransactionExecuted.new.tap do |ev|
44
+ ev.bid = bid.ref
45
+ ev.ask = ask.ref
46
+ ev.volume = volume
47
+ ev.quote = quote
48
+ end
49
+ end
50
+
51
+ def bid
52
+ match.bid
53
+ end
54
+
55
+ def ask
56
+ match.ask
57
+ end
58
+
59
+ def volume
60
+ match.volume
61
+ end
62
+
63
+ def quote
64
+ @quote ||= begin
65
+ CalculateQuote.new(volume: volume, unit_price: match.price_paid).perform
66
+ end
67
+ end
68
+
69
+ def transfer(_amount, from: nil, to: nil)
70
+ from.withdraw _amount
71
+ to.deposit _amount
72
+ end
73
+
74
+ def bid_base_account
75
+ market.base_account_for match.bid
76
+ end
77
+
78
+ def ask_base_account
79
+ market.base_account_for match.ask
80
+ end
81
+
82
+ def bid_quote_account
83
+ market.quote_account_for match.bid
84
+ end
85
+
86
+ def ask_quote_account
87
+ market.quote_account_for match.ask
88
+ end
89
+
90
+ def same_user?
91
+ bid.user == ask.user
92
+ end
93
+
94
+ def bid_fee
95
+ @bid_fee ||= CalculateFee.new(order: bid, collected_amount: volume).perform
96
+ end
97
+
98
+ def ask_fee
99
+ @ask_fee ||= CalculateFee.new(order: ask, collected_amount: quote).perform
100
+ end
101
+
102
+ def exchange_base_account
103
+ market.exchange_base_account
104
+ end
105
+
106
+ def exchange_quote_account
107
+ market.exchange_quote_account
108
+ end
109
+ end
@@ -0,0 +1,28 @@
1
+ class LimitMatches < Command.new(:matches, :destination_limit)
2
+ def perform
3
+ matches.select do |match|
4
+ next false if destination_limit <= 0
5
+
6
+ quote = calc_quote(match)
7
+ if quote > destination_limit
8
+ # IDEA: instead of lowering the match amount maybe this should be considered insolvency
9
+ match.volume = calc_volume(match, destination_limit)
10
+ destination_limit = 0
11
+ else
12
+ destination_limit -= quote
13
+ end
14
+
15
+ match.volume > 0
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def calc_quote(_match)
22
+ _match.match.destination_collected_amount for_volume: _match.volume
23
+ end
24
+
25
+ def calc_volume(_match, _new_quote)
26
+ _match.match.volume_required_to_collect _new_quote
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ class MatchOrderToSlope < Command.new(:slope, :order)
2
+ def perform
3
+ Enumerator.new do |y|
4
+ remaining = order.volume
5
+ each_order do |match|
6
+ next if match.timestamp > _order.timestamp # << this should trigger an alert, should never happen
7
+
8
+ if match.volume >= remaining
9
+ y << build_match(match, remaining)
10
+ break
11
+ else
12
+ y << build_match(match, match.volume)
13
+ remaining -= match.volume
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def each_order
22
+ if order.market_order?
23
+ slope.each
24
+ else
25
+ slope.each_until_limit(order.limit)
26
+ end
27
+ end
28
+
29
+ def build_match(_match, _volume)
30
+ Match.new(trigger: order, match: _match, volume: _volume)
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ module Moneymarket
2
+ class Account
3
+ attr_reader :user, :currency, :total, :frozen, :ref
4
+
5
+ def initialize(_user, _currency, _total, _frozen, _ref = nil)
6
+ @user = _user
7
+ @currency = _currency
8
+ @total = _total
9
+ @frozen = _frozen
10
+ @ref = _ref
11
+ end
12
+
13
+ def available
14
+ total - frozen
15
+ end
16
+
17
+ def freeze(_amount)
18
+ # TODO: check currency?
19
+ raise ArgumentError, 'amount to freeze must be positive' if _amount < 0
20
+ raise ArgumentError, 'trying to freeze more than is available' if _amount > available
21
+ @frozen += _amount
22
+ end
23
+
24
+ def unfreeze(_amount)
25
+ raise ArgumentError, 'amount to unfreeze must be positive' if _amount < 0
26
+ raise ArgumentError, 'trying to unfreeze more than is frozen' if _amount > frozen
27
+ @frozen -= _amount
28
+ end
29
+
30
+ def deposit(_amount)
31
+ raise ArgumentError, 'amount to deposit must be positive' if _amount < 0
32
+ @total += _amount
33
+ end
34
+
35
+ def withdraw(_amount)
36
+ raise ArgumentError, 'amount to withdraw must be positive' if _amount < 0
37
+ raise ArgumentError, 'not enough funds to withdraw' if _amount > available
38
+ @total -= _amount
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ module Moneymarket
2
+ class AccountProvider
3
+ def initialize
4
+ @accounts = {}
5
+ end
6
+
7
+ def reset
8
+ @accounts = {}
9
+ end
10
+
11
+ def exchange_account_for(_currency)
12
+ assert_implemented :exchange_account_for
13
+ end
14
+
15
+ def account_for(_user_id, _currency)
16
+ assert_implemented :account_for
17
+ end
18
+
19
+ def preload_user_balances(_users, _currencies)
20
+ assert_implemented :preload_user_balances
21
+ end
22
+
23
+ def preload_balances(_order)
24
+ _order.base_balance = fetch_balance(_order.account_id, _order.base_currency)
25
+ _order.quote_balance = fetch_balance(_order.account_id, _order.quote_currency)
26
+ end
27
+
28
+ private
29
+
30
+ def assert_implemented(_name)
31
+ raise NotImplementedError, "#{_name} is not implemented"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Moneymarket
2
+ class Ask < Order
3
+ def bid?
4
+ false
5
+ end
6
+
7
+ def source_account
8
+ :base_account
9
+ end
10
+
11
+ def destination_account
12
+ :quote_account
13
+ end
14
+
15
+ def source_required_amount(for_volume: nil)
16
+ volume || amount
17
+ end
18
+
19
+ def destination_collected_amount(for_volume: nil)
20
+ Calculator.quote(volume: volume || amount, unit_price: limit)
21
+ end
22
+
23
+ def volume_required_to_collect(_source_amount)
24
+ _source_amount
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module Moneymarket
2
+ class Bid < Order
3
+ def bid?
4
+ true
5
+ end
6
+
7
+ def source_account
8
+ :quote_account
9
+ end
10
+
11
+ def destination_account
12
+ :base_account
13
+ end
14
+
15
+ def source_required_amount(for_volume: nil)
16
+ Calculator.quote(volume: for_volume || volume, unit_price: limit)
17
+ end
18
+
19
+ def destination_collected_amount(for_volume: nil)
20
+ for_volume || volume
21
+ end
22
+
23
+ def volume_required_to_collect(_source_amount)
24
+ Calculator.volume(
25
+ quote: _source_amount,
26
+ unit_price: limit,
27
+ unit_currency: volume.currency
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ module Moneymarket
2
+ def Book
3
+ attr_reader :asks, :bids
4
+
5
+ def initialize(_market)
6
+ @market = _market
7
+ @bids = Slope.new { |a, b| a.limit_cents <=> b.limit_cents }
8
+ @asks = Slope.new { |a, b| b.limit_cents <=> a.limit_cents }
9
+ end
10
+
11
+ def flush_events
12
+ flushed = @events
13
+ @events = []
14
+ flushed
15
+ end
16
+
17
+ def push(_order)
18
+ # TODO: check market(currency)
19
+
20
+ if _order.new?
21
+ raise ArgumentError, 'insolvent order' unless check_solvent _order
22
+
23
+ matches = match_to_slope _order
24
+ matches = adjust_market_order matches, _order if _order.market_order?
25
+ preload_balances matches
26
+ matches.each { |tx| execute_match tx }
27
+ _order.flag_as_open
28
+
29
+ freeze_source_balance _order
30
+ # TODO: execute_triggers (stop-loss, take-profit, etc)
31
+ end
32
+
33
+ slope_for(_order).add _order if _order.open?
34
+ end
35
+
36
+ def cancel(_order)
37
+ if _order.open?
38
+
39
+ slope_for(_order).remove _order
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def slope_for(_order)
46
+ if order.bid? then bids else asks end
47
+ end
48
+
49
+ def oposite_slope(_order)
50
+ if order.bid? then asks else bids end
51
+ end
52
+
53
+ def check_solvent(_order)
54
+ CheckOrderSolvent.for market: market, order: _order
55
+ end
56
+
57
+ def match_to_slope(_order)
58
+ slope = oposite_slope(_order)
59
+ MatchOrderToSlope.for slope: slope, order: _order
60
+ end
61
+
62
+ def adjust_market_order(_matches, _order)
63
+ limit = market.source_account(_order).available_amount
64
+ LimitMatches.for matches: _matches, limit: limit
65
+ end
66
+
67
+ def execute_match(_match)
68
+ @events << ExecuteMatch.for(market: market, match: _match)
69
+ end
70
+
71
+ def preload_balances(_matches)
72
+ users = _matches.map { |m| [m.bid.user, m.ask.user] }.flatten.uniq
73
+ market.preload_user_balances users
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,39 @@
1
+ module Moneymarket
2
+ class Market
3
+ attr_reader :account_provider
4
+
5
+ def initialize(_base_currency, _quote_currency, account_provider = nil)
6
+ @base_currency = _base_currency
7
+ @quote_currency = _quote_currency
8
+ @account_provider = account_provider
9
+ end
10
+
11
+ def preload_user_balances(_users)
12
+ account_provider.preload_balances(_users, [_base_currency, _quote_currency])
13
+ end
14
+
15
+ def exchange_base_account
16
+ account_provider.exchange_account_for base_currency
17
+ end
18
+
19
+ def exchange_quote_account
20
+ account_provider.exchange_account_for quote_currency
21
+ end
22
+
23
+ def base_account_for(_order)
24
+ account_provider.account_for(_order.user_id, base_currency)
25
+ end
26
+
27
+ def quote_account_for(_order)
28
+ account_provider.account_for(_order.user_id, quote_currency)
29
+ end
30
+
31
+ def source_account_for(_order)
32
+ public_send(_order.source_account, _order)
33
+ end
34
+
35
+ def destination_account_for(_order)
36
+ public_send(_order.destination_account, _order)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module Moneymarket
2
+ class Match
3
+ attr_reader :market, :bid, :ask
4
+ attr_accessor :volume
5
+
6
+ def initialize(_trigger, _match, _volume)
7
+ @trigger = _trigger
8
+ @bid = _trigger.bid? ? _trigger : _match
9
+ @ask = _trigger.bid? ? _match : _trigger
10
+ @match = _match
11
+ @volume = _volume
12
+ end
13
+
14
+ def price_paid
15
+ match.limit
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ module Moneymarket
2
+ class Order
3
+ NEW = :new
4
+ OPEN = :open
5
+ TRADED = :traded
6
+ CANCELED = :canceled
7
+
8
+ attr_reader :user, :volume, :limit, :fee, :ref
9
+
10
+ def initialize(user: nil, status: NEW, volume: nil, limit: nil, fee: 0.0, ref: nil)
11
+ @user = user
12
+ @status = status
13
+ @volume = volume
14
+ @limit = limit
15
+ @fee = fee
16
+ @ref = ref
17
+ end
18
+
19
+ def market_order?
20
+ limit.nil?
21
+ end
22
+
23
+ def base_currency
24
+ volume.currency
25
+ end
26
+
27
+ def quote_currency
28
+ limit.currency
29
+ end
30
+
31
+ def consume(_volume)
32
+ raise ArgumentError, 'cannot consume, order closed' if closed?
33
+ raise ArgumentError, 'amount to consume must be positive' if _volume < 0
34
+ raise ArgumentError, 'trying to consume more than is available' if _volume > @volume
35
+ @volume -= _volume
36
+
37
+ @status = TRADED if @volume == 0
38
+ end
39
+
40
+ def flag_as_open
41
+ if market_order?
42
+ flag_as_canceled
43
+ else
44
+ @status = OPEN if new?
45
+ end
46
+ end
47
+
48
+ def flag_as_canceled
49
+ @status = CANCELED unless closed?
50
+ end
51
+
52
+ [NEW, OPEN, TRADED, CANCELED].each do |name|
53
+ define_method("#{name}?") { @status == name }
54
+ end
55
+
56
+ def closed?
57
+ traded? || canceled?
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ module Moneymarket
2
+ class Slope
3
+ def initialize(&_comp_func)
4
+ @orders = []
5
+ @cmp = _comp_func
6
+ end
7
+
8
+ def add(_order)
9
+ idx = orders.index { |o| compare(o, _order) > 0 }
10
+ if idx.nil?
11
+ orders << _order
12
+ else
13
+ orders.insert idx, _order
14
+ end
15
+ end
16
+
17
+ def remove(_order)
18
+ orders.delete(_order)
19
+ end
20
+
21
+ def each
22
+ orders.each
23
+ end
24
+
25
+ def each_until_limit(_limit)
26
+ Enumerator.new do |y|
27
+ orders.each do |order|
28
+ break if cmp.call(order, _limit) > 0
29
+ y << order
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :orders, :cmp
37
+
38
+ def compare(a, b)
39
+ r = cmp.call a, b
40
+ r = a.created_at <=> b.created_at if r == 0
41
+ r
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ module Moneymarket
2
+ module Calculator
3
+ extend self
4
+
5
+ def quote(volume: nil, unit_price: nil)
6
+ base_currency = volume.currency
7
+ quote = ((unit_price.cents * volume.cents).to_d / base_currency.subunit_to_unit).ceil
8
+ Money.new quote, unit_price.currency
9
+ end
10
+
11
+ def volume(quote: nil, unit_price: nil, unit_currency: nil)
12
+ volume = ((quote.cents * unit_currency.subunit_to_unit).to_d / unit_price.cents).floor
13
+ Money.new volume, unit_currency
14
+ end
15
+
16
+ def fee(amount: nil, percent: nil)
17
+ result = (amount.cents * (percent.to_d / 100)).floor # fees always round to floor
18
+ Money.new result, amount.currency
19
+ end
20
+
21
+ def order_fee(order: nil, collected_amount: nil) # move this to command
22
+ collected_amount = order.collected_amount if collected_amount.nil?
23
+ fee_cents = (collected_amount.cents * (order.fee.to_d / 100)).floor
24
+ Money.new fee_cents, collected_amount.currency
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Moneymarket
2
+ module Command
3
+ def self.new(*_attributes)
4
+ attr_names = []
5
+ attr_defaults = {}
6
+
7
+ _attributes.each do |att|
8
+ if att.is_a? Hash
9
+ attr_defaults.merge att
10
+ attr_names += att.keys
11
+ else
12
+ attr_names << att
13
+ end
14
+ end
15
+
16
+ Struct.new(*attr_names) do
17
+ define_method(:initialize) do |kwargs={}|
18
+ kwargs = attr_defaults.merge kwargs
19
+ attr_values = attr_names.map { |a| kwargs[a] }
20
+ super(*attr_values)
21
+ end
22
+
23
+ define_method(:perform) {}
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Trader
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,238 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: moneymarket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ignacio Baixas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: money
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '6.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '6.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '10.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '10.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-nc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '2.11'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.11'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '4.5'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '4.5'
111
+ - !ruby/object:Gem::Dependency
112
+ name: terminal-notifier-guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.6'
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: 1.6.1
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ~>
126
+ - !ruby/object:Gem::Version
127
+ version: '1.6'
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: 1.6.1
131
+ - !ruby/object:Gem::Dependency
132
+ name: pry
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ~>
136
+ - !ruby/object:Gem::Version
137
+ version: '0.10'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ~>
143
+ - !ruby/object:Gem::Version
144
+ version: '0.10'
145
+ - !ruby/object:Gem::Dependency
146
+ name: pry-remote
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ~>
150
+ - !ruby/object:Gem::Version
151
+ version: '0.1'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ~>
157
+ - !ruby/object:Gem::Version
158
+ version: '0.1'
159
+ - !ruby/object:Gem::Dependency
160
+ name: pry-byebug
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: '3.2'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ~>
171
+ - !ruby/object:Gem::Version
172
+ version: '3.2'
173
+ - !ruby/object:Gem::Dependency
174
+ name: pry-nav
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ~>
178
+ - !ruby/object:Gem::Version
179
+ version: '0.2'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ~>
185
+ - !ruby/object:Gem::Version
186
+ version: '0.2'
187
+ description: Yep
188
+ email:
189
+ - ignacio@platan.us
190
+ executables:
191
+ - moneymarket
192
+ extensions: []
193
+ extra_rdoc_files: []
194
+ files:
195
+ - bin/moneymarket
196
+ - lib/moneymarket.rb
197
+ - lib/moneymarket/commands/check_order_solvent.rb
198
+ - lib/moneymarket/commands/execute_match.rb
199
+ - lib/moneymarket/commands/limit_matches.rb
200
+ - lib/moneymarket/commands/match_order_to_slope.rb
201
+ - lib/moneymarket/core/account.rb
202
+ - lib/moneymarket/core/account_provider.rb
203
+ - lib/moneymarket/core/ask.rb
204
+ - lib/moneymarket/core/bid.rb
205
+ - lib/moneymarket/core/book.rb
206
+ - lib/moneymarket/core/market.rb
207
+ - lib/moneymarket/core/match.rb
208
+ - lib/moneymarket/core/order.rb
209
+ - lib/moneymarket/core/slope.rb
210
+ - lib/moneymarket/utils/calculator.rb
211
+ - lib/moneymarket/utils/command.rb
212
+ - lib/moneymarket/version.rb
213
+ homepage: https://github.com/surbtc/moneymarket
214
+ licenses:
215
+ - MIT
216
+ metadata: {}
217
+ post_install_message:
218
+ rdoc_options: []
219
+ require_paths:
220
+ - lib
221
+ required_ruby_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - '>='
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ required_rubygems_version: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - '>='
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 2.6.1
234
+ signing_key:
235
+ specification_version: 4
236
+ summary: Moneymarket is a lightweight in-memory foreign exchange market engine
237
+ test_files: []
238
+ has_rdoc: