Class: BitexBot::OpeningFlow
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- BitexBot::OpeningFlow
- Extended by:
- Forwardable
- Defined in:
- lib/bitex_bot/models/opening_flow.rb
Overview
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.
Direct Known Subclasses
Statuses collapse
- .active ⇒ Object
- .old_active ⇒ Object
-
#statuses ⇒ Array<String>
All possible flow statuses.
Class Method Summary collapse
- .active_transaction?(transaction, threshold) ⇒ Boolean
-
.calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions) ⇒ Object
create_for_market helpers.
-
.create_for_market(taker_balance, taker_orders, taker_transactions, maker_fee, taker_fee, store) ⇒ Object
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.
-
.create_open_position!(transaction, flow) ⇒ Object
sync_open_positions helpers rubocop:disable Metrics/AbcSize.
- .create_order!(maker_price) ⇒ Object
- .enough_funds?(order) ⇒ Boolean
- .enough_remote_funds?(taker_balance, remote_value) ⇒ Boolean
-
.expected_kind_transaction?(transaction) ⇒ Boolean
sought_transaction helpers.
- .expected_order_book?(maker_transaction) ⇒ Boolean
- .maker_plus(fee) ⇒ Object
- .open_position?(transaction) ⇒ Boolean
-
.sought_transaction?(threshold, transaction) ⇒ Boolean
This use hooks methods, these must be defined in the subclass: #transaction_class.
-
.sync_open_positions ⇒ Object
Buys on bitex represent open positions, we mirror them locally so that we can plan on how to close them.
Instance Method Summary collapse
Class Method Details
.active ⇒ Object
18 19 20 |
# File 'lib/bitex_bot/models/opening_flow.rb', line 18 def self.active where.not(status: :finalised) end |
.active_transaction?(transaction, threshold) ⇒ Boolean
157 158 159 |
# File 'lib/bitex_bot/models/opening_flow.rb', line 157 def self.active_transaction?(transaction, threshold) threshold.present? && transaction. < (threshold - 30.minutes).to_i end |
.calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions) ⇒ Object
create_for_market helpers
80 81 82 83 84 85 86 |
# 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) ⇒ Object
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
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# 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. end |
.create_open_position!(transaction, flow) ⇒ Object
sync_open_positions helpers rubocop:disable Metrics/AbcSize
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# 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) ⇒ Object
88 89 90 91 92 |
# 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. end |
.enough_funds?(order) ⇒ Boolean
94 95 96 |
# 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) ⇒ Boolean
98 99 100 |
# 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) ⇒ Boolean
sought_transaction helpers
153 154 155 |
# 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) ⇒ Boolean
165 166 167 |
# 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) ⇒ Object
102 103 104 |
# File 'lib/bitex_bot/models/opening_flow.rb', line 102 def self.maker_plus(fee) value_to_use * fee / 100 end |
.old_active ⇒ Object
22 23 24 |
# 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) ⇒ Boolean
161 162 163 |
# 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) ⇒ Boolean
This use hooks methods, these must be defined in the subclass:
#transaction_class
144 145 146 147 148 149 |
# 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 ⇒ Object
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
111 112 113 114 115 116 117 118 119 120 121 |
# 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 |
Instance Method Details
#finalise! ⇒ Object
183 184 185 186 |
# 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 |
#statuses ⇒ Array<String>
All possible flow statuses
16 |
# File 'lib/bitex_bot/models/opening_flow.rb', line 16 cattr_accessor(:statuses) { %w[executing settling finalised] } |