Content-Length: 439295 | pFad | http://github.com/robaho/go-trader/commit/2c0cfe807afd2de4da3a0d58779d2ab4464bb471

0B support market orders · robaho/go-trader@2c0cfe8 · GitHub
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
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
@@ -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.

20 changes: 14 additions & 6 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
@@ -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] {
@@ -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])
17 changes: 16 additions & 1 deletion internal/exchange/exchange.go
Original file line number Diff line number Diff line change
@@ -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
@@ -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)
}

10 changes: 8 additions & 2 deletions internal/exchange/orderbook.go
Original file line number Diff line number Diff line change
@@ -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)
}
@@ -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
}

19 changes: 12 additions & 7 deletions internal/exchange/qfixapp.go
Original file line number Diff line number Diff line change
@@ -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
@@ -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)

5 changes: 4 additions & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
@@ -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
}

@@ -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())

0 comments on commit 2c0cfe8

Please sign in to comment.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/robaho/go-trader/commit/2c0cfe807afd2de4da3a0d58779d2ab4464bb471

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy