heisencoin 0.0.4 → 0.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a517b7b9c3fcf114963652c00454aa5c5dcf6f26
4
- data.tar.gz: 616a927a7ea8aee4b1d035871d5b18843b922215
3
+ metadata.gz: 326b290fabe0c62d7550a2dfb52874ed1ca31e30
4
+ data.tar.gz: c573f496f45fa8286c49ab654204c967df72493a
5
5
  SHA512:
6
- metadata.gz: 42d4787b3c35d1b7bc4e02391c812a504c7aefd768be7d566b8afbba875337116544705d3c03f3e841621192cae4172655202a98fdb6137fc88148d34474ccb6
7
- data.tar.gz: 3abe1f3032fbfd2f1999ef0e7727e17c82275ce8fb404a16ac52dcdc7aed8a3da657c0c223b510530eb76fe9b633d2130a01472292531cca6db83817c19aaddb
6
+ metadata.gz: 199af64a408a1f9008b380d330fea5358b0776b03d5c74ea4665b08831d9d8d31fc37802c52aeb1095e738e6fd2bfdf06f1f70fb6d258b7fc268c3a96f351cfe
7
+ data.tar.gz: 67d94d62b442509717b2eccfe5dfa4e982f3a6756fc7ec1d86f88d6f905bebb73a8d37705ab8e91e6e2a756118a4d2e12accdebb567aea3115fa409706ccf9c1
@@ -0,0 +1,100 @@
1
+ module Heisencoin
2
+ class Arbitrage
3
+ attr_accessor :exchanges, :bids, :asks
4
+
5
+ def initialize
6
+ @exchanges = []
7
+ @asks = Market.new(:ask)
8
+ @bids = Market.new(:bid)
9
+ end
10
+
11
+ def add_exchanges(exchanges)
12
+ @exchanges += exchanges
13
+ end
14
+
15
+ def exchange(name)
16
+ exchanges.select{|e| e.name == name}.first
17
+ end
18
+
19
+ def add_depth(exchange, depth)
20
+ @asks.drop_offers(exchange)
21
+ @asks.import(exchange, depth["asks"])
22
+ @bids.drop_offers(exchange)
23
+ @bids.import(exchange, depth["bids"])
24
+ end
25
+
26
+ def best_price(market1, market2)
27
+ level = nil
28
+ market1.offers.each do |offer|
29
+ good = market2.offers.any?{|offer_other| yield offer.price, offer_other.price}
30
+ if good
31
+ level = offer.price
32
+ break
33
+ end
34
+ end
35
+ level
36
+ end
37
+
38
+ def profitable_asks(highwater=nil)
39
+ highwater ||= best_price(@bids, @asks){|offer, other| offer > other}
40
+ if highwater
41
+ @asks.offers_better_than(highwater)
42
+ else
43
+ []
44
+ end
45
+ end
46
+
47
+ def profitable_bids(lowwater=nil)
48
+ lowwater ||= best_price(@asks, @bids){|offer, other| offer < other}
49
+ if lowwater
50
+ @bids.offers_better_than(lowwater)
51
+ else
52
+ []
53
+ end
54
+ end
55
+
56
+ def trade_all(asks, bids)
57
+ working_bids = bids.dup
58
+ trades = []
59
+ asks.each do |ask|
60
+ step_trades = consume_ask(working_bids, ask, ask.price*(1-ask.exchange.fee))
61
+ trades += step_trades
62
+ qty_traded = step_trades.reduce(0){|memo, trade| memo += trade.quantity} #sum
63
+ break if qty_traded < ask.quantity #out of coins
64
+ end
65
+ trades
66
+ end
67
+
68
+ def consume_ask(bids, ask, price_limit)
69
+ trades = []
70
+ price = price_limit
71
+ quantity = ask.quantity
72
+ near_zero = 0.0001 #floats
73
+ bids.each do |bid|
74
+ if (bid.price*(1+bid.exchange.fee)) >= price
75
+ if bid.quantity > near_zero
76
+ trade_quantity = [bid.quantity, quantity].min
77
+ trades << Trade.new({'from_offer' => ask.to_simple,
78
+ 'to_offer' => bid.to_simple,
79
+ 'quantity' => trade_quantity})
80
+ bid.quantity -= trade_quantity
81
+ quantity -= trade_quantity
82
+ end
83
+ end
84
+ break if quantity < near_zero
85
+ end
86
+ trades
87
+ end
88
+
89
+ def plan
90
+ #buy sell plan
91
+ plan = Plan.new
92
+ plan << trade_all(profitable_asks, profitable_bids)
93
+ end
94
+
95
+ def spread
96
+ @asks.offers.first.price - @bids.offers.first.price
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,26 @@
1
+ require 'time'
2
+
3
+ module Heisencoin
4
+ class Exchange
5
+ attr_accessor :name, :fee, :time
6
+
7
+ def initialize(simple = nil)
8
+ from_simple(simple) if simple
9
+ end
10
+
11
+ def from_simple(simple)
12
+ @name = simple['name']
13
+ @fee = simple['fee']
14
+ end
15
+
16
+ def to_simple
17
+ {'name' => name,
18
+ 'fee' => fee}
19
+ end
20
+
21
+ def ==(other)
22
+ self.name == other.name
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,56 @@
1
+ module Heisencoin
2
+ class Market
3
+ attr_accessor :offers
4
+
5
+ def initialize(half)
6
+ @offers = []
7
+ if half == :bid || half == :ask
8
+ @half = half
9
+ else
10
+ raise "Bad market type parameter"
11
+ end
12
+ end
13
+
14
+ def import(exchange, offers)
15
+ offers.each do |raw_offer|
16
+ offer = Offer.from_array(exchange, [raw_offer.first, raw_offer.last])
17
+ sorted_insert(@offers, offer) {|offer| offer.price}
18
+ end
19
+ end
20
+
21
+ def drop_offers(exchange)
22
+ offers.reject!{|o| o.exchange == exchange}
23
+ end
24
+
25
+ def sorted_insert(array, element)
26
+ value = yield element
27
+ if array.length == 0 || better_than(value, (yield array[0]))
28
+ array.unshift(element)
29
+ else
30
+ last_index = array.length-1
31
+ for idx in 0..last_index
32
+ if not better_than((yield array[idx]), value)
33
+ array.insert(idx, element)
34
+ break
35
+ end
36
+ if idx == last_index
37
+ array.push(element)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def better_than(a,b)
44
+ if @half == :ask
45
+ a < b
46
+ elsif @half == :bid
47
+ a > b
48
+ end
49
+ end
50
+
51
+ def offers_better_than(price)
52
+ offers.select{|offer| better_than(offer.price, price)}
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ module Heisencoin
2
+ class Offer
3
+ attr_accessor :exchange, :price, :quantity
4
+
5
+ def initialize(simple = nil)
6
+ from_simple(simple) if simple
7
+ end
8
+
9
+ def from_simple(simple)
10
+ @exchange = Exchange.new(simple['exchange'])
11
+ @price = simple['price']
12
+ @quantity = simple['quantity']
13
+ end
14
+
15
+ def to_simple
16
+ {'exchange' => exchange.to_simple,
17
+ 'price' => price,
18
+ 'quantity' => quantity}
19
+ end
20
+
21
+ def ==(other)
22
+ exchange.name == other.exchange.name
23
+ price == other.price
24
+ quantity = other.quantity
25
+ end
26
+
27
+ def cost(quantity)
28
+ quantity * price
29
+ end
30
+
31
+ def self.from_array(exchange, raw_offer)
32
+ Offer.new({'exchange' => exchange.to_simple,
33
+ 'price' => raw_offer[0],
34
+ 'quantity' => raw_offer[1]})
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ module Heisencoin
2
+ class Plan
3
+ attr_accessor :steps, :state, :purse, :spent
4
+
5
+ @@states = ["planning", "buying", "moving-in", "selling", "moving-out"]
6
+
7
+ def initialize(simple = nil)
8
+ @steps = []
9
+ @state = @@states.first
10
+ from_simple(simple) if simple
11
+ end
12
+
13
+ def from_simple(simple)
14
+ @state = simple["state"]
15
+ @purse = simple["purse"]
16
+ @spent = simple["spent"]
17
+ @steps = simple["steps"].map{|trade_simple| Trade.new(trade_simple)}
18
+ end
19
+
20
+ def to_simple
21
+ {"state" => @state,
22
+ "purse" => @purse,
23
+ "spent" => @spent,
24
+ "steps" => @steps.map{|step| step.to_simple}}
25
+ end
26
+
27
+ def <<(trades)
28
+ @steps += trades
29
+ self
30
+ end
31
+
32
+ def quantity
33
+ @steps.reduce(0){|sum,trade|sum+trade.quantity}
34
+ end
35
+
36
+ def profit
37
+ @steps.reduce(0){|total, trade| total+trade.profit}
38
+ end
39
+
40
+ def cost
41
+ @steps.reduce(0){|total, trade| total+trade.cost}
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ module Heisencoin
2
+ class Trade
3
+ attr_accessor :from_offer, :to_offer, :quantity
4
+
5
+ def initialize(simple = nil)
6
+ from_simple(simple) if simple
7
+ end
8
+
9
+ def from_simple(simple)
10
+ @from_offer = Offer.new(simple['from_offer'])
11
+ @to_offer = Offer.new(simple['to_offer'])
12
+ @quantity = simple['quantity']
13
+ end
14
+
15
+ def to_simple
16
+ {'from_offer' => @from_offer.to_simple,
17
+ 'to_offer' => @to_offer.to_simple,
18
+ 'quantity' => @quantity}
19
+ end
20
+
21
+ def ==(other)
22
+ @from_offer == other.from_offer
23
+ @to_offer == other.to_offer
24
+ @quantity == other.quantity
25
+ end
26
+
27
+ def cost
28
+ from_offer.cost(@quantity)
29
+ end
30
+
31
+ def profit
32
+ earned = to_offer.cost(@quantity)
33
+ earned - cost
34
+ end
35
+
36
+ end
37
+ end
data/tests/arbitrage.rb CHANGED
@@ -52,8 +52,8 @@ class TestMeme < Minitest::Test
52
52
  before do
53
53
  # full setup
54
54
  @arby = Heisencoin::Arbitrage.new
55
- @ex1 = Heisencoin::Exchange.new({'name' =>'btcx'})
56
- @ex2 = Heisencoin::Exchange.new({'name' =>'crytpsy'})
55
+ @ex1 = Heisencoin::Exchange.new({'name' =>'btcx', 'fee' => 0.2})
56
+ @ex2 = Heisencoin::Exchange.new({'name' =>'crytpsy', 'fee' => 0.3})
57
57
  @arby.add_exchanges([@ex1, @ex2])
58
58
  # ex1 has a 14.1 and 14.2 bid above ex2's 14 ask
59
59
  depth = {"asks" => [ [16,1],
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heisencoin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Don Park
@@ -17,6 +17,12 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - lib/heisencoin.rb
20
+ - lib/heisencoin/trade.rb
21
+ - lib/heisencoin/market.rb
22
+ - lib/heisencoin/exchange.rb
23
+ - lib/heisencoin/plan.rb
24
+ - lib/heisencoin/offer.rb
25
+ - lib/heisencoin/arbitrage.rb
20
26
  - tests/trade.rb
21
27
  - tests/offer.rb
22
28
  - tests/arbitrage.rb