bitex_bot 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +63 -0
  3. data/.rubocop.yml +33 -0
  4. data/Gemfile +1 -1
  5. data/Rakefile +1 -1
  6. data/bin/bitex_bot +1 -1
  7. data/bitex_bot.gemspec +34 -34
  8. data/lib/bitex_bot/database.rb +67 -67
  9. data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +142 -0
  10. data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +137 -0
  11. data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +116 -0
  12. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +111 -0
  13. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +117 -0
  14. data/lib/bitex_bot/models/buy_closing_flow.rb +23 -16
  15. data/lib/bitex_bot/models/buy_opening_flow.rb +48 -54
  16. data/lib/bitex_bot/models/close_buy.rb +2 -2
  17. data/lib/bitex_bot/models/closing_flow.rb +98 -79
  18. data/lib/bitex_bot/models/open_buy.rb +11 -10
  19. data/lib/bitex_bot/models/open_sell.rb +11 -10
  20. data/lib/bitex_bot/models/opening_flow.rb +157 -99
  21. data/lib/bitex_bot/models/order_book_simulator.rb +62 -67
  22. data/lib/bitex_bot/models/sell_closing_flow.rb +25 -20
  23. data/lib/bitex_bot/models/sell_opening_flow.rb +47 -54
  24. data/lib/bitex_bot/models/store.rb +3 -1
  25. data/lib/bitex_bot/robot.rb +203 -176
  26. data/lib/bitex_bot/settings.rb +71 -12
  27. data/lib/bitex_bot/version.rb +1 -1
  28. data/lib/bitex_bot.rb +40 -16
  29. data/settings.rb.sample +43 -66
  30. data/spec/bitex_bot/settings_spec.rb +87 -15
  31. data/spec/factories/bitex_buy.rb +3 -3
  32. data/spec/factories/bitex_sell.rb +3 -3
  33. data/spec/factories/buy_opening_flow.rb +1 -1
  34. data/spec/factories/open_buy.rb +12 -10
  35. data/spec/factories/open_sell.rb +12 -10
  36. data/spec/factories/sell_opening_flow.rb +1 -1
  37. data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +200 -0
  38. data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +176 -0
  39. data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +209 -0
  40. data/spec/models/bitex_api_spec.rb +1 -1
  41. data/spec/models/buy_closing_flow_spec.rb +140 -71
  42. data/spec/models/buy_opening_flow_spec.rb +126 -56
  43. data/spec/models/order_book_simulator_spec.rb +10 -10
  44. data/spec/models/robot_spec.rb +61 -47
  45. data/spec/models/sell_closing_flow_spec.rb +130 -62
  46. data/spec/models/sell_opening_flow_spec.rb +129 -60
  47. data/spec/spec_helper.rb +19 -16
  48. data/spec/support/bitex_stubs.rb +13 -14
  49. data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +35 -0
  50. data/spec/support/bitstamp/bitstamp_stubs.rb +91 -0
  51. metadata +60 -42
  52. data/lib/bitex_bot/models/bitfinex_api_wrapper.rb +0 -118
  53. data/lib/bitex_bot/models/bitstamp_api_wrapper.rb +0 -82
  54. data/lib/bitex_bot/models/itbit_api_wrapper.rb +0 -68
  55. data/lib/bitex_bot/models/kraken_api_wrapper.rb +0 -188
  56. data/spec/models/bitfinex_api_wrapper_spec.rb +0 -17
  57. data/spec/models/bitstamp_api_wrapper_spec.rb +0 -15
  58. data/spec/models/itbit_api_wrapper_spec.rb +0 -15
  59. data/spec/support/bitstamp_stubs.rb +0 -110
@@ -0,0 +1,116 @@
1
+ # Wrapper implementation for Itbit API.
2
+ # https://api.itbit.com/docs
3
+ class ItbitApiWrapper < ApiWrapper
4
+ def self.setup(settings)
5
+ Itbit.tap do |conf|
6
+ conf.client_key = settings.client_key
7
+ conf.secret = settings.secret
8
+ conf.user_id = settings.user_id
9
+ conf.default_wallet_id = settings.default_wallet_id
10
+ conf.sandbox = settings.sandbox
11
+ end
12
+ end
13
+
14
+ def self.amount_and_quantity(order_id, _transactions)
15
+ order = Itbit::Order.find(order_id)
16
+ amount = order.volume_weighted_average_price * order.amount_filled
17
+ quantity = order.amount_filled
18
+
19
+ [amount, quantity]
20
+ end
21
+
22
+ def self.balance
23
+ balance_summary_parser(wallet[:balances])
24
+ end
25
+
26
+ def self.find_lost(type, price, _quantity)
27
+ orders.find { |o| o.type == type && o.price == price && o.timestamp >= 5.minutes.ago.to_i }
28
+ end
29
+
30
+ def self.order_book
31
+ order_book_parser(Itbit::XBTUSDMarketData.orders)
32
+ end
33
+
34
+ def self.orders
35
+ Itbit::Order.all(status: :open).map { |o| order_parser(o) }
36
+ end
37
+
38
+ def self.place_order(type, price, quantity)
39
+ Itbit::Order.create!(type, :xbtusd, quantity.round(4), price.round(2), wait: true)
40
+ rescue RestClient::RequestTimeout => e
41
+ # On timeout errors, we still look for the latest active closing order that may be available.
42
+ # We have a magic threshold of 5 minutes and also use the price to recognize an order as the current one.
43
+ # TODO: Maybe we can identify the order using metadata instead of price.
44
+ BitexBot::Robot.log(:error, 'Captured Timeout on itbit')
45
+ latest = last_order_by(price)
46
+ return latest if latest.present?
47
+
48
+ BitexBot::Robot.log(:error, 'Could not find my order')
49
+ raise e
50
+ end
51
+
52
+ def self.transactions
53
+ Itbit::XBTUSDMarketData.trades.map { |t| transaction_parser(t.symbolize_keys) }
54
+ end
55
+
56
+ # We don't need to fetch the list of transaction for itbit since we wont actually use them later.
57
+ def self.user_transactions
58
+ []
59
+ end
60
+
61
+ private_class_method
62
+
63
+ # [
64
+ # { total_balance: 0.2e2, currency: :usd, available_balance: 0.1e2 },
65
+ # { total_balance: 0.0, currency: :xbt, available_balance: 0.0 },
66
+ # { total_balance: 0.0, currency: :eur, available_balance: 0.0 },
67
+ # { total_balance: 0.0, currency: :sgd, available_balance: 0.0 }
68
+ # ]
69
+ def self.balance_summary_parser(balances)
70
+ BalanceSummary.new(balance_parser(balances, :xbt), balance_parser(balances, :usd), 0.5.to_d)
71
+ end
72
+
73
+ def self.wallet
74
+ Itbit::Wallet.all.find { |w| w[:id] == Itbit.default_wallet_id }
75
+ end
76
+
77
+ def self.balance_parser(balances, currency)
78
+ currency_balance = balances.find { |balance| balance[:currency] == currency }
79
+ Balance.new(
80
+ currency_balance[:total_balance].to_d,
81
+ currency_balance[:total_balance].to_d - currency_balance[:available_balance].to_d,
82
+ currency_balance[:available_balance].to_d
83
+ )
84
+ end
85
+
86
+ def self.last_order_by(price)
87
+ Itbit::Order.all.select { |o| o.price == price && (o.created_time - Time.now.to_i).abs < 500 }.first
88
+ end
89
+
90
+ # {
91
+ # bids: [[0.63921e3, 0.195e1], [0.637e3, 0.47e0], [0.63e3, 0.158e1]],
92
+ # asks: [[0.6424e3, 0.4e0], [0.6433e3, 0.95e0], [0.6443e3, 0.25e0]]
93
+ # }
94
+ def self.order_book_parser(book)
95
+ OrderBook.new(Time.now.to_i, order_summary_parser(book[:bids]), order_summary_parser(book[:asks]))
96
+ end
97
+
98
+ def self.order_summary_parser(orders)
99
+ orders.map { |order| OrderSummary.new(order[0], order[1]) }
100
+ end
101
+
102
+ # <Itbit::Order:
103
+ # @id='8fd820d3-baff-4d6f-9439-ff03d816c7ce', @wallet_id='b440efce-a83c-4873-8833-802a1022b476', @side=:buy,
104
+ # @instrument=:xbtusd, @type=:limit, @amount=0.1005e1, @display_amount=0.1005e1, @price=0.1e3,
105
+ # @volume_weighted_average_price=0.0, @amount_filled=0.0, @created_time=1415290187, @status=:open,
106
+ # @metadata={foo: 'bar'}, @client_order_identifier='o'
107
+ # >
108
+ def self.order_parser(order)
109
+ Order.new(order.id, order.side, order.price, order.amount, order.created_time, order)
110
+ end
111
+
112
+ # { tid: 601855, price: 0.41814e3, amount: 0.19e-1, date: 1460161126 }
113
+ def self.transaction_parser(transaction)
114
+ Transaction.new(transaction[:tid], transaction[:price], transaction[:amount], transaction[:date])
115
+ end
116
+ end
@@ -0,0 +1,111 @@
1
+ # Wrapper implementation for Kraken API.
2
+ # https://www.kraken.com/en-us/help/api
3
+ class KrakenApiWrapper < ApiWrapper
4
+ MIN_AMOUNT = 0.002
5
+
6
+ def self.setup(settings)
7
+ HTTParty::Basement.headers('User-Agent' => BitexBot.user_agent)
8
+ @settings = settings
9
+ end
10
+
11
+ def self.client
12
+ @client ||= KrakenClient.load(@settings)
13
+ end
14
+
15
+ def self.amount_and_quantity(order_id, _transactions)
16
+ KrakenOrder.amount_and_quantity(order_id)
17
+ end
18
+
19
+ def self.balance
20
+ balance_summary_parser(client.private.balance)
21
+ rescue KrakenClient::ErrorResponse, Net::ReadTimeout
22
+ retry
23
+ end
24
+
25
+ def self.enough_order_size?(quantity, _price)
26
+ quantity >= MIN_AMOUNT
27
+ end
28
+
29
+ def self.find_lost(type, price, quantity)
30
+ KrakenOrder.find_lost(type, price, quantity)
31
+ end
32
+
33
+ def self.order_book
34
+ order_book_parser(client.public.order_book('XBTUSD')[:XXBTZUSD])
35
+ rescue NoMethodError
36
+ retry
37
+ end
38
+
39
+ def self.orders
40
+ KrakenOrder.open.map { |ko| order_parser(ko) }
41
+ end
42
+
43
+ def self.send_order(type, price, quantity)
44
+ KrakenOrder.create!(type, price, quantity)
45
+ end
46
+
47
+ def self.transactions
48
+ client.public.trades('XBTUSD')[:XXBTZUSD].reverse.map { |t| transaction_parser(t) }
49
+ rescue NoMethodError
50
+ retry
51
+ end
52
+
53
+ # We don't need to fetch the list of transactions for Kraken
54
+ def self.user_transactions
55
+ []
56
+ end
57
+
58
+ private_class_method
59
+
60
+ # { ZEUR: '1433.0939', XXBT: '0.0000000000', 'XETH': '99.7497224800' }
61
+ def self.balance_summary_parser(balances)
62
+ open_orders = KrakenOrder.open
63
+ BalanceSummary.new(
64
+ balance_parser(balances, :XXBT, btc_reserved(open_orders)),
65
+ balance_parser(balances, :ZUSD, usd_reserved(open_orders)),
66
+ client.private.trade_volume(pair: 'XBTUSD')[:fees][:XXBTZUSD][:fee].to_d
67
+ )
68
+ end
69
+
70
+ def self.balance_parser(balances, currency, reserved)
71
+ Balance.new(balances[currency].to_d, reserved, balances[currency].to_d - reserved)
72
+ end
73
+
74
+ def self.btc_reserved(open_orders)
75
+ orders_by(open_orders, :sell).map { |o| (o.amount - o.executed_amount).to_d }.sum
76
+ end
77
+
78
+ def self.usd_reserved(open_orders)
79
+ orders_by(open_orders, :buy).map { |o| (o.amount - o.executed_amount) * o.price.to_d }.sum
80
+ end
81
+
82
+ def self.orders_by(open_orders, order_type)
83
+ open_orders.select { |o| o.type == order_type }
84
+ end
85
+
86
+ # {
87
+ # 'asks': [['204.52893', '0.010', 1440291148], ['204.78790', '0.312', 1440291132]],
88
+ # 'bids': [['204.24000', '0.100', 1440291016], ['204.23010', '0.312', 1440290699]]
89
+ # }
90
+ def self.order_book_parser(book)
91
+ OrderBook.new(Time.now.to_i, order_summary_parser(book[:bids]), order_summary_parser(book[:asks]))
92
+ end
93
+
94
+ def self.order_summary_parser(stock_market)
95
+ stock_market.map { |stock| OrderSummary.new(stock[0].to_d, stock[1].to_d) }
96
+ end
97
+
98
+ # <KrakenOrder: @id='O5TDV2-WDYB2-6OGJRD', @type=:buy, @price='1.01', @amount='1.00000000', @datetime='2013-09-26 23:15:04'>
99
+ def self.order_parser(order)
100
+ Order.new(order.id.to_s, order.type, order.price, order.amount, order.datetime, order)
101
+ end
102
+
103
+ # [
104
+ # ['price', 'amount', 'timestamp', 'buy/sell', 'market/limit', 'miscellaneous']
105
+ # ['202.51626', '0.01440000', 1440277319.1922, 'b', 'l', ''],
106
+ # ['202.54000', '0.10000000', 1440277322.8993, 'b', 'l', '']
107
+ # ]
108
+ def self.transaction_parser(transaction)
109
+ Transaction.new(transaction[2].to_i, transaction[0].to_d, transaction[1].to_d, transaction[2].to_i)
110
+ end
111
+ end
@@ -0,0 +1,117 @@
1
+ require 'kraken_client'
2
+
3
+ # Wrapper for Kraken orders.
4
+ class KrakenOrder
5
+ cattr_accessor :last_closed_order
6
+ attr_accessor :id, :amount, :executed_amount, :price, :avg_price, :type, :datetime
7
+
8
+ # rubocop:disable Metrics/AbcSize
9
+ def self.create!(type, price, quantity)
10
+ self.last_closed_order = closed.first.try(:id) || Time.now.to_i
11
+ find(order_info_by(type, price.truncate(1), quantity.truncate(8))['txid'].first)
12
+ rescue KrakenClient::ErrorResponse => e
13
+ # Order could not be placed
14
+ if e.message == 'EService:Unavailable'
15
+ BitexBot::Robot.log(:debug, 'Captured EService:Unavailable error when placing order on Kraken. Retrying...')
16
+ retry
17
+ elsif e.message.start_with?('EGeneral:Invalid')
18
+ BitexBot::Robot.log(:debug, "Captured #{e.message}: type: #{type}, price: #{price}, quantity: #{quantity}")
19
+ raise OrderArgumentError, e.message
20
+ elsif e.message != 'error'
21
+ raise
22
+ end
23
+ end
24
+ # rubocop:enable Metrics/AbcSize
25
+
26
+ def self.order_info_by(type, price, quantity)
27
+ KrakenApiWrapper.client.private.add_order(pair: 'XBTUSD', type: type, ordertype: 'limit', price: price, volume: quantity)
28
+ end
29
+
30
+ def self.find(id)
31
+ new(*KrakenApiWrapper.client.private.query_orders(txid: id).first)
32
+ rescue KrakenClient::ErrorResponse
33
+ retry
34
+ end
35
+
36
+ def self.amount_and_quantity(order_id)
37
+ order = find(order_id)
38
+ amount = order.avg_price * order.executed_amount
39
+ quantity = order.executed_amount
40
+
41
+ [amount, quantity]
42
+ end
43
+
44
+ def self.open
45
+ KrakenApiWrapper.client.private.open_orders['open'].map { |o| new(*o) }
46
+ rescue KrakenClient::ErrorResponse
47
+ retry
48
+ end
49
+
50
+ def self.closed(start: 1.hour.ago.to_i)
51
+ KrakenApiWrapper.client.private.closed_orders(start: start)[:closed].map { |o| new(*o) }
52
+ rescue KrakenClient::ErrorResponse
53
+ retry
54
+ end
55
+
56
+ def self.find_lost(type, price, quantity)
57
+ BitexBot::Robot.log(:debug, "Looking for #{type} order in open orders...")
58
+ order = open_order_by(type, price, quantity)
59
+ return log_and_return(order, :open) if order.present?
60
+
61
+ BitexBot::Robot.log(:debug, "Looking for #{type} order in closed orders...")
62
+ order = closed_order_by(type, price, quantity)
63
+ return log_and_return(order, :closed) if order && order.id != last_closed_order
64
+ end
65
+
66
+ def self.log_and_return(order, status)
67
+ BitexBot::Robot.log(:debug, "Found open #{status} with ID #{order.id}")
68
+ order
69
+ end
70
+
71
+ # description: [type, price, quantity]
72
+ def self.open_order_by(type, price, quantity)
73
+ open.detect { |o| o == [type, price, quantity] }
74
+ end
75
+
76
+ # description: [type, price, quantity]
77
+ def self.closed_order_by(type, price, quantity)
78
+ closed(start: last_closed_order).detect { |o| o == [type, price, quantity] }
79
+ end
80
+
81
+ # id: 'O5TDV2-WDYB2-6OGJRD'
82
+ # order_data: {
83
+ # 'refid': nil, 'userref': nil, 'status': 'open', 'opentm': 1440292821.4839, 'starttm': 0, 'expiretm': 0,
84
+ # 'descr': {
85
+ # 'pair': 'ETHEUR', 'type': 'buy', 'ordertype': 'limit', 'price': '1.19000', 'price2': '0', 'leverage': 'none',
86
+ # 'order': 'buy 1204.00000000 ETHEUR @ limit 1.19000'
87
+ # },
88
+ # 'vol': '1204.00000000', 'vol_exec': '0.00000000', 'cost': '0.00000', 'fee': '0.00000', 'price': '0.00000',
89
+ # 'misc': '', 'oflags': 'fciq'
90
+ # }
91
+ # }
92
+ def initialize(id, order_data)
93
+ self.id = id
94
+ self.type = order_data[:descr][:type].to_sym
95
+ self.datetime = order_data[:opentm].to_i
96
+ self.amount = order_data[:vol].to_d
97
+ self.executed_amount = order_data[:vol_exec].to_d
98
+ self.price = order_data[:descr][:price].to_d
99
+ self.avg_price = order_data[:price].to_d
100
+ end
101
+
102
+ def cancel!
103
+ KrakenApiWrapper.client.private.cancel_order(txid: id)
104
+ rescue KrakenClient::ErrorResponse => e
105
+ e.message == 'EService:Unavailable' ? retry : raise
106
+ end
107
+
108
+ def ==(other)
109
+ if other.is_a?(self.class)
110
+ other.id == id
111
+ elsif other.is_a?(Array)
112
+ other == [type, price, amount]
113
+ end
114
+ end
115
+ end
116
+
117
+ class OrderArgumentError < StandardError; end
@@ -1,34 +1,41 @@
1
1
  module BitexBot
2
+ # It sold at Bitex and needs to close (buy) in the other market.
2
3
  class BuyClosingFlow < ClosingFlow
3
4
  has_many :open_positions, class_name: 'OpenBuy', foreign_key: :closing_flow_id
4
5
  has_many :close_positions, class_name: 'CloseBuy', foreign_key: :closing_flow_id
5
- scope :active, lambda { where(done: false) }
6
+
7
+ scope :active, -> { where(done: false) }
6
8
 
7
9
  def self.open_position_class
8
10
  OpenBuy
9
11
  end
10
-
11
- def order_method
12
- :sell
13
- end
14
12
 
15
- # The amount received when selling initially, minus
16
- # the amount spent re-buying the sold coins.
17
- def get_usd_profit
18
- close_positions.sum(:amount) - open_positions.sum(:amount)
19
- end
20
-
21
- # The coins we actually bought minus the coins we were supposed
22
- # to re-buy
23
- def get_btc_profit
13
+ private
14
+
15
+ # create_or_cancel! hookers
16
+ # The coins we actually bought minus the coins we were supposed to re-buy
17
+ def estimate_btc_profit
24
18
  quantity - close_positions.sum(:quantity)
25
19
  end
26
20
 
27
- def get_next_price_and_quantity
21
+ # The amount received when selling initially, minus the amount spent re-buying the sold coins.
22
+ def estimate_fiat_profit
23
+ positions_balance_amount - open_positions.sum(:amount)
24
+ end
25
+
26
+ def next_price_and_quantity
28
27
  closes = close_positions
29
- next_price = desired_price - ((closes.count * (closes.count * 3)) / 100.0)
28
+ next_price = desired_price - price_variation(closes.count)
30
29
  next_quantity = quantity - closes.sum(:quantity)
30
+
31
31
  [next_price, next_quantity]
32
32
  end
33
+ # end: create_or_cancel! hookers
34
+
35
+ # create_order_and_close_position hookers
36
+ def order_method
37
+ :sell
38
+ end
39
+ # end: create_order_and_close_position hookers
33
40
  end
34
41
  end
@@ -1,83 +1,77 @@
1
1
  module BitexBot
2
- # A workflow for buying bitcoin in Bitex and selling on another exchange. The
3
- # BuyOpeningFlow factory function estimates how much you could sell on the other
4
- # exchange and calculates a reasonable price taking into account the remote
5
- # orderbook and the recent operated volume.
2
+ # A workflow for buying bitcoin in Bitex and selling on another exchange. The BuyOpeningFlow factory function estimates how
3
+ # much you could sell on the other exchange and calculates a reasonable price taking into account the remote order book and the
4
+ # recent operated volume.
6
5
  #
7
- # When created, a BuyOpeningFlow places a Bid on Bitex for the calculated amount and
8
- # price, when the Bid is matched on Bitex an OpenBuy is created to sell the
9
- # matched amount for a higher price on the other exchange.
6
+ # When created, a BuyOpeningFlow places a Bid on Bitex for the calculated amount and price, when the Bid is matched on Bitex an
7
+ # OpenBuy is created to sell the matched amount for a higher price on the other exchange.
10
8
  #
11
- # A BuyOpeningFlow can be cancelled at any point, which will cancel the Bitex order
12
- # and any orders on the remote exchange created from its OpenBuy's
13
- #
14
- # @attr order_id The first thing a BuyOpeningFlow does is placing a Bid on Bitex,
15
- # this is its unique id.
9
+ # A BuyOpeningFlow can be cancelled at any point, which will cancel the Bitex order and any orders on the remote exchange
10
+ # created from its OpenBuy's
11
+ #
12
+ # @attr order_id The first thing a BuyOpeningFlow does is placing a Bid on Bitex, this is its unique id.
16
13
  class BuyOpeningFlow < OpeningFlow
17
-
18
- # Start a workflow for buying bitcoin on bitex and selling on the other
19
- # exchange. The amount to be spent on bitex is retrieved from Settings, if
20
- # there is not enough USD on bitex or BTC on the other exchange then no
21
- # order will be placed and an exception will be raised instead.
22
- # The amount a BuyOpeningFlow will try to buy and the price it will try to buy at
23
- # are derived from these parameters:
24
- #
25
- # @param btc_balance [BigDecimal] amount of btc available in the other
26
- # exchange that can be sold to balance this purchase.
27
- # @param order_book [[price, quantity]] a list of lists representing a bid
28
- # order book in the other exchange.
29
- # @param transactions [Hash] a list of hashes representing
30
- # all transactions in the other exchange. Each hash contains 'date', 'tid',
31
- # 'price' and 'amount', where 'amount' is the BTC transacted.
32
- # @param bitex_fee [BigDecimal] the transaction fee to pay on bitex.
33
- # @param other_fee [BigDecimal] the transaction fee to pay on the other
34
- # exchange.
14
+ # Start a workflow for buying bitcoin on bitex and selling on the other exchange. The amount to be spent on bitex is
15
+ # retrieved from Settings, if there is not enough USD on bitex or BTC on the other exchange then no order will be placed
16
+ # and an exception will be raised instead.
17
+ #
18
+ # The amount a BuyOpeningFlow will try to buy and the price it will try to buy at are derived from these parameters:
19
+ #
20
+ # @param btc_balance [BigDecimal] amount of btc available in the other exchange that can be sold to balance this purchase.
21
+ # @param order_book [[price, quantity]] a list of lists representing a bid order book in the other exchange.
22
+ # @param transactions [Hash] a list of hashes representing all transactions in the other exchange:
23
+ # Each hash contains 'date', 'tid', 'price' and 'amount', where 'amount' is the BTC transacted.
24
+ # @param maker_fee [BigDecimal] the transaction fee to pay on maker exchange.
25
+ # @param taker_fee [BigDecimal] the transaction fee to pay on taker exchange.
35
26
  # @param store [Store] An updated config for this robot, mainly to use for profit.
36
27
  #
37
28
  # @return [BuyOpeningFlow] The newly created flow.
38
- # @raise [CannotCreateFlow] If there's any problem creating this flow, for
39
- # example when you run out of USD on bitex or out of BTC on the other
40
- # exchange.
41
- def self.create_for_market(btc_balance, order_book, transactions,
42
- bitex_fee, other_fee, store)
29
+ # @raise [CannotCreateFlow] If there's any problem creating this flow, for example when you run out of USD on bitex or out
30
+ # of BTC on the other exchange.
31
+ def self.create_for_market(btc_balance, order_book, transactions, maker_fee, taker_fee, store)
43
32
  super
44
33
  end
45
-
34
+
35
+ # sync_open_positions helpers
36
+ def self.transaction_order_id(transaction)
37
+ transaction.bid_id
38
+ end
39
+
46
40
  def self.open_position_class
47
41
  OpenBuy
48
42
  end
49
-
43
+ # end: sync_open_positions helpers
44
+
45
+ # sought_transaction helpers
50
46
  def self.transaction_class
51
47
  Bitex::Buy
52
48
  end
53
-
54
- def self.transaction_order_id(transaction)
55
- transaction.bid_id
49
+ # end: sought_transaction helpers
50
+
51
+ # create_for_market helpers
52
+ def self.maker_price(bitcoin_to_resell)
53
+ value_to_use / bitcoin_to_resell * (1 - profit / 100)
56
54
  end
57
55
 
58
56
  def self.order_class
59
57
  Bitex::Bid
60
58
  end
61
-
62
- def self.value_to_use
63
- store.buying_amount_to_spend_per_order || Settings.buying.amount_to_spend_per_order
64
- end
65
-
59
+
66
60
  def self.profit
67
61
  store.buying_profit || Settings.buying.profit
68
62
  end
69
-
70
- def self.get_safest_price(transactions, order_book, dollars_to_use)
71
- OrderBookSimulator.run(Settings.time_to_live, transactions,
72
- order_book, dollars_to_use, nil)
73
- end
74
-
75
- def self.get_remote_value_to_use(value_to_use_needed, safest_price)
63
+
64
+ def self.remote_value_to_use(value_to_use_needed, safest_price)
76
65
  value_to_use_needed / safest_price
77
66
  end
78
67
 
79
- def self.get_bitex_price(usd_to_spend, bitcoin_to_resell)
80
- (usd_to_spend / bitcoin_to_resell) * (1 - self.profit / 100.0)
68
+ def self.safest_price(transactions, order_book, dollars_to_use)
69
+ OrderBookSimulator.run(Settings.time_to_live, transactions, order_book, dollars_to_use, nil)
70
+ end
71
+
72
+ def self.value_to_use
73
+ store.buying_amount_to_spend_per_order || Settings.buying.amount_to_spend_per_order
81
74
  end
75
+ # end: create_for_market helpers
82
76
  end
83
77
  end
@@ -1,7 +1,7 @@
1
1
  module BitexBot
2
- # A CloseBuy represents an Ask on the remote exchange intended
3
- # to close one or several OpenBuy positions.
2
+ # A CloseBuy represents an Ask on the remote exchange intended to close one or several OpenBuy positions.
4
3
  # TODO: document attributes.
4
+ #
5
5
  class CloseBuy < ActiveRecord::Base
6
6
  end
7
7
  end