class BitexBot::OpeningFlow
Any arbitrage workflow has 2 stages, opening positions and then closing them. The OpeningFlow
stage places an order on bitex, detecting and storing all transactions spawn from that order as Open positions.
Public Class Methods
active()
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 18 def self.active where.not(status: :finalised) end
active_transaction?(transaction, threshold)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 157 def self.active_transaction?(transaction, threshold) threshold.present? && transaction.timestamp < (threshold - 30.minutes).to_i end
calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions)
click to toggle source
create_for_market
helpers
# File lib/bitex_bot/models/opening_flow.rb, line 80 def self.calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions) value_to_use_needed = (value_to_use + maker_plus(maker_fee)) / (1 - taker_fee / 100) safest_price = safest_price(taker_transactions, taker_orders, value_to_use_needed) remote_value = remote_value_to_use(value_to_use_needed, safest_price) [remote_value, safest_price] end
create_for_market(taker_balance, taker_orders, taker_transactions, maker_fee, taker_fee, store)
click to toggle source
This use hooks methods, these must be defined in the subclass:
#maker_price #order_class #remote_value_to_use #safest_price #value_to_use
rubocop:disable Metrics/AbcSize
# File lib/bitex_bot/models/opening_flow.rb, line 34 def self.create_for_market(taker_balance, taker_orders, taker_transactions, maker_fee, taker_fee, store) self.store = store remote_value, safest_price = calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions) Robot.log( :info, "Opening: Need #{taker_specie_to_spend} #{remote_value.truncate(8)} on #{Robot.taker.name} taker,"\ " has #{taker_balance.truncate(8)}." ) unless enough_remote_funds?(taker_balance, remote_value) raise CannotCreateFlow, "Needed #{remote_value} but you only have #{taker_specie_to_spend} #{taker_balance} on your taker market." end price = maker_price(remote_value) order = create_order!(price) unless enough_funds?(order) raise CannotCreateFlow, "Needed #{maker_specie_to_spend} #{value_per_order} on #{Robot.maker.name} maker to place this #{order_class}"\ " but you only have #{maker_specie_to_spend} #{available_maker_balance}." end flow = create!( price: price, value_to_use: value_to_use, suggested_closing_price: safest_price, status: 'executing', order_id: order.id ) Robot.log( :info, "Opening: Placed #{order_class} ##{order.id} #{maker_specie_to_spend} #{value_per_order} @ #{price.truncate(2)}."\ " (#{maker_specie_to_obtain} #{remote_value})."\ " #{name.demodulize}##{flow.id} suggests closing price #{Robot.taker.quote.upcase}"\ " #{flow.suggested_closing_price}." ) flow rescue StandardError => e raise CannotCreateFlow, e.message end
create_open_position!(transaction, flow)
click to toggle source
sync_open_positions
helpers rubocop:disable Metrics/AbcSize
# File lib/bitex_bot/models/opening_flow.rb, line 125 def self.create_open_position!(transaction, flow) Robot.log( :info, "Opening: #{self} ##{flow.id} was hit for #{Robot.maker.base.upcase} #{transaction.raw.quantity}"\ " @ #{Robot.maker.quote.upcase} #{transaction.price}. Creating #{open_position_class}..." ) open_position_class.create!( transaction_id: transaction.id, price: transaction.price, amount: transaction.amount, quantity: transaction.raw.quantity, opening_flow: flow ) end
create_order!(maker_price)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 88 def self.create_order!(maker_price) Robot.maker.send_order(order_type, maker_price, value_per_order, true) rescue StandardError => e raise CannotCreateFlow, e.message end
enough_funds?(order)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 94 def self.enough_funds?(order) !order.reason.to_s.inquiry.not_enough_funds? end
enough_remote_funds?(taker_balance, remote_value)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 98 def self.enough_remote_funds?(taker_balance, remote_value) taker_balance >= remote_value end
expected_kind_transaction?(transaction)
click to toggle source
sought_transaction helpers
# File lib/bitex_bot/models/opening_flow.rb, line 153 def self.expected_kind_transaction?(transaction) transaction.raw.is_a?(transaction_class) end
expected_order_book?(maker_transaction)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 165 def self.expected_order_book?(maker_transaction) maker_transaction.raw.order_book.to_s == Robot.maker.base_quote end
maker_plus(fee)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 102 def self.maker_plus(fee) value_to_use * fee / 100 end
old_active()
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 22 def self.old_active active.where('created_at < ?', Settings.time_to_live.seconds.ago) end
open_position?(transaction)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 161 def self.open_position?(transaction) open_position_class.find_by_transaction_id(transaction.id) end
sought_transaction?(threshold, transaction)
click to toggle source
This use hooks methods, these must be defined in the subclass:
#transaction_class
# File lib/bitex_bot/models/opening_flow.rb, line 144 def self.sought_transaction?(threshold, transaction) expected_kind_transaction?(transaction) && !active_transaction?(transaction, threshold) && !open_position?(transaction) && expected_order_book?(transaction) end
sync_open_positions()
click to toggle source
Buys on bitex represent open positions, we mirror them locally so that we can plan on how to close them. This use hooks methods, these must be defined in the subclass:
#transaction_order_id(transaction) #open_position_class
# File lib/bitex_bot/models/opening_flow.rb, line 111 def self.sync_open_positions threshold = open_position_class.order('created_at DESC').first.try(:created_at) Robot.maker.transactions.map do |transaction| next unless sought_transaction?(threshold, transaction) flow = find_by_order_id(transaction_order_id(transaction)) next unless flow.present? create_open_position!(transaction, flow) end.compact end
Public Instance Methods
finalise!()
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 183 def finalise! order = order_class.find(order_id) canceled_or_completed?(order) ? do_finalize : do_cancel(order) end
Private Instance Methods
canceled_or_completed?(order)
click to toggle source
finalise! helpers
# File lib/bitex_bot/models/opening_flow.rb, line 191 def canceled_or_completed?(order) %i[cancelled completed].any? { |status| order.status == status } end
do_cancel(order)
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 200 def do_cancel(order) Robot.log(:info, "Opening: #{order_class} ##{order_id} canceled.") order.cancel! settling! unless settling? end
do_finalize()
click to toggle source
# File lib/bitex_bot/models/opening_flow.rb, line 195 def do_finalize Robot.log(:info, "Opening: #{order_class} ##{order_id} finalised.") finalised! end