def test_transfer(): exchange = mock.Mock() price = Decimal(9750.19).quantize(Decimal(10)**-2) exchange.quote_price = lambda pair: price exchange.name = "bitfinex" order = mock.Mock() order.path_id = "fake_id" exchange_pair = ExchangePair(exchange, USD / BTC) source = Wallet(exchange, 18903.89 * USD) source.lock(917.07 * USD, order, "test") target = Wallet(exchange, 3.79283997 * BTC) quantity = (256.19 * USD).lock_for("fake_id") commission = (2.99 * USD).lock_for("fake_id") Wallet.transfer(source, target, quantity, commission, exchange_pair, "transfer") source = Wallet(exchange, 3.79283997 * BTC) source.lock(3.00000029 * BTC, order, "test") target = Wallet(exchange, 18903.89 * USD) quantity = (2.19935873 * BTC).lock_for("fake_id") commission = (0.00659732 * BTC).lock_for("fake_id") Wallet.transfer(source, target, quantity, commission, exchange_pair, "transfer")
def execute_sell_order(order: 'Order', base_wallet: 'Wallet', quote_wallet: 'Wallet', current_price: float, options: 'ExchangeOptions', clock: 'Clock') -> 'Trade': """Executes a sell order on the exchange. Parameters ---------- order : `Order` The order that is being filled. base_wallet : `Wallet` The wallet of the base instrument. quote_wallet : `Wallet` The wallet of the quote instrument. current_price : float The current price of the exchange pair. options : `ExchangeOptions` The exchange options. clock : `Clock` The clock for the trading process.. Returns ------- `Trade` The executed trade that was made. """ if order.type == TradeType.LIMIT and order.price > current_price: return None filled = order.remaining.contain(order.exchange_pair) commission = options.commission * filled quantity = filled - commission if commission.size < Decimal(10)**-quantity.instrument.precision: logging.warning( "Commission is less than instrument precision. Canceling order. " "Consider defining a custom instrument with a higher precision.") order.cancel("COMMISSION IS LESS THAN PRECISION.") return None # Transfer Funds from Quote Wallet to Base Wallet transfer = Wallet.transfer(source=quote_wallet, target=base_wallet, quantity=quantity, commission=commission, exchange_pair=order.exchange_pair, reason="SELL") trade = Trade(order_id=order.id, step=clock.step, exchange_pair=order.exchange_pair, side=TradeSide.SELL, trade_type=order.type, quantity=transfer.quantity, price=transfer.price, commission=transfer.commission) return trade
def execute_buy_order(order: 'Order', base_wallet: 'Wallet', quote_wallet: 'Wallet', current_price: float, options: 'ExchangeOptions', clock: 'Clock') -> 'Trade': """Executes a buy order on the exchange. Parameters ---------- order : `Order` The order that is being filled. base_wallet : `Wallet` The wallet of the base instrument. quote_wallet : `Wallet` The wallet of the quote instrument. current_price : float The current price of the exchange pair. options : `ExchangeOptions` The exchange options. clock : `Clock` The clock for the trading process.. Returns ------- `Trade` The executed trade that was made. """ if order.type == TradeType.LIMIT and order.price < current_price: return None filled = order.remaining.contain(order.exchange_pair) if order.type == TradeType.MARKET: scale = order.price / max(current_price, order.price) filled = scale * filled commission = options.commission * filled quantity = filled - commission if commission.size < Decimal(10)**-quantity.instrument.precision: logging.warning( "Commission is less than instrument precision. Canceling order. " "Consider defining a custom instrument with a higher precision." ) order.cancel("COMMISSION IS LESS THAN PRECISION.") return None transfer = Wallet.transfer(source=base_wallet, target=quote_wallet, quantity=quantity, commission=commission, exchange_pair=order.exchange_pair, reason="BUY") trade = Trade(order_id=order.id, step=clock.step, exchange_pair=order.exchange_pair, side=TradeSide.BUY, trade_type=order.type, quantity=transfer.quantity, price=transfer.price, commission=transfer.commission) # n_order = self.check_all_open_order(SIDE_BUY) # if n_order>0: print(' ======>> There are more than {} existing buy orders, not placing further order'.format(n_order)) if self.isLive: order = self.client.create_order(symbol=order.exchange_pair, side=SIDE_BUY, type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, quantity=transfer.quantity, price=transfer.price) print(' =============== Placing buy order ============== \n', order) else: order = self.client.create_test_order( symbol=order.exchange_pair, side=SIDE_BUY, type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, quantity=transfer.quantity, price=transfer.price) print(' =============== Placing test buy order ============== \n', order) return trade
def execute_sell_order(order: 'Order', base_wallet: 'Wallet', quote_wallet: 'Wallet', current_price: float, options: 'ExchangeOptions', clock: 'Clock') -> 'Trade': """Executes a sell order on the exchange. Parameters ---------- order : `Order` The order that is being filled. base_wallet : `Wallet` The wallet of the base instrument. quote_wallet : `Wallet` The wallet of the quote instrument. current_price : float The current price of the exchange pair. options : `ExchangeOptions` The exchange options. clock : `Clock` The clock for the trading process.. Returns ------- `Trade` The executed trade that was made. """ if order.type == TradeType.LIMIT and order.price < current_price: return None filled = order.remaining.contain(order.exchange_pair) if order.type == TradeType.MARKET: scale = order.price / max(current_price, order.price) filled = scale * filled commission = options.commission * filled quantity = filled - commission if commission.size < Decimal(10)**-quantity.instrument.precision: logging.warning( "Commission is less than instrument precision. Canceling order. " "Consider defining a custom instrument with a higher precision.") order.cancel("COMMISSION IS LESS THAN PRECISION.") return None # Todo: Fill Order with Position-Data got by Broker if quantity.instrument == order.exchange_pair.pair.base: instrument = order.exchange_pair.pair.quote converted_size = quantity.size / order.price else: instrument = order.exchange_pair.pair.base converted_size = quantity.size * order.price converted = Quantity(instrument, converted_size, quantity.path_id).quantize() position = Position( id=order.id, # to be replaced by borker-id order=order, quantity=converted, ) quote_wallet.add_position(position) transfer = Wallet.transfer(source=base_wallet, target=quote_wallet, quantity=quantity, commission=commission, exchange_pair=order.exchange_pair, reason="SELL") trade = Trade(order_id=order.id, step=clock.step, exchange_pair=order.exchange_pair, side=TradeSide.SELL, trade_type=order.type, quantity=transfer.quantity, price=transfer.price, commission=transfer.commission) return trade
def execute_buy_order(order: 'Order', base_wallet: 'Wallet', quote_wallet: 'Wallet', current_price: float, options: 'ExchangeOptions', clock: 'Clock') -> 'Trade': """Executes a buy order on the exchange. Parameters ---------- order : `Order` The order that is being filled. base_wallet : `Wallet` The wallet of the base instrument. quote_wallet : `Wallet` The wallet of the quote instrument. current_price : float The current price of the exchange pair. options : `ExchangeOptions` The exchange options. clock : `Clock` The clock for the trading process.. Returns ------- `Trade` The executed trade that was made. """ if order.type == TradeType.LIMIT and order.price < current_price: return None filled = order.remaining.contain(order.exchange_pair) if order.type == TradeType.MARKET: scale = order.price / max(current_price, order.price) filled = scale * filled commission = options.commission * filled quantity = filled - commission if commission.size < Decimal(10)**-quantity.instrument.precision: order.cancel("COMMISSION IS LESS THAN PRECISION.") return None transfer = Wallet.transfer( source=base_wallet, target=quote_wallet, quantity=quantity, commission=commission, exchange_pair=order.exchange_pair, reason="BUY" ) trade = Trade( order_id=order.id, step=clock.step, exchange_pair=order.exchange_pair, side=TradeSide.BUY, trade_type=order.type, quantity=transfer.quantity, price=transfer.price, commission=transfer.commission ) return trade
def get_orders(self, action: int, portfolio: 'Portfolio') -> 'List[Order]': # HOLD Action if action == 0: return [] #(ep, (criteria, proportion, duration, side)) = self.actions[action] #ep = self.actions[action][0] #(criteria, proportion, duration, side) = self.actions[action][1] ep, side = self.actions[action] # Empty Orders List orders = [] # check different action if (action != self.action): # proportion of used balance proportion = 1 duration = None criteria = None # get base/quote wallets quote_wallet = portfolio.get_wallet(ep.exchange.id, instrument=ep.pair.quote) base_wallet = portfolio.get_wallet(ep.exchange.id, instrument=ep.pair.base) # get current price price = ep.price # create orders to close all already open-position positions = quote_wallet.get_positions().values( ) # avoid RuntimeError: dictionary changed size during iteration, when remove position from dict. for position in positions: if position.is_open: #close positions based on last opened orders #quantity = Quantity(instrument=ep.pair.quote, size=position.quantity.size) # adjut worth quantity - if sell-position already open quantity = Quantity(instrument=ep.pair.quote, size=position.get_worth_quantity()) position.is_locked = True order = Order(path_id=position.id, step=self.clock.step, side=TradeSide.CLOSE, trade_type=self._trade_type, exchange_pair=ep, price=ep.price, quantity=quantity, criteria=criteria, end=self.clock.step + duration if duration else None, portfolio=portfolio) # Transfer Funds from Quote Wallet to Base Wallet transfer = Wallet.transfer( source=quote_wallet, target=base_wallet, quantity=order.quantity, commission=Quantity(instrument=ep.pair.quote, size=0), exchange_pair=order.exchange_pair, reason="CLOSE") self.broker.submit(order) self.broker.update() if side != TradeSide.CLOSE: # return money to base-wallets instrument = side.instrument(ep.pair) balance = base_wallet.balance.as_float() size = (balance * proportion) size = min(balance, size) quantity = (size * instrument).quantize() value = size * float(price) if size < 10 ** -instrument.precision \ or value < self.min_order_pct*portfolio.net_worth: return [] order = Order(step=self.clock.step, side=side, trade_type=self._trade_type, exchange_pair=ep, price=ep.price, quantity=quantity, criteria=criteria, end=self.clock.step + duration if duration else None, portfolio=portfolio) orders.append(order) if self._order_listener is not None: order.attach(self._order_listener) # TODO: Check a way to check: check only when order is completed self.action = action return orders