func (engine *RBTEngine) removeNodeOrder(book *rbt.Tree, node *TreeNode, order *Order) {
	delete(node.orders, order.OrderId)
	if len(node.orders) == 0 {
		book.Remove(order.Price)
	}
}
TODO: linked list?
TODO: sort after add?
func (n TreeNode) sortedOrders() []*Order {
log.Debug("sortedOrders")
orders := make([]*Order, 0)
for _, v := range n.orders {
orders = append(orders, v)
}
sort.Slice(orders[:], func(i, j int) bool {
return orders[i].Timestamp < orders[j].Timestamp
})
return orders
}
func (n *TreeNode) delete(id uint64) {
package main
import (
	"os"
	"os/signal"
	"time"
	"github.com/Shopify/sarama"
	jsoniter "github.com/json-iterator/go"
	log "github.com/sirupsen/logrus"
	rbt "github.com/emirpasic/gods/trees/redblacktree"
	"github.com/emirpasic/gods/utils"
)
func init() {
	log.SetFormatter(&log.TextFormatter{
		TimestampFormat: "2006/01/02 - 15:04:05",
		FullTimestamp:   true,
	})
	log.SetLevel(log.InfoLevel)
}
type RBTEngine struct {
	pair    string
	askbook *rbt.Tree
	bidbook *rbt.Tree
}
func NewRBTEngine(pair string) RBTEngine {
	return RBTEngine{
		pair: pair,
		askbook: rbt.NewWith(utils.Float64Comparator),
		bidbook: rbt.NewWith(utils.Float64Comparator),
	}
}
func (engine *RBTEngine) AddAskOrderBook(order *Order) {
	treeNode, ok := engine.askbook.Get(order.Price)
	if !ok {
		node := NewTreeNode()
		node.upsert(order)
		engine.askbook.Put(order.Price, node)
	} else {
		node := treeNode.(*TreeNode)
		node.upsert(order)
		engine.askbook.Put(order.Price, treeNode)
	}
}
func (engine *RBTEngine) addOrder(book *rbt.Tree, order *Order) {
	treeNode, ok := book.Get(order.Price)
	if !ok {
		node := NewTreeNode()
		node.upsert(order)
		book.Put(order.Price, node)
	} else {
		node := treeNode.(*TreeNode)
		node.upsert(order)
		book.Put(order.Price, treeNode)
	}
}
func (engine *RBTEngine) addAskOrder(sellbook *rbt.Tree, order *Order) {
	addOrder(sellbook, order)
}
func (engine *RBTEngine) addBidOrder(buybook *rbt.Tree, order *Order) {
	addOrder(buybook, order)
}
func (engine *RBTEngine) Run(order *Order) {
	switch order.OrderAction {
	case "SELL":
		log.Debug("*Run ASK for Highest price ", order.Price, order)
		matchingOrder, matches := engine.matchAskOrder(order)
		log.Debug("*Run ASK matches ", len(matches), matches)
		if matchingOrder.Amount > 0 {
			addAskOrder(engine.askbook, order)
		}
	case "BUY":
		log.Debug("*Run BID for Lowest price ", order.Price, order)
		matchingOrder, matches := engine.matchBidOrder(order)
		log.Debug("*Run BID matches ", len(matches), matches)
		if matchingOrder.Amount > 0 {
			addBidOrder(engine.bidbook, order)
		}
	default:
		log.Info("Invalid Order Type")
	}
}
func (engine *RBTEngine) matchBidOrder(askOrder *Order) (*Order, []Match) {
	log.Debug("matchBidOrder")
	return engine.matchAskBook(askOrder)
}
func (engine *RBTEngine) matchAskBook(bidOrder *Order) (*Order, []Match) {
	log.Debug("matchAskBook askbook.Keys() ", engine.askbook.Keys())
	var matches []Match
	askbookit := engine.askbook.Iterator()
	for askbookit.Begin(); askbookit.Next(); {
		askBookNodePrice, askBookNode := askbookit.Key().(float64), askbookit.Value().(*TreeNode)
		log.Debug("matchAskBook bidOrder    ", bidOrder.Price, bidOrder)
		log.Debug("matchAskBook aksBookNode ", askBookNodePrice, askBookNode)
		if bidOrder.Price >= askBookNodePrice {
			log.Debug("matchAskBook bidOrder.Price >= askBookNodePrice ", bidOrder.Price, askBookNodePrice)
			_, nodeMatches := engine.matchBookNode(engine.askbook, askBookNode, bidOrder)
			log.Debug("matchAskBook nodeMatches ", len(nodeMatches), nodeMatches)
			for _, match := range nodeMatches {
				matches = append(matches, match)
			}
		} else {
			log.Debug("matchAskBook bidOrder.Price < askBookNodePrice ", bidOrder.Price, askBookNodePrice)
			break
		}
		if bidOrder.Amount == 0 {
			break
		}
	}
	return bidOrder, matches
}
func (engine *RBTEngine) matchAskOrder(askOrder *Order) (*Order, []Match) {
	log.Debug("matchAskOrder")
	return engine.matchBidBook(askOrder)
}
func (engine *RBTEngine) matchBidBook(askOrder *Order) (*Order, []Match) {
	log.Debug("matchBidBook bidbook.Keys() ", engine.bidbook.Keys())
	var matches []Match
	bidbookit := engine.bidbook.Iterator()
	for bidbookit.End(); bidbookit.Prev(); {
		bidBookNodePrice, bidBookNode := bidbookit.Key().(float64), bidbookit.Value().(*TreeNode)
		log.Debug("matchBidBook askOrder    ", askOrder.Price, askOrder)
		log.Debug("matchBidBook bidBookNode ", bidBookNodePrice, bidBookNode)
		if bidBookNodePrice >= askOrder.Price {
			log.Debug("bidBookNodePrice >= matchBidBook askOrder.Price ", bidBookNodePrice, askOrder.Price)
			_, nodeMatches := engine.matchBookNode(engine.bidbook, bidBookNode, askOrder)
			log.Debug("matchBidBook nodeMatches ", len(nodeMatches), nodeMatches)
			for _, match := range nodeMatches {
				matches = append(matches, match)
			}
		} else {
			log.Debug("matchBidBook askOrder.Price > bidBookNodePrice ", askOrder.Price, bidBookNodePrice)
			break
		}
		if askOrder.Amount == 0 {
			break
		}
	}
	return askOrder, matches
}
func (engine *RBTEngine) matchBookNode(book *rbt.Tree, node *TreeNode, matchingOrder *Order) (*Order, []Match) {
	log.Debug("matchBookNode")
	var nodeMatches []Match
	for _, nodeOrder := range node.orders {
		if nodeOrder.Amount >= matchingOrder.Amount {
			log.Debug("matchBookNode nodeOrder.Amount > matchingOrder.Amount ", nodeOrder.Amount, matchingOrder.Amount)
			nodeMatch := NewMatch(*matchingOrder, *nodeOrder, matchingOrder.Amount, nodeOrder.Price)
			log.Debug("matchBookNode nodeMatch ", nodeMatch)
			nodeMatches = append(nodeMatches, nodeMatch)
			nodeOrder.Amount = nodeOrder.Amount - matchingOrder.Amount
			if nodeOrder.Amount == 0 {
				engine.removeNodeOrder(book, node, nodeOrder)
			}
			matchingOrder.Amount = matchingOrder.Amount - matchingOrder.Amount  
			break                                                               
		} else if nodeOrder.Amount < matchingOrder.Amount {
			log.Debug("matchBookNode nodeOrder.Amount < matchingOrder.Amount ", nodeOrder.Amount, matchingOrder.Amount)
			nodeMatch := NewMatch(*matchingOrder, *nodeOrder, nodeOrder.Amount, nodeOrder.Price)
			log.Debug("matchBookNode nodeMatch ", nodeMatch)
			nodeMatches = append(nodeMatches, nodeMatch)
			matchingOrder.Amount = matchingOrder.Amount - nodeOrder.Amount
			nodeOrder.Amount = nodeOrder.Amount - nodeOrder.Amount  
			engine.removeNodeOrder(book, node, nodeOrder)           
		}
	}
	return matchingOrder, nodeMatches
}