/// <summary> /// Adds an order for the given contract and market side with the given properties /// </summary> /// <returns>The added order</returns> public IOrder AddOrder(long orderID, Contract contract, OrderType orderType, MarketSide marketSide, decimal price, decimal quantity, string clOrdID, TradingAccount account) { var order = new Order(orderID, orderType, contract, marketSide, price, quantity, clOrdID, account); var stack = _market.GetOrCreate( contract, () => { var os = OrderStackFactory.CreateStandardSortedStack(_orderMatcher); os.OrdersMatched += OnOrdersMatched; return os; }); stack.AddOrder(order); return order; }
public Message CreateNewOrderSingleMessage(string symbol, MarketSide marketSide, string clOrdID, TradingAccount account, decimal price, decimal quantity, OrderType orderType, string execID) { var fOrdType = TranslateFixFields.Translate(orderType); var fSide = TranslateFixFields.Translate(marketSide); var fSymbol = new Symbol(symbol); var fTransactTime = new TransactTime(DateTime.Now); var fClOrdID = new ClOrdID(clOrdID); var nos = new NewOrderSingle(fClOrdID, fSymbol, fSide, fTransactTime, fOrdType) { OrderQty = new OrderQty(quantity), TimeInForce = new TimeInForce(TimeInForce.GOOD_TILL_CANCEL) }; if (orderType == OrderType.Limit) nos.Price = new Price(price); return nos; }
public IcebergOrder(IServerFacade serverFacade, string symbol, string clOrdID, MarketSide side, decimal totalQuantity, decimal clipSize, decimal initialPrice, decimal priceDelta) { if (totalQuantity <= 0) throw new ApplicationException( "Iceberg Order must have a quantity greater than zero"); if (clipSize > totalQuantity) throw new ApplicationException( "Iceberg Order total quantity must be greater or equal to the clip size"); Symbol = symbol; ClOrdID = clOrdID; Side = side; TotalQuantity = totalQuantity; ClipSize = clipSize; InitialPrice = initialPrice; PriceDelta = priceDelta; State = ActivationState.Suspended; _serverFacade = serverFacade; RemainingQuantity = totalQuantity; CurrentPrice = InitialPrice; CurrentQuantity = clipSize; }
/// <summary> /// Adds an order to the system with the given properties /// </summary> /// <param name="sessionID">The FIX Session ID</param> /// <param name="orderType"> /// The type of order, currently only Limit orders are supported /// </param> /// <param name="symbol">The symbol for the order</param> /// <param name="marketSide">The side of the market for the order</param> /// <param name="clOrdID">The FIX ClOrdID for the order, set by the client</param> /// <param name="account">The trading account associated with the order</param> /// <param name="quantity">The quantity of the order</param> /// <param name="price">The price of the order, may be null for market orders</param> /// <returns>The new order after it has been added</returns> /// <exception cref="FixATServerException">If the order is rejected</exception> public IOrder AddOrder(FixSessionID sessionID, OrderType orderType, string symbol, MarketSide marketSide, string clOrdID, TradingAccount account, decimal quantity, decimal? price = null) { // A more complete system would look the contract up in a contract store var contract = new Contract(symbol); // TODO Replace this with a better mechanism (esp if more order types are supported) decimal orderPrice; switch (orderType) { case OrderType.Limit: { if (!price.HasValue) throw new FixATServerException("Limit order must specify a price"); orderPrice = price.Value; break; } // Uncomment this if and when market orders are supported //case OrderType.Market: // { // if (price.HasValue) // throw new FixATServerException( // "Market order should not have a specified price"); // orderPrice = GetMarketPrice(contract, marketSide); // break; // } default: throw new FixATServerException( string.Format("Order Type {0} not supported", orderType)); } if (OrderWouldLeadToACrossedMarket(marketSide, contract, orderPrice)) throw new FixATServerException("Order would lead to a crossed market"); var order = _orderRepository.AddOrder(CreateOrderID(), contract, orderType, marketSide, orderPrice, quantity, clOrdID, account); _orderOwners[order.ID] = sessionID; return order; }
private bool CanTradeOrder(object o, MarketSide side) { var vm = o as OrderBookViewModel; var selectedOrderRow = LvOrders.SelectedItem; var osr = selectedOrderRow as OrderBookViewModel.OrderStackRow; if (vm == null || osr == null) return false; return !string.IsNullOrWhiteSpace( side == MarketSide.Bid ? osr.BidClOrdID : osr.AskClOrdID); }
private void TradeOrder(object o, MarketSide side) { var vm = o as OrderBookViewModel; var selectedOrderRow = LvOrders.SelectedItem; var osr = selectedOrderRow as OrderBookViewModel.OrderStackRow; if (vm == null || osr == null) return; if (side == MarketSide.Bid) vm.BuyOrder(osr); else vm.SellOrder(osr); }
public bool CancelOrder(string symbol, string clOrdID, MarketSide side, string orderID) { var msg = _fixMessageGenerator.CreateOrderCancelMessage(symbol, clOrdID, GenerateOrderCancelClOrdID(clOrdID), side, orderID); return _app.Send(msg); }
public void CreateIcebergOrder(string symbol, string clOrdID, MarketSide side, decimal totalQuantity, decimal clipSize, decimal initialPrice, decimal priceDelta) { _orderMediator.AddIcebergOrder(symbol, clOrdID, side, totalQuantity, clipSize, initialPrice, priceDelta); }
public Order(long id, OrderType orderType, Contract contract, MarketSide marketSide, decimal price, decimal quantity, string clOrdID, TradingAccount tradingAccount) { ID = id; OrderType = orderType; Contract = contract; MarketSide = marketSide; Price = price; Quantity = quantity; LastUpdateTime = DateTime.UtcNow; ClOrdID = clOrdID; Account = tradingAccount; OriginalQuantity = quantity; }
public void AddIcebergOrder(string symbol, string clOrdID, MarketSide side, decimal totalQuantity, decimal clipSize, decimal initialPrice, decimal priceDelta) { var io = new IcebergOrder(_serverFacade, symbol, clOrdID, side, totalQuantity, clipSize, initialPrice, priceDelta); _atOrderRepository.IcebergOrders.Add(io); OnIcebergOrderAdded(io); }
public OrderList(string symbol, MarketSide side) { Symbol = symbol; Side = side; }
public async Task <string> PlaceOrderAsync(string symbol, decimal price, decimal size, MarketSide side) { var orderId = Guid.NewGuid().ToString(); _orders.Add(orderId, size); return(orderId); }
private bool OrderWouldLeadToACrossedMarket(MarketSide marketSide, Contract contract, decimal orderPrice) { var bestOppositePrice = _orderRepository.GetBestPrice(contract, marketSide.Opposite()); var wouldCross = marketSide == MarketSide.Bid ? orderPrice > bestOppositePrice : orderPrice < bestOppositePrice; return wouldCross; }
private ReaderWriterLockSlim GetSidedLock(MarketSide side) { return((side == MarketSide.Bid) ? _bidsLock : _asksLock); }
public async Task <string> RePlaceOrderAsync(string orderId, string symbol, decimal price, decimal size, MarketSide side) { if (_orders.TryGetValue(orderId, out var oldSize)) { await CancelOrder(orderId); return(await PlaceOrderAsync(symbol, price, size, side)); } return(null); }
public static MarketSide Opposite(this MarketSide side) { return(side == MarketSide.Bid ? MarketSide.Ask : MarketSide.Bid); }
private SortedSet <IOrder> GetSidedStack(MarketSide side) { return((side == MarketSide.Bid) ? _bids : _asks); }
public Message CreateRejectNewOrderExecutionReport(string symbol, MarketSide marketSide, string clOrdID, decimal orderQuantity, TradingAccount account, string execID, string rejectionReason, int? rejectionCode = null) { var exReport = new ExecutionReport( new OrderID("unknown orderID"), new ExecID(execID), new ExecType(ExecType.REJECTED), new OrdStatus(OrdStatus.REJECTED), new Symbol(symbol), TranslateFixFields.Translate(marketSide), new LeavesQty(0m), new CumQty(0m), new AvgPx(0m)) { ClOrdID = new ClOrdID(clOrdID), OrderQty = new OrderQty(orderQuantity) }; if (rejectionCode.HasValue) { exReport.OrdRejReason = new OrdRejReason(rejectionCode.Value); } if (TradingAccount.IsSet(account)) { exReport.Account = new Account(account.Name); } return exReport; }
protected override void ReceivedDepth(string _symbol, string _type, JToken _token) { JArray _array = (JArray)_token; JArray _list = (_array.Count == 2 && _array[1].Type == JTokenType.Array) ? _array[1].Value <JArray>() : _array; //_symbol = _symbol.Split('.')[1]; if (_type == "START") { BookItems _asks = new BookItems(MarketSide.Ask); BookItems _bids = new BookItems(MarketSide.Bid); foreach (JArray _item in _list) { decimal _price = _item[0].Value <decimal>(); int _count = _item[1].Value <int>(); decimal _amount = _item[2].Value <decimal>(); MarketSide _side = _amount > 0 ? MarketSide.Bid : MarketSide.Ask; string _id = _price.ToString(); BookItem _bookItem = new BookItem(_symbol, _side, _price, Math.Abs(_amount), _id); if (_side == MarketSide.Bid) { _bids.TryAdd(_id, _bookItem); } if (_side == MarketSide.Ask) { _asks.TryAdd(_id, _bookItem); } } this.Books[_symbol, MarketSide.Ask] = _asks; this.Books[_symbol, MarketSide.Bid] = _bids; this.OnBookStarted(_symbol); } else { decimal _price = _list[1].Value <decimal>(); int _count = _list[2].Value <int>(); decimal _amount = _list[3].Value <decimal>(); MarketSide _side = _amount > 0 ? MarketSide.Bid : MarketSide.Ask; BookItems _items = this.Books[_symbol, _side]; if (_count == 0) { BookItem _item = _items.Delete(_price.ToString()); if (_item != null) { this.OnBookDelete(_item); } } else { BookItem _item = _items.Update(_price.ToString(), Math.Abs(_amount)); if (_item == null) { _item = _items.Insert(_price.ToString(), _price, _amount); this.OnBookInsert(_item); } else { this.OnBookUpdate(_item); } } } }
public BookItems(MarketSide _side) { this.Side = _side; }
public Message CreateOrderCancelMessage(string symbol, string clOrdID, string newClOrdID, MarketSide side, string orderID) { var ocq = new OrderCancelRequest(new OrigClOrdID(clOrdID), new ClOrdID(newClOrdID), new Symbol(symbol), TranslateFixFields.Translate(side), new TransactTime(DateTime.Now)) { OrderID = new OrderID(orderID) }; return ocq; }
public async Task <string> RePlaceOrderAsync(string orderId, string symbol, decimal price, decimal size, MarketSide side) { try { var request = new BulkLimitOrderRequest(); request.AssetPairId = symbol; request.CancelPreviousOrders = false; request.Orders.Add(new BulkOrder() { OldId = orderId, Side = side == MarketSide.Long ? Side.Buy : Side.Sell, Price = price.ToString(CultureInfo.InvariantCulture), Volume = size.ToString(CultureInfo.InvariantCulture) }); var result = await Client.PrivateApi.PlaceBulkLimitOrderAsync(request); if (result.Error != null) { Console.WriteLine($"ERROR: Cannot PlaceBulkLimitOrderAsync ({orderId}): {result.Error.Message}"); return(null); } var status = result.Payload.Statuses.FirstOrDefault(); if (status == null || status.Error != ErrorCode.Success) { Console.WriteLine($"ERROR: Cannot PlaceBulkLimitOrderAsync ({orderId}): {status.Error}"); return(null); } return(status.Id); } catch (Exception ex) { Console.WriteLine($"ERROR: Cannot RePlaceOrder ({orderId}):"); Console.WriteLine(ex); return(null); } }
private void Trade(OrderStackRow osr, MarketSide side) { if (osr == null) throw new ArgumentNullException("osr"); // The server currently does not support click trading, so we fake // it here by adding a matching order to the other side of the market. // If the server supported ImmediateOrCancel then we'd use that. var isBid = side == MarketSide.Bid; var matchingOrderDetails = new OrderRecord { ClOrdID = _clOrdIDGenerator.CreateClOrdID(), LastUpdateTime = DateTime.UtcNow, OrderID = string.Empty, // Set by server OrdType = OrderType.Limit, Price = decimal.Parse(isBid ? osr.BidPrice : osr.AskPrice), Quantity = decimal.Parse(isBid ? osr.BidQty : osr.AskQty), Side = isBid ? MarketSide.Ask : MarketSide.Bid, Symbol = osr.Symbol, Status = OrderStatus.New }; _serverFacade.CreateOrder(matchingOrderDetails); }
public async Task <string> PlaceOrderAsync(string symbol, decimal price, decimal size, MarketSide side) { try { var request = new LimitOrderRequest() { AssetPairId = symbol, Price = price.ToString(CultureInfo.InvariantCulture), Side = side == MarketSide.Long ? Side.Buy : Side.Sell, Volume = size.ToString(CultureInfo.InvariantCulture) }; var result = await Client.PrivateApi.PlaceLimitOrderAsync(request); if (result.Error != null) { Console.WriteLine($"ERROR: Cannot place limit order: {result.Error.Message}"); return(string.Empty); } return(result.Payload.OrderId); } catch (Exception ex) { Console.WriteLine("ERROR: Cannot place limit order:"); Console.WriteLine(ex); return(string.Empty); } }
public void TwoOrdersOnTheSameSideShouldNotMatch(MarketSide side) { var orderString = string.Format("Limit;TEST;{0};10@10", side); var stack = BuildStack(new[] {orderString, orderString}); var sortedBids = stack.Item1; var sortedAsks = stack.Item2; var matcher = CreateMatcher(); var matches = matcher.Match(sortedBids, sortedAsks); Assert.IsEmpty(matches, "There should not be any matches"); }
/// <summary> /// Adds an order to the system with the given properties /// </summary> /// <param name="sessionID">The FIX Session ID</param> /// <param name="orderType"> /// The type of order, currently only Limit orders are supported /// </param> /// <param name="symbol">The symbol for the order</param> /// <param name="marketSide">The side of the market for the order</param> /// <param name="clOrdID">The FIX ClOrdID for the order, set by the client</param> /// <param name="account">The trading account associated with the order</param> /// <param name="quantity">The quantity of the order</param> /// <param name="price">The price of the order, may be null for market orders</param> /// <returns>The new order after it has been added</returns> /// <exception cref="FixATServerException">If the order is rejected</exception> public IOrder AddOrder(FixSessionID sessionID, OrderType orderType, string symbol, MarketSide marketSide, string clOrdID, TradingAccount account, decimal quantity, decimal?price = null) { // A more complete system would look the contract up in a contract store var contract = new Contract(symbol); // TODO Replace this with a better mechanism (esp if more order types are supported) decimal orderPrice; switch (orderType) { case OrderType.Limit: { if (!price.HasValue) { throw new FixATServerException("Limit order must specify a price"); } orderPrice = price.Value; break; } // Uncomment this if and when market orders are supported //case OrderType.Market: // { // if (price.HasValue) // throw new FixATServerException( // "Market order should not have a specified price"); // orderPrice = GetMarketPrice(contract, marketSide); // break; // } default: throw new FixATServerException( string.Format("Order Type {0} not supported", orderType)); } if (OrderWouldLeadToACrossedMarket(marketSide, contract, orderPrice)) { throw new FixATServerException("Order would lead to a crossed market"); } var order = _orderRepository.AddOrder(CreateOrderID(), contract, orderType, marketSide, orderPrice, quantity, clOrdID, account); _orderOwners[order.ID] = sessionID; return(order); }
/// <summary> /// Gets the best price for the given contract and side of the market /// </summary> /// <returns> /// null if there are no orders for that contract or side, otherwise the best price /// </returns> public decimal? GetBestPrice(Contract contract, MarketSide side) { OrderStack stack; if (!_market.TryGetValue(contract, out stack)) { return null; } return stack.GetBestPrice(side); }
private static OrderRecord DefaultFakeOrderRecord(MarketSide side = MarketSide.Bid, decimal price = 100, decimal quantity = 10) { return new OrderRecord { ClOrdID = "ClOrdID", LastUpdateTime = DateTime.UtcNow, OrderID = "", OrdType = OrderType.Limit, Price = price, Quantity = quantity, Side = side, Status = OrderStatus.New, Symbol = "Symbol" }; }
public void TotalFillWithDeltaAdjustsPriceAsWellAsRefilling(MarketSide side, decimal delta, decimal price, decimal priceAfterFill) { const decimal totalQ = 20; const decimal clipQ = 10; var expected = DefaultFakeOrderRecord(quantity: totalQ, price: price, side: side); var mockServer = new Mock<IServerFacade>(); mockServer.Setup(s => s.CreateOrder(It.IsAny<OrderRecord>())).Returns(true); var iceberg = new IcebergOrder(mockServer.Object, expected.Symbol, expected.ClOrdID, expected.Side, expected.Quantity, clipQ, price, delta); iceberg.Activate(); var idGen = new SimpleOrderIDGenerator(); iceberg.ActivatedMarketOrderAccepted(idGen.GetID()); iceberg.OnTotalFill(); iceberg.ActivatedMarketOrderAccepted(idGen.GetID()); mockServer.Verify(s => s.CreateOrder(It.IsAny<OrderRecord>()), Times.Exactly(2)); Assert.AreEqual(clipQ, iceberg.CurrentQuantity, "Order not refilled to clip size"); Assert.AreEqual(totalQ - clipQ, iceberg.RemainingQuantity, "Remaining quantity should be total minus one clip"); Assert.AreEqual(priceAfterFill, iceberg.CurrentPrice, "Current price not as expected after fill with delta"); Assert.AreNotEqual(iceberg.InitialPrice, iceberg.CurrentPrice, "Current price should not equal initial price after fill with delta"); }
protected override void ReceivedDepth(string _symbol, string _type, JToken _token) { //this.Log("ReceivedDepth - " + _token.ToString()); JArray _list = (JArray)_token; if (_type == "partial") { _symbol = _list[0]["symbol"].Value <string>(); BookItems _asks = new BookItems(MarketSide.Ask); BookItems _bids = new BookItems(MarketSide.Bid); foreach (JObject _item in _list) { MarketSide _side = _item["side"].Value <string>().ToUpper() == "BUY" ? MarketSide.Bid : MarketSide.Ask; string _id = _item["id"].Value <string>(); decimal _price = _item["price"].Value <decimal>(); decimal _amount = _item["size"].Value <decimal>() / _price; BookItem _bookItem = new BookItem(_symbol, _side, _price, _amount, _id); if (_side == MarketSide.Bid) { _bids.TryAdd(_id, _bookItem); } if (_side == MarketSide.Ask) { _asks.TryAdd(_id, _bookItem); } } this.Books[_symbol, MarketSide.Ask] = _asks; this.Books[_symbol, MarketSide.Bid] = _bids; if (this.BookSize > 0) { this.Books[_symbol, MarketSide.Ask].Resize(this.BookSize); this.Books[_symbol, MarketSide.Bid].Resize(this.BookSize); } this.depths.Add(_symbol); this.OnBookStarted(_symbol); } else if (_type == "insert") { foreach (JObject _item in _list) { _symbol = _item["symbol"].Value <string>().ToUpper(); if (!this.depths.Contains(_symbol)) { continue; } MarketSide _side = _item["side"].Value <string>().ToUpper() == "BUY" ? MarketSide.Bid : MarketSide.Ask; string _id = _item["id"].Value <string>(); decimal _price = _item["price"].Value <decimal>(); decimal _amount = _item["size"].Value <decimal>() / _price; BookItem _bookItem = this.Books[_symbol, _side].Insert(_id, _price, _amount); this.OnBookInsert(_bookItem); if (this.BookSize > 0 && this.Books[_symbol, _side].Count > this.BookSize * 2) { this.Books[_symbol, _side].Resize(this.BookSize); } } } else if (_type == "update") { foreach (JObject _item in _list) { _symbol = _item["symbol"].Value <string>().ToUpper(); if (!this.depths.Contains(_symbol)) { continue; } MarketSide _side = _item["side"].Value <string>().ToUpper() == "BUY" ? MarketSide.Bid : MarketSide.Ask; string _id = _item["id"].Value <string>(); decimal _amount = _item["size"].Value <decimal>(); BookItem _bookItem = this.Books[_symbol, _side][_id]; if (_bookItem == null && this.BookSize > 0) { return; } else if (_bookItem == null) { this.Log("Book update failed 1 - " + _item.ToString(Newtonsoft.Json.Formatting.None)); return; } _amount = _amount / _bookItem.Price; _bookItem = this.Books[_symbol, _side].Update(_id, _amount); if (_bookItem != null) { this.OnBookUpdate(_bookItem); } else if (this.BookSize == 0) { this.Log("Book update failed 2 - " + _item.ToString(Newtonsoft.Json.Formatting.None)); } } } else if (_type == "delete") { foreach (JObject _item in _list) { _symbol = _item["symbol"].Value <string>().ToUpper(); if (!this.depths.Contains(_symbol)) { continue; } MarketSide _side = _item["side"].Value <string>().ToUpper() == "BUY" ? MarketSide.Bid : MarketSide.Ask; string _id = _item["id"].Value <string>(); BookItem _bookItem = this.Books[_symbol, _side].Delete(_id); if (_bookItem != null) { this.OnBookDelete(_bookItem); } else if (this.BookSize == 0) { this.Log("Book delete failed - " + _item.ToString(Newtonsoft.Json.Formatting.None)); } } } }