NuBotTrading / src / main / java / com / nubits / nubot / models / OrderBook.java

/*
 * Copyright (C) 2015 Nu Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package com.nubits.nubot.models;


import com.nubits.nubot.global.Constant;
import com.nubits.nubot.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;

public class OrderBook {

    private static final Logger LOG = LoggerFactory.getLogger(OrderBook.class.getName());

    CurrencyPair pair;
    String exchangeName;
    ArrayList<Order> orderList = new ArrayList<>();

    public OrderBook() {
    }

    public OrderBook(String exchange, CurrencyPair pair, ArrayList<Order> orderList) {
        this.exchangeName = exchange;
        this.pair = pair;
        this.orderList = orderList;
    }

    public CurrencyPair getPair() {
        return this.pair;
    }

    public void setPair(CurrencyPair pair) {
        this.pair = pair;
    }

    public String getExchange() {
        return exchangeName;
    }

    public void setExchange(String exchange) {
        this.exchangeName = exchange;
    }

    public ArrayList<Order> getOrderBook() {
        return orderList;
    }

    public void setOrderBook(ArrayList<Order> orderList) {
        this.orderList = orderList;
    }

    public ArrayList<Order> getBids(String sort) {
        return getOrdersImpl(Constant.BUY, sort, 0, 0, 0, 0);
    }

    public ArrayList<Order> getAsks() {
        return getAsks(Constant.DESC);
    }

    public ArrayList<Order> getBids() {
        return getBids(Constant.DESC);
    }

    public ArrayList<Order> getAsks(String sort) {
        return getOrdersImpl(Constant.SELL, sort, 0, 0, 0, 0);
    }

    public ArrayList<Order> getBids(String sort, double fromPrice, double toPrice) {
        return getOrdersImpl(Constant.BUY, sort, fromPrice, toPrice, 0, 0);
    }

    public ArrayList<Order> getAsks(String sort, double fromPrice, double toPrice) {
        return getOrdersImpl(Constant.SELL, sort, fromPrice, toPrice, 0, 0);
    }

    public ArrayList<Order> getBids(String sort, int minVolume, int maxVolume) {
        return getOrdersImpl(Constant.BUY, sort, 0, 0, minVolume, maxVolume);
    }

    public ArrayList<Order> getAsks(String sort, int minVolume, int maxVolume) {
        return getOrdersImpl(Constant.SELL, sort, 0, 0, minVolume, maxVolume);
    }

    private ArrayList<Order> getOrdersImpl(String type, String sort, double fromPrice, double toPrice, int minVolume, int maxVolume) {
        ArrayList<Order> orderList = new ArrayList<>();

        //Check the order type
        for (Order order : this.orderList) {
            if (order.getType().equals(type)) {
                orderList.add(order);
            }
        }

        //check for fromPrice
        if (fromPrice > 0) {
            ArrayList<Order> fromPriceOrderList = new ArrayList<>();
            for (Order order : orderList) {
                if (order.getPrice().getQuantity() >= fromPrice) {
                    fromPriceOrderList.add(order);
                }
            }
            orderList = fromPriceOrderList;
        }

        //check for toPrice
        if (toPrice > 0) {
            ArrayList<Order> toPriceList = new ArrayList<>();
            for (Order order : orderList) {
                if (order.getPrice().getQuantity() <= toPrice) {
                    toPriceList.add(order);
                }
            }
            orderList = toPriceList;
        }

        //check for minVolume
        if (minVolume > 0) {
            ArrayList<Order> minVolumeList = new ArrayList<>();
            for (Order order : orderList) {
                if (order.getAmount().getQuantity() >= minVolume) {
                    minVolumeList.add(order);
                }
            }
            orderList = minVolumeList;
        }

        //check for maxVolume
        if (maxVolume > 0) {
            ArrayList<Order> maxVolumeList = new ArrayList<>();
            for (Order order : orderList) {
                if (order.getAmount().getQuantity() <= maxVolume) {
                    maxVolumeList.add(order);
                }
            }
            orderList = maxVolumeList;
        }

        //sort the resultant list by price
        if (sort.equals(Constant.ASC)) {
            Collections.sort(orderList, Order.priceComparatorAsc);
        } else {
            Collections.sort(orderList, Order.priceComparatorDesc);
        }

        return orderList;
    }

    public Order getTopBid() {
        return getTopOrderImpl(Constant.BUY);
    }

    public Order getTopAsk() {
        return getTopOrderImpl(Constant.SELL);
    }

    private Order getTopOrderImpl(String type) {
        Order topOrder = null;
        double topPrice = 0;
        double lowestPrice = 999999999;

        for (Order order : this.orderList) {
            if (type.equals(Constant.BUY)) {
                if (order.getPrice().getQuantity() > topPrice && order.getType().equals(type)) {
                    topPrice = order.getPrice().getQuantity(); //highest bid
                    topOrder = order;
                }
            } else {
                if (order.getPrice().getQuantity() < lowestPrice && order.getType().equals(type)) { //lowest ask
                    lowestPrice = order.getPrice().getQuantity();
                    topOrder = order;
                }
            }
        }
        if (topOrder == null)
            LOG.warn("Requested topOrder from OrderBook : no open orders found on " + type + " side.");
        return topOrder;
    }

    public double getTotalLiquidity(String type) {
        return gettotalLiquidityImpl(type, 0, 0);
    }

    public double getTotalLiquidity(String type, double toPrice) {
        return gettotalLiquidityImpl(type, toPrice, 0);
    }

    public double getTotalLiquidity(String type, double toPrice, double fromPrice) {
        return gettotalLiquidityImpl(type, toPrice, fromPrice);
    }

    private double gettotalLiquidityImpl(String type, double toPrice, double fromPrice) {
        double totalLiquidity = 0;
        ArrayList<Order> totalLiquidityList = new ArrayList<>();

        //retrieve orders of the correct type
        for (Order order : this.orderList) {
            if (order.getType().equals(type)) {
                totalLiquidityList.add(order);
            }
        }

        //filter by toPrice
        if (toPrice > 0) {
            ArrayList<Order> toPriceList = new ArrayList<>();
            for (Order order : totalLiquidityList) {
                if (order.getPrice().getQuantity() <= toPrice) {
                    toPriceList.add(order);
                }
            }
            totalLiquidityList = toPriceList;
        }

        //filter by fromPrice
        if (fromPrice > 0) {
            ArrayList<Order> fromPriceList = new ArrayList<>();
            for (Order order : totalLiquidityList) {
                if (order.getPrice().getQuantity() >= fromPrice) {
                    fromPriceList.add(order);
                }
            }
            totalLiquidityList = fromPriceList;
        }

        //calculate the total liquidity from the filtered list
        for (Order order : totalLiquidityList) {
            totalLiquidity += order.getAmount().getQuantity();
        }

        return totalLiquidity;
    }

    public ArrayList<Order> sortByPrice(String sort) {
        //sort the list by price
        if (sort.equals(Constant.ASC)) {
            Collections.sort(this.orderList, Order.priceComparatorAsc);
        } else {
            Collections.sort(this.orderList, Order.priceComparatorDesc);
        }

        return this.orderList;
    }

    public ArrayList<Order> sortByVolume(String sort) {
        //sort the list by volume
        if (sort.equals(Constant.ASC)) {
            Collections.sort(this.orderList, Order.volumeComparatorAsc);
        } else {
            Collections.sort(this.orderList, Order.volumeComparatorDesc);
        }

        return this.orderList;
    }

    public ArrayList<Order> sortByDate(String sort) {
        //sort the list by date
        if (sort.equals(Constant.ASC)) {
            Collections.sort(this.orderList, Order.dateComparatorAsc);
        } else {
            Collections.sort(this.orderList, Order.dateComparatorDesc);
        }

        return this.orderList;
    }

    public void draw() {
        Utils.drawOrderBooks(this.getAsks(), this.getBids());
    }

    @Override
    public String toString() {
        String toReturn = "OrderBook of " + exchangeName + ":" + pair.toStringSep() + "\n";
        toReturn += "#bids: " + this.getBids().size() + " ; #asks:" + this.getAsks().size() + "\n";
        toReturn += "total bids volume: " + this.getTotalLiquidity(Constant.BUY) + " " + pair.getPaymentCurrency().getCode() + " ; total asks volume:" + this.getTotalLiquidity(Constant.SELL) + " " + pair.getPaymentCurrency().getCode() + "\n";
        toReturn += "Asks : \n" + this.getOpenOrdersStr(Constant.SELL) + "\n";
        toReturn += "Bids : \n" + this.getOpenOrdersStr(Constant.BUY) + "\n";

        return toReturn;
    }

    private String getOpenOrdersStr(String type) {
        String toReturn = "";
        ArrayList<Order> orders;
        if (type.equals(Constant.BUY)) {
            orders = this.getBids();
        } else {
            orders = this.getAsks();
        }

        for (int i = 0; i < orders.size(); i++) {
            toReturn += orders.get(i).toString() + "\n";
        }
        return toReturn;
    }
}