Skip to content

Commit

Permalink
support market orders
Browse files Browse the repository at this point in the history
  • Loading branch information
robaho committed Oct 3, 2018
1 parent 11d90ef commit 2c0cfe8
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The client offers a command line GUI, "market maker", and a "playback".

The exchange itself has a bare bones web interface, that uses web sockets to provide real-time book updates.

The exchange is designed to allow for easy back-testing of trading strategies.
The exchange is designed to allow for easy back-testing of trading strategies. It supports limit and market orders.

It was primarily developed to further my knowledge of Go and test its suitability for high-performance financial applications.

Expand Down
20 changes: 14 additions & 6 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,16 @@ func processCommand(g *gocui.Gui, v *gocui.View) error {
goto again
}
if "help" == parts[0] {
fmt.Fprintln(v, "The available commands are: quit, {buy:sell} SYMBOL QTY PRICE, modify ORDERID QTY PRICE, cancel ORDERID, book SYMBOL")
fmt.Fprintln(v, "The available commands are: quit, {buy:sell} SYMBOL QTY [PRICE], modify ORDERID QTY PRICE, cancel ORDERID, book SYMBOL")
} else if "quit" == parts[0] {
return gocui.ErrQuit
} else if ("buy" == parts[0] || "sell" == parts[0]) && len(parts) == 4 {
} else if ("buy" == parts[0] || "sell" == parts[0]) && (len(parts) == 4 || len(parts) == 3) {
instrument := IMap.GetBySymbol(parts[1])
if instrument == nil {
fmt.Fprintln(v, "unknown instrument", parts[1])
goto again
}
qty := NewDecimal(parts[2])
price := NewDecimal(parts[3])

var side Side
if "buy" == parts[0] {
Expand All @@ -317,10 +316,19 @@ func processCommand(g *gocui.Gui, v *gocui.View) error {
fmt.Fprintln(v, "incorrect buy/sell type", parts[1])
goto again
}
var err error
if len(parts) == 4 {
price := NewDecimal(parts[3])
order := LimitOrder(instrument, side, price, qty)
_, err = exchange.CreateOrder(order)
} else {
order := MarketOrder(instrument, side, qty)
_, err = exchange.CreateOrder(order)
}
if err != nil {
vlogf("log", "unable to submit order %s\n", err.Error())
}

order := LimitOrder(instrument, side, price, qty)

exchange.CreateOrder(order)
} else if "modify" == parts[0] && len(parts) == 4 {
orderID := NewOrderID(parts[1])
qty := NewDecimal(parts[2])
Expand Down
17 changes: 16 additions & 1 deletion internal/exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ type session struct {
quotes map[Instrument]quotePair
}

var buyMarketPrice = NewDecimal("9999999999999")
var sellMarketPrice = ZERO

// return the "effective price" of an order - so market orders can always be at the top
func (so *sessionOrder) getPrice() decimal.Decimal {
if so.order.OrderType == Market {
if so.order.Side == Buy {
return buyMarketPrice
} else {
return sellMarketPrice
}
}
return so.order.Price
}

func newSession(id string) session {
s := session{}
s.id = id
Expand Down Expand Up @@ -100,7 +115,7 @@ func (e *exchange) CreateOrder(session string, order *Order) (OrderID, error) {
book := ob.buildBook()
sendMarketData(MarketEvent{book, trades})
App.sendExecutionReports(trades)
if len(trades) == 0 {
if len(trades) == 0 || order.OrderState == Cancelled {
App.sendExecutionReport(enum.ExecType_NEW, so)
}

Expand Down
10 changes: 8 additions & 2 deletions internal/exchange/orderbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ func (ob *orderBook) add(so sessionOrder) ([]trade, error) {
// match and build trades
var trades = matchTrades(ob)

// cancel any remaining market order
if so.order.OrderType == Market && so.order.IsActive() {
so.order.OrderState = Cancelled
ob.remove(so)
}

return trades, nil
}

func insertSort(orders []sessionOrder, so sessionOrder, direction int) []sessionOrder {
index := sort.Search(len(orders), func(i int) bool {
cmp := so.order.Price.Cmp(orders[i].order.Price) * direction
cmp := so.getPrice().Cmp(orders[i].getPrice()) * direction
if cmp == 0 {
cmp = CmpTime(so.time, orders[i].time)
}
Expand All @@ -72,7 +78,7 @@ func matchTrades(book *orderBook) []trade {
bid := book.bids[0]
ask := book.asks[0]

if !bid.order.Price.GreaterThanOrEqual(ask.order.Price) {
if !bid.getPrice().GreaterThanOrEqual(ask.getPrice()) {
break
}

Expand Down
19 changes: 12 additions & 7 deletions internal/exchange/qfixapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ func (app *myApplication) onNewOrderSingle(msg newordersingle.NewOrderSingle, se
if err != nil {
return err
}
price, err := msg.GetPrice()
if err != nil {
return err
}
qty, err := msg.GetOrderQty()
if err != nil {
return err
Expand All @@ -80,14 +76,23 @@ func (app *myApplication) onNewOrderSingle(msg newordersingle.NewOrderSingle, se
if err != nil {
return err
}
if ordType != enum.OrdType_LIMIT {
return quickfix.NewMessageRejectError("only limit orders supported", 0, nil)
var price decimal.Decimal
if ordType == enum.OrdType_LIMIT {
price, err = msg.GetPrice()
if err != nil {
return err
}
}
instrument := IMap.GetBySymbol(symbol)
if instrument == nil {
return quickfix.NewMessageRejectError("unknown symbol "+symbol, 0, nil)
}
order := LimitOrder(instrument, MapFromFixSide(side), price, qty)
var order *Order
if ordType == enum.OrdType_LIMIT {
order = LimitOrder(instrument, MapFromFixSide(side), price, qty)
} else {
order = MarketOrder(instrument, MapFromFixSide(side), qty)
}
order.Id = NewOrderID(clOrdId)
app.e.CreateOrder(sessionID.String(), order)

Expand Down
5 changes: 4 additions & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (c *connector) CreateOrder(order *Order) (OrderID, error) {
return -1, NotConnected
}

if order.OrderType != Limit {
if order.OrderType != Limit && order.OrderType != Market {
return -1, UnsupportedOrderType
}

Expand All @@ -129,6 +129,9 @@ func (c *connector) CreateOrder(order *Order) (OrderID, error) {
order.Id = orderID

var ordtype = field.NewOrdType(enum.OrdType_LIMIT)
if order.OrderType == Market {
ordtype = field.NewOrdType(enum.OrdType_MARKET)
}

fixOrder := newordersingle.New(field.NewClOrdID(orderID.String()), field.NewSide(MapToFixSide(order.Side)), field.NewTransactTime(time.Now()), ordtype)
fixOrder.SetSymbol(order.Instrument.Symbol())
Expand Down

0 comments on commit 2c0cfe8

Please sign in to comment.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy