// aktualizacja danych na liście po odebraniu ich z sieci internal void Update(DTO.OrderData data) { var order = list.Where(o => o.Id == data.BrokerId).SingleOrDefault(); if (order != null) { order.Update(data); } else { list.Add(new BosOrder(Account, data)); } }
// aktualizacja informacji o bieżących zleceniach private void OrderUpdateHandler(OrderData orderData) { var account = Accounts[orderData.AccountNumber]; account.Orders.Update(orderData); InvokeUpdate(account); }
// 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 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 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 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; }
/// <summary> /// Wysłanie do systemu nowego zlecenia z podanymi parametrami. /// <para>Zobacz też metody klasy BosInstrument: Order, Buy, Sell - które od razu określają, /// którego instrumentu dane zlecenie ma dotyczyć i ewentualnie prezyzują też stronę transakcji (kupno/sprzedaż)</para> /// </summary> /// <param name="account">Rachunek, na który zostaje przeznaczone to zlecenie.</param> /// <param name="instrument">Instrument, którego walory chcemy kupić/sprzedać.</param> /// <param name="side">Zlecenie kupna (BosOrderSide.Buy) czy sprzedaży (BosOrderSide.Sell).</param> /// <param name="price">Limit ceny, jaki wstawiamy do zlecenia (BosPrice.PKC/PCR/PCRO... lub po prostu kwota).</param> /// <param name="activationPrice">Ewentualny limit aktywacji zlecenia (null, jeśli aktywowane od razu, bez stop'a).</param> /// <param name="quantity">Liczba walorów, jaką zamierzamy kupić/sprzedać.</param> /// <param name="minimumQuantity">Minimalna liczba walorów, jaka musi się zrealizować, albo zlecenie będzie anulowane. /// Podając tutaj to samo, co w polu "quantity", uzyskujemy zlecenie typu "WuA".</param> /// <param name="visibleQuantity">Liczba walorów ujawniana w arkuszu ofert ("WUJ").</param> /// <param name="immediateOrCancel">Czy to zlecenie typu "WiA" (to, co nie wykona się natychmiast, jest od razu anulowane).</param> /// <param name="expirationDate">Data ważności zlecenia (null, jeśli tylko na bieżącą sesję).</param> public static void Create(BosAccount account, BosInstrument instrument, BosOrderSide side, BosPrice price, decimal? activationPrice, uint quantity, uint? minimumQuantity, uint? visibleQuantity, bool immediateOrCancel, DateTime? expirationDate) { var data = new OrderData(); data.AccountNumber = account.Number; data.MainData = new OrderMainData(); data.MainData.CreateTime = DateTime.Now; data.MainData.Instrument = instrument.Convert(); data.MainData.Side = side; data.MainData.PriceType = price.Type; data.MainData.PriceLimit = price.NumValue; data.MainData.ActivationPrice = activationPrice; data.MainData.Quantity = quantity; data.MainData.MinimumQuantity = minimumQuantity; data.MainData.VisibleQuantity = visibleQuantity; data.MainData.ImmediateOrCancel = immediateOrCancel; data.MainData.ExpirationDate = expirationDate; account.api.Connection.OrderCreate(data); // TODO: Zastanawiam się jeszczcze m.in. co z ClientId, TradeDate... i czy w ogóle byłby sens // od razu tworzyć taki nowy obiekt BosOrder (zamiast zaczekać aż sam się doda przy OrderUpdate). }
// przygotowanie obiektu transportowego na podstawie bieżącego obiektu private OrderData GetData() { var data = new OrderData(); data.AccountNumber = Account.Number; data.BrokerId = Id; data.MainData = new OrderMainData(); data.MainData.CreateTime = CreateTime; data.MainData.Instrument = Instrument.Convert(); data.MainData.Side = Side; data.MainData.PriceType = Price.Type; data.MainData.PriceLimit = Price.NumValue; data.MainData.ActivationPrice = ActivationPrice; data.MainData.Quantity = Quantity; data.MainData.MinimumQuantity = MinimumQuantity; data.MainData.VisibleQuantity = VisibleQuantity; data.MainData.ImmediateOrCancel = ImmediateOrCancel; data.MainData.ExpirationDate = ExpirationDate; return data; }
// aktualizacja danych obiektu po odebraniu ich z sieci internal void Update(OrderData data) { if (data.MainData != null) Update(data.MainData); if (data.StatusReport != null) Update(data.StatusReport); if (data.TradeReport != null) Update(data.TradeReport); }
// konstruktor wywoływany w klasie BosOrders, gdy pojawia się nowy numer zlecenia internal BosOrder(BosAccount account, OrderData data) { Account = account; Id = data.BrokerId; Update(data); }