// funkcja pomocnicza zamieniająca typ zlecenia FIXML na nasz DTO.PriceType private static PriceType ExecReport_GetPriceType(ExecutionReportMsg msg) { switch (msg.Type) { case OrderType.Limit: case OrderType.StopLimit: return PriceType.Limit; case OrderType.PKC: case OrderType.StopLoss: return PriceType.PKC; case OrderType.PCR_PCRO: var time = msg.TimeInForce; var pcro = ((time == OrdTimeInForce.Opening) || (time == OrdTimeInForce.Closing)); return pcro ? PriceType.PCRO : PriceType.PCR; default: throw new ArgumentException("Unknown ExecReport-OrderType"); } }
/// <summary> /// Przykład wysyłki zleceń, korzystając bezpośrednio z klas NewOrderSingleMsg i spółki... /// Test ten wrzuca na giełdę zlecenie kupna 1 x FW20 po 1000zł (*raczej* nie ma szans się zrealizować :)), /// następnie je modyfikuje ustawiając limit ceny oczko wyżej... aż ostatecznie całe zlecenie anuluje. /// </summary> public void Execute() { var accountNumber = "00-22-..."; // <- wpisz tu swój numer, żeby program nie musiał o niego pytać if (accountNumber.EndsWith("...")) { Console.Write("Podaj numer rachunku (końcówkę z " + accountNumber + "): "); var str = Console.ReadLine(); accountNumber = accountNumber.Replace("...", str); Trace.WriteLine("Wybrany rachunek: " + accountNumber); } // nawiązanie połączenia z NOL3 i zalogowanie użytkownika using (var nol = new NolClient()) { Thread.Sleep(2000); var tmp = FixmlMsg.DebugFormattedXml.Enabled; try { ExecutionReportMsg execReport; // --- wysyłka nowego zlecenia --- Console.WriteLine("\nPress any key... to send NEW order request [Esc - exit]\n"); if (Console.ReadKey(true).Key == ConsoleKey.Escape) return; var newRequest = new NewOrderSingleMsg(); newRequest.Account = accountNumber; newRequest.Side = OrderSide.Buy; newRequest.Instrument = FixmlInstrument.FindBySym("FW20H12"); newRequest.Quantity = 1; newRequest.Price = 1000; using (var socket = NolClient.GetSyncSocket()) { FixmlMsg.DebugFormattedXml.Enabled = true; // <- wyświetli nam dokładną treść komunikatów newRequest.Send(socket); execReport = new ExecutionReportMsg(socket); FixmlMsg.DebugFormattedXml.Enabled = tmp; } Thread.Sleep(3000); // --- modyfikacja tego zlecenia --- Console.WriteLine("\nPress any key... to MODIFY this order request [Esc - exit]\n"); if (Console.ReadKey(true).Key == ConsoleKey.Escape) return; var replaceRequest = new OrderReplaceRequestMsg(); replaceRequest.BrokerOrderId2 = execReport.BrokerOrderId2; replaceRequest.Account = accountNumber; replaceRequest.Side = OrderSide.Buy; replaceRequest.Instrument = newRequest.Instrument; replaceRequest.Quantity = 1; replaceRequest.Price = 1001; using (var socket = NolClient.GetSyncSocket()) { FixmlMsg.DebugFormattedXml.Enabled = true; replaceRequest.Send(socket); execReport = new ExecutionReportMsg(socket); FixmlMsg.DebugFormattedXml.Enabled = tmp; } Thread.Sleep(3000); // --- anulowanie tego zlecenia --- Console.WriteLine("\nPress any key... to CANCEL this order request [Esc - exit]\n"); if (Console.ReadKey(true).Key == ConsoleKey.Escape) return; var cancelRequest = new OrderCancelRequestMsg(); cancelRequest.BrokerOrderId2 = replaceRequest.BrokerOrderId2; cancelRequest.Account = accountNumber; cancelRequest.Side = newRequest.Side; cancelRequest.Instrument = newRequest.Instrument; cancelRequest.Quantity = newRequest.Quantity; using (var socket = NolClient.GetSyncSocket()) { FixmlMsg.DebugFormattedXml.Enabled = true; cancelRequest.Send(socket); execReport = new ExecutionReportMsg(socket); FixmlMsg.DebugFormattedXml.Enabled = false; } Thread.Sleep(3000); Console.WriteLine("\nPress any key... to exit\n"); Console.ReadKey(true); Console.WriteLine("\n\nThank you :)\n"); } catch (Exception e) { MyUtil.PrintError(e); } FixmlMsg.DebugFormattedXml.Enabled = tmp; } // tu następuje wylogowanie }
// funkcja pomocnicza zamieniająca status zlecenia FIXML na nasz BosOrderStatus private static BosOrderStatus ExecReport_GetStatus(ExecutionReportMsg msg) { switch (msg.Status) { case ExecReportStatus.New: return BosOrderStatus.Active; case ExecReportStatus.PartiallyFilled: return BosOrderStatus.ActiveFilled; case ExecReportStatus.Canceled: return ((msg.CumulatedQuantity ?? 0) > 0) ? BosOrderStatus.CancelledFilled : BosOrderStatus.Cancelled; case ExecReportStatus.Filled: return BosOrderStatus.Filled; case ExecReportStatus.Expired: return BosOrderStatus.Expired; case ExecReportStatus.Rejected: return BosOrderStatus.Rejected; case ExecReportStatus.PendingReplace: return BosOrderStatus.PendingReplace; case ExecReportStatus.PendingCancel: return BosOrderStatus.PendingCancel; default: throw new ArgumentException("Unknown ExecReport-Status"); } }
// wewnętrzna obsługa komunikatu "ExecRpt" // - wywołuje zdarzenie "OrderUpdateEvent" private void ExecReportMsgHandler(ExecutionReportMsg msg) { if (OrderUpdateEvent != null) { var order = new OrderData(); order.AccountNumber = msg.Account; order.BrokerId = msg.BrokerOrderId2; order.ClientId = msg.ClientOrderId; // UWAGA: odczyt danych z komunikatu ExecRpt odbywa się "na czuja"... bo dostarczoną // z Bossy/Comarchu dokumentacją na ten temat to można sobie najwyżej w kominku podpalić. // Generalnie wszystko jest rozjechane, ale te statusy zleceń to już chyba po pijaku pisali. // Tyle - musiałem to tu napisać !!! ;-P // Bo mnie już krew zalewa, jak widzę co ten NOL3 wysyła i gdzie (w których polach)... // I że w ogóle musiałem te wszystkie możliwe przypadki samodzielnie analizować... // A jak za miesiąc przestanie działać, bo coś tam "naprawią", to chyba kogoś postrzelę ;-( // raport o wykonaniu - być może cząstkowym - danego zlecenia // (z praktyki wynika, że zawsze wcześniej dostaniemy "pełen" ExecRpt z pozostałymi // informacjami o tym zleceniu... dlatego teraz odbieramy sobie tylko raport z tej transakcji) if (msg.ExecType == ExecReportType.Trade) { order.TradeReport = new OrderTradeData(); order.TradeReport.Time = (DateTime)msg.TransactionTime; order.TradeReport.Price = (decimal)msg.Price; // LastPrice !? order.TradeReport.Quantity = (uint)msg.Quantity; // LastQuantity !? order.TradeReport.NetValue = (decimal)msg.NetMoney; order.TradeReport.Commission = (decimal)msg.CommissionValue; } else { // w pozostałych przypadkach wygląda na to, że lepiej się oprzeć na polu "Status" // (bo ExecType czasem jest, czasem nie ma - różnie to z nim bywa... a Status jest chyba zawsze) order.StatusReport = new OrderStatusData(); order.StatusReport.Status = ExecReport_GetStatus(msg); order.StatusReport.Quantity = (uint)msg.CumulatedQuantity; order.StatusReport.NetValue = (decimal)msg.NetMoney; order.StatusReport.Commission = (decimal)msg.CommissionValue; // czasem == 0, ale dlaczego!? kto to wie... // pozostałe dane - żeby się nie rozdrabniać - też aktualizujemy za każdym razem // (teoretycznie wystarczyłoby przy "new" i "replace"... ale czasem jako pierwsze // przychodzi np. "filled" i kto wie co jeszcze innego, więc tak będzie bezpieczniej) order.MainData = new OrderMainData(); order.MainData.CreateTime = (DateTime)msg.TransactionTime; order.MainData.Instrument = msg.Instrument.Convert(); order.MainData.Side = (msg.Side == Fixml.OrderSide.Buy) ? BosOrderSide.Buy : BosOrderSide.Sell; order.MainData.PriceType = ExecReport_GetPriceType(msg); if (order.MainData.PriceType == PriceType.Limit) order.MainData.PriceLimit = msg.Price; if ((msg.Type == OrderType.StopLimit) || (msg.Type == OrderType.StopLoss)) order.MainData.ActivationPrice = msg.StopPrice; order.MainData.Quantity = (uint)msg.Quantity; order.MainData.MinimumQuantity = (msg.TimeInForce == OrdTimeInForce.WuA) ? msg.Quantity : msg.MinimumQuantity; order.MainData.VisibleQuantity = msg.DisplayQuantity; order.MainData.ImmediateOrCancel = (msg.TimeInForce == OrdTimeInForce.WiA); order.MainData.ExpirationDate = (msg.TimeInForce == OrdTimeInForce.Date) ? msg.ExpireDate : null; } // wywołanie zdarzenia z przygotowanymi danymi OrderUpdateEvent(order); } }
// Metoda IBosClient do anulowania istniejącego zlecenia. public void OrderCancel(OrderData data) { Debug.WriteLine("\nOrderCancel..."); using (Socket socket = NolClient.GetSyncSocket()) { OrderCancelRequestMsg request = new OrderCancelRequestMsg(); request.Account = data.AccountNumber; request.BrokerOrderId2 = data.BrokerId; request.Instrument = FixmlInstrument.Find(data.MainData.Instrument); request.Side = (data.MainData.Side == BosOrderSide.Buy) ? OrderSide.Buy : OrderSide.Sell; request.Quantity = data.MainData.Quantity; request.Send(socket); ExecutionReportMsg response = new ExecutionReportMsg(socket); } Debug.WriteLine("OrderCancel OK\n"); }
// Metoda IBosClient do modyfikacji istniejącego zlecenia. public void OrderReplace(OrderData data) { Debug.WriteLine("\nOrderReplace..."); using (Socket socket = NolClient.GetSyncSocket()) { OrderReplaceRequestMsg request = new OrderReplaceRequestMsg(); request.Account = data.AccountNumber; request.BrokerOrderId2 = data.BrokerId; request.Instrument = FixmlInstrument.Find(data.MainData.Instrument); request.Side = (data.MainData.Side == BosOrderSide.Buy) ? OrderSide.Buy : OrderSide.Sell; request.Type = Order_GetType(data.MainData); request.Price = data.MainData.PriceLimit; request.StopPrice = data.MainData.ActivationPrice; request.Quantity = data.MainData.Quantity; request.MinimumQuantity = data.MainData.MinimumQuantity; request.DisplayQuantity = data.MainData.VisibleQuantity; request.TimeInForce = Order_GetTimeInForce(data.MainData); request.ExpireDate = data.MainData.ExpirationDate; request.Send(socket); ExecutionReportMsg response = new ExecutionReportMsg(socket); } Debug.WriteLine("OrderReplace OK\n"); }
// Metoda IBosClient do składania nowego zlecenia. public string OrderCreate(OrderData data) { string clientId; Debug.WriteLine("\nOrderCreate..."); using (Socket socket = NolClient.GetSyncSocket()) { NewOrderSingleMsg request = new NewOrderSingleMsg(); clientId = request.ClientOrderId; // automatycznie przydzielone kolejne Id request.Account = data.AccountNumber; request.CreateTime = data.MainData.CreateTime; request.Instrument = FixmlInstrument.Find(data.MainData.Instrument); request.Side = (data.MainData.Side == BosOrderSide.Buy) ? OrderSide.Buy : OrderSide.Sell; request.Type = Order_GetType(data.MainData); request.Price = data.MainData.PriceLimit; request.StopPrice = data.MainData.ActivationPrice; request.Quantity = data.MainData.Quantity; request.MinimumQuantity = data.MainData.MinimumQuantity; request.DisplayQuantity = data.MainData.VisibleQuantity; request.TimeInForce = Order_GetTimeInForce(data.MainData); request.ExpireDate = data.MainData.ExpirationDate; request.Send(socket); ExecutionReportMsg response = new ExecutionReportMsg(socket); } Debug.WriteLine("OrderCreate OK\n"); return clientId; }