public void InitializationOfAmqpStuff() { const String queueName = "queue_name"; const String exchangeName = "exchange_name"; const String routingKey = "routing_key"; var configuration = new EnvironmentConfiguration(); configuration.Endpoint(new Uri("amqp://localhost:5672")); configuration.GeneratesMessageIdBy(new Mock<INewId>().Object); configuration.ResolveMessageTypeBy(new Mock<IMessageTypeResolver>().Object); var model = new Mock<IModel>(); var connectionBuilder = StubConnectionBuilder(model); var broker = new BrokerWrapper(connectionBuilder.Object, model.Object, configuration); var consumer = new FakeConsumer(_ => Task.Factory.StartNew(() => { })); var queue = broker.DeclareQueue(queueName); var exchange = broker.DeclareDirectExchange(exchangeName); broker.DeclareExchangeBinding(exchange, queue, routingKey); broker.SubscribeByAtLeastOnce(queue, _ => { _.Consumes(consumer); }); broker.Connect(); model.Verify(_ => _.QueueDeclare(queueName, false, false, false, It.IsAny<IDictionary<String, Object>>())); model.Verify(_ => _.ExchangeDeclare(exchangeName, "direct", false, false, It.IsAny<IDictionary<String, Object>>())); model.Verify(_ => _.QueueBind(queueName, exchangeName, routingKey, It.IsAny<IDictionary<String, Object>>())); }
public void AccSumEnd(int p_reqId) { if (m_getAccountSummaryMres != null) { m_getAccountSummaryMres.Set(); // Sets the state of the event to signaled, which allows one or more threads waiting on the event to proceed. } // if you don't cancel it, all the data update come every 1 minute, which might be good, because we can give it to user instantenously.... // However, it would be an unnecessary traffic all the time... So, better to Cancel the data streaming. BrokerWrapper.CancelAccountSummary(p_reqId); }
public List <BrAccSum>?GetAccountSums() { List <BrAccSum>?result = null; int accReqId = -1; try { Stopwatch sw1 = Stopwatch.StartNew(); lock (m_getAccountSummaryLock) // IB only allows one query at a time, so next client has to wait { m_accSums = new List <BrAccSum>(); // delete old values if (m_getAccountSummaryMres == null) { m_getAccountSummaryMres = new ManualResetEventSlim(false); // initialize as unsignaled } else { m_getAccountSummaryMres.Reset(); // set to unsignaled, which makes thread to block } accReqId = BrokerWrapper.ReqAccountSummary(); bool wasLightSet = m_getAccountSummaryMres.Wait(5000); // timeout at 5sec if (!wasLightSet) { Utils.Logger.Error("ReqAccountSummary() ended with timeout error."); } //m_getAccountSummaryMres.Dispose(); // not necessary. We keep it for the next sessions for faster execution. result = m_accSums; // save it before releasing the lock, so other threads will not overwrite the result } sw1.Stop(); Utils.Logger.Info($"ReqAccountSummary() ends in {sw1.ElapsedMilliseconds}ms GatewayId: '{this.GatewayId}', Thread Id= {Thread.CurrentThread.ManagedThreadId}"); } catch (Exception e) { Utils.Logger.Error($"ReqAccountSummary() ended with exception: {e.Message}. BrokerYF is only replaced by BrokerIB after the IB connection had been established."); return(null); } finally { if (accReqId != -1) { BrokerWrapper.CancelAccountSummary(accReqId); } } return(result); }
public List <BrAccPos>?GetAccountPoss(string[] p_exclSymbolsArr) { List <BrAccPos>?result = null; try { Stopwatch sw2 = Stopwatch.StartNew(); lock (m_getAccountPositionsLock) //ReqPositions() doesn't have a reqID, so if we allow multiple threads to do it at the same time, we cannot sort out the output { m_accPoss = new List <BrAccPos>(); // delete old values m_exclSymbolsArr = p_exclSymbolsArr; if (m_getAccountPosMres == null) { m_getAccountPosMres = new ManualResetEventSlim(false); // initialize as unsignaled } else { m_getAccountPosMres.Reset(); // set to unsignaled, which makes the thread to block } BrokerWrapper.ReqPositions(); bool wasLightSet = m_getAccountPosMres.Wait(10000); // timeout at 10sec if (!wasLightSet) { Utils.Logger.Error("ReqPositions() ended with timeout error."); } result = m_accPoss; // save it before releasing the lock, so other threads will not overwrite the result } sw2.Stop(); // London local DEV client to servers: US-East: 180ms, Dublin: 51-64ms. This is 2x ping time. Linux server will be 5-10ms. Utils.Logger.Info($"ReqPositions() ends in {sw2.ElapsedMilliseconds}ms GatewayId: '{this.GatewayId}', Thread Id= {Thread.CurrentThread.ManagedThreadId}"); } catch (Exception e) { Utils.Logger.Error("ReqPositions() ended with exception: " + e.Message); return(null); } return(result); }
public void InitializationOfAmqpStuff() { const String queueName = "queue_name"; const String exchangeName = "exchange_name"; const String routingKey = "routing_key"; var configuration = new EnvironmentConfiguration(); configuration.GeneratesMessageIdBy(new Mock <INewId>().Object); configuration.ResolveMessageTypeBy(new Mock <IMessageTypeResolver>().Object); var model = new Mock <IModel>(); var connectionBuilder = StubConnectionBuilder(model); var broker = new BrokerWrapper(connectionBuilder.Object, model.Object, configuration); var consumer = new FakeConsumer(_ => Task.Factory.StartNew(() => { })); var queue = broker.DeclareQueue(queueName); var exchange = broker.DeclareDirectExchange(exchangeName); broker.DeclareExchangeBinding(exchange, queue, routingKey); broker.SubscribeByAtLeastOnce(queue, _ => { _.Consumes(consumer); }); broker.Connect(); model.Verify(_ => _.QueueDeclare(queueName, false, false, false, It.IsAny <IDictionary <String, Object> >())); model.Verify(_ => _.ExchangeDeclare(exchangeName, "direct", false, false, It.IsAny <IDictionary <String, Object> >())); model.Verify(_ => _.QueueBind(queueName, exchangeName, routingKey, It.IsAny <IDictionary <String, Object> >())); }
// this service should be implemented using the low-level BrokerWrappers (so if should work, no matter it is IB or YF or GF BrokerWrapper) // this will be called multiple times in parallel. So, be careful when to use Shared resources, we need locking public string GetRealtimePriceService(string p_input) { string resultPrefix = "", resultPostfix = ""; try { // !!! HACK, temporary: from 2019-01-31 - 2019-05-02, when VXXB was alive. After that VXX is the active again. // http://www.snifferquant.com/dac/VixTimer asks for VXX every 20 minutes, which is wrong. VBroker will fail on that. If VXX is asked, we change it to VXXB. // TEMPorary. Remove these after Laszlo fixed the query in VixTimer quote //p_input = p_input.Replace(",VXX,", ",VXXB"); // caret(^) is not a valid URL character, so it is encoded to %5E; skip the first '?' , convert everything to Uppercase, because '%5e', and '%5E' is the same for us string input = Uri.UnescapeDataString(p_input.Substring(1)); // change %20 to ' ', and %5E to '^' , "^VIX" is encoded in the URI as "^%5EVIX" string[] inputParams = input.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries); List <Tuple <string, Dictionary <int, PriceAndTime>?, int> > tickerList = new List <Tuple <string, Dictionary <int, PriceAndTime>?, int> >(); int nTempTickers = 0; foreach (var inputParam in inputParams) { if (inputParam.StartsWith("S=", StringComparison.CurrentCultureIgnoreCase)) // symbols { string[] tickerArray = inputParam.Substring("S=".Length).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var symbol in tickerArray) { string sqTicker = symbol.ToUpper().Replace("^^", "#"); // Robert's suggestion all Futures tickers #+Underlying, therefore #^VIX, because the underlying is ^VIX Contract contract = VBrokerUtils.ParseSqTickerToContract(sqTicker); Dictionary <int, PriceAndTime> rtPrices; if (sqTicker[0] == '^') // if Index, not stock. Index has only LastPrice and TickType.ClosePrice { rtPrices = new Dictionary <int, PriceAndTime>() // we are interested in the following Prices { { TickType.LAST, new PriceAndTime() } }; } else { rtPrices = new Dictionary <int, PriceAndTime>() // we are interested in the following Prices { { TickType.ASK, new PriceAndTime() }, { TickType.BID, new PriceAndTime() }, //{ TickType.CLOSE, new PriceAndTime() }, { TickType.LAST, new PriceAndTime() } }; } if (BrokerWrapper.GetAlreadyStreamedPrice(contract, ref rtPrices)) { tickerList.Add(new Tuple <string, Dictionary <int, PriceAndTime>?, int>(sqTicker, rtPrices, -1)); // -1 means: isTemporaryTicker = false, it was permanently subscribed } else { tickerList.Add(new Tuple <string, Dictionary <int, PriceAndTime>?, int>(sqTicker, null, -2)); // -2 means: isTemporaryTicker = true nTempTickers++; } } } if (inputParam.StartsWith("JSONP=", StringComparison.CurrentCultureIgnoreCase)) // symbols { string jsonpPrefix = inputParam.Substring(6); resultPrefix = jsonpPrefix + "("; resultPostfix = ")"; // semicolon; ';' is not required in JS, because of semicolon auto-insertion } } // 1. if there are tickers without data, subscribe to them, wait for them, get the data if (nTempTickers > 0) // try to enter the Critical section ONLY if it is necessary. Try to not LOCK threads unnecessarily { lock (BrokerWrapper) // RequestMarketData will use gBrokerApi; so Synch it as a Critical section { try { AutoResetEvent priceTickARE = new AutoResetEvent(false); // set it to non-signaled => which means Block //priceTickARE.Reset(); // set it to non-signaled => which means Block for (int i = 0; i < tickerList.Count; i++) { if (tickerList[i].Item2 == null) // if it is temporary ticker (it should be -2, at this point) { int mktDataId = BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract(tickerList[i].Item1), string.Empty, true, (cb_mktDataId, cb_mktDataSubscr, cb_tickType, cb_price) => { Utils.Logger.Trace($"{cb_mktDataSubscr.Contract.Symbol} : {cb_tickType}: {cb_price}"); //Console.WriteLine($"{cb_mktDataSubscr.Contract.Symbol} : {cb_tickType}: {cb_price}"); // better not clutter the console if ((cb_tickType == TickType.LAST) || (((cb_tickType == TickType.ASK) || (cb_tickType == TickType.BID)) && (cb_mktDataSubscr.Contract.SecType != "IND"))) // for stocks or for Futures { priceTickARE.Set(); } }); // as Snapshot, not streaming data tickerList[i] = new Tuple <string, Dictionary <int, PriceAndTime>?, int>(tickerList[i].Item1, null, mktDataId); } } // instead of Thread.Sleep(2000); // wait until data is here; TODO: make it sophisticated later //Thread.Sleep(2000); int iTimeoutCount = 0; DateTime waitStartTime = DateTime.UtcNow; while (iTimeoutCount < 5) // all these checks usually takes 0.1 seconds = 100msec, so do it every time after connection { bool isOneSignalReceived = priceTickARE.WaitOne(400); // 400ms wait, max 5 times. if (isOneSignalReceived) // so, it was not a timeout, but a real signal { var waitingDuration = DateTime.UtcNow - waitStartTime; if (waitingDuration.TotalMilliseconds > 2000.0) { break; // if we wait more than 2 seconds, break the While loop } bool isAllInfoReceived = true; for (int i = 0; i < tickerList.Count; i++) { if (tickerList[i].Item2 == null) // if it is temporary ticker { string sqTicker = tickerList[i].Item1; int mktDataId = tickerList[i].Item3; Contract contract = VBrokerUtils.ParseSqTickerToContract(sqTicker); Dictionary <int, PriceAndTime> rtPrices; if (sqTicker[0] == '^') // if Index, not stock. Index has only LastPrice and TickType.ClosePrice { rtPrices = new Dictionary <int, PriceAndTime>() // we are interested in the following Prices { { TickType.LAST, new PriceAndTime() } }; } else { rtPrices = new Dictionary <int, PriceAndTime>() // we are interested in the following Prices { { TickType.ASK, new PriceAndTime() }, { TickType.BID, new PriceAndTime() }, //{ TickType.CLOSE, new PriceAndTime() }, { TickType.LAST, new PriceAndTime() } }; } if (BrokerWrapper.GetAlreadyStreamedPrice(contract, ref rtPrices)) { tickerList[i] = new Tuple <string, Dictionary <int, PriceAndTime>?, int>(sqTicker, rtPrices, mktDataId); } else { isAllInfoReceived = false; // break; don't end the for cycle here. Maybe a ticker (VIX futures) will be never received, because IB user doesn't have subscription. However, all tickers AFTER that ticker should be tried too. } } } // for if (isAllInfoReceived) { break; // break from while if all is received } } else { iTimeoutCount++; } } //Console.WriteLine($"Waiting for RT prices: {(DateTime.UtcNow - waitStartTime).TotalMilliseconds} ms."); // don't clutter Console Utils.Logger.Trace($"Waiting for RT prices: {(DateTime.UtcNow - waitStartTime).TotalMilliseconds} ms."); } catch (Exception e) { Utils.Logger.Error("GetRealtimePriceService() inner part ended with exception 2: " + e.Message); return(resultPrefix + @"{ ""Message"": ""Exception in WebVBroker app Execute() 2. Exception: " + e.Message + @""" }" + resultPostfix); } finally { foreach (var tickerItem in tickerList) { if (tickerItem.Item3 != -1) // if it was a temporary ticker { BrokerWrapper.CancelMktData(tickerItem.Item3); } } } // finally } // lock } // if nTempTickers // 2. Assuming BrokerWrapper.GetAlreadyStreamedPrice() now has all the data StringBuilder jsonResultBuilder = new StringBuilder(resultPrefix + "["); bool isFirstTickerWrittenToOutput = false; foreach (var tickerItem in tickerList) { if (isFirstTickerWrittenToOutput) { jsonResultBuilder.AppendFormat(","); } string sqTicker = tickerItem.Item1; var rtPrices = tickerItem.Item2; bool isTemporaryTicker = (tickerItem.Item3 != -1); // if it is temporary ticker if (rtPrices == null) { jsonResultBuilder.AppendFormat(@"{{""Symbol"":""{0}""}}", sqTicker); // Data is not given } else { // I wanted to return only Ask/Bid, but because Indices has only LastPrice (no Ask, Bid), I gave up: let's return LastPrice for stocks too //PriceAndTime ask, bid; //isFound = priceInfo.TryGetValue(TickType.AskPrice, out ask); //if (!isFound) // return @"{ ""Message"": ""No askprice yet. Maybe later."" }"; //isFound = priceInfo.TryGetValue(TickType.BidPrice, out bid); //if (!isFound) // return @"{ ""Message"": ""No bidprice yet. Maybe later."" }"; //jsonResultBuilder.AppendFormat(@"{{""Symbol"": ""{0}"", ""Ask"": {1}, ""Bid"", {2} }}", ticker, ask.Price, bid.Price); // Warning. I am not sure why this return "" things is here inside the for loop. We may have to eliminate it. or change it to continue. PriceAndTime?ask = null, bid = null; bool isFound = rtPrices.TryGetValue(TickType.LAST, out PriceAndTime? last); if (!isFound) { return(resultPrefix + @"{ ""Message"": ""No last price yet. Maybe later."" }" + resultPostfix); // this didn't happen with the Fixed tickers, as the records are already in the Array at Program start } if (sqTicker[0] != '^') { isFound = rtPrices.TryGetValue(TickType.ASK, out ask); if (!isFound) { return(resultPrefix + @"{ ""Message"": ""No ask price yet. Maybe later."" }" + resultPostfix); } isFound = rtPrices.TryGetValue(TickType.BID, out bid); if (!isFound) { return(resultPrefix + @"{ ""Message"": ""No bid price yet. Maybe later."" }" + resultPostfix); } } // Robin suggested that don't send "NaN", and 00:00:00 in cases where there is nothing, just send the ticker back // also, if data in memory cache is 10 hours old: say we don't have Realtime price. That is not realtime // for GOOG as a snapshot price, IB gives Ask, Bid instantly, but sometimes it doesn't give Last (as last didn't occur or what). In that case, we want to give back Ask, Bid at least DateTime highestOfAllTime = DateTime.MinValue; if (last != null && last.Time > highestOfAllTime) { highestOfAllTime = last.Time; } if (ask != null && ask.Time > highestOfAllTime) { highestOfAllTime = ask.Time; } if (bid != null && bid.Time > highestOfAllTime) { highestOfAllTime = bid.Time; } if (highestOfAllTime.AddHours(11) < DateTime.UtcNow) // it was 10 hours at the beginning, but Robert wanted 17 hours; I still disagree, but increased from 10 to 11. It doesn't help with Snapshot prices, because IBGateway doesn't give price for them 10 hours after market close { jsonResultBuilder.AppendFormat(@"{{""Symbol"":""{0}""}}", sqTicker); // Data is too old. } else { // last Data is not too old (we could have checked the Ask or Bid data, but Indices have only Last data if (sqTicker[0] == '^') // if Index, not stock. Index has only LastPrice and TickType.ClosePrice { if (last == null || Double.IsNaN(last.Price)) { jsonResultBuilder.AppendFormat(@"{{""Symbol"":""{0}""}}", sqTicker); } else { jsonResultBuilder.AppendFormat(@"{{""Symbol"":""{0}"",""LastUtc"":""{1:yyyy'-'MM'-'dd'T'HH:mm:ss}"",""Last"":{2},""UtcTimeType"":""{3}""}}", sqTicker, last.Time, last.Price, (isTemporaryTicker) ? "SnapshotTime" : "LastChangedTime"); } } else { // stock or Futures, not Index jsonResultBuilder.AppendFormat(@"{{""Symbol"":""{0}""", sqTicker); bool isWrittenAnythingToOutput = false; if (last != null && !Double.IsNaN(last.Price)) { jsonResultBuilder.AppendFormat(@",""LastUtc"":""{0:yyyy'-'MM'-'dd'T'HH:mm:ss}"",""Last"":{1}", last.Time, last.Price); isWrittenAnythingToOutput = true; } if (bid != null && !Double.IsNaN(bid.Price)) { jsonResultBuilder.AppendFormat(@",""BidUtc"":""{0:yyyy'-'MM'-'dd'T'HH:mm:ss}"",""Bid"":{1}", bid.Time, bid.Price); // Bid is the smaller than Ask; start with that isWrittenAnythingToOutput = true; } if (ask != null && !Double.IsNaN(ask.Price)) { jsonResultBuilder.AppendFormat(@",""AskUtc"":""{0:yyyy'-'MM'-'dd'T'HH:mm:ss}"",""Ask"":{1}", ask.Time, ask.Price); isWrittenAnythingToOutput = true; } if (isWrittenAnythingToOutput) // if we had no data, there is no point of writing the TimeType { jsonResultBuilder.AppendFormat(@",""UtcTimeType"":""{0}""", (isTemporaryTicker) ? "SnapshotTime" : "LastChangedTime"); } jsonResultBuilder.Append(@"}"); } } // last data is not too old } isFirstTickerWrittenToOutput = true; } jsonResultBuilder.Append(@"]" + resultPostfix); Utils.Logger.Info("GetRealtimePriceService() ended properly: " + jsonResultBuilder.ToString()); return(jsonResultBuilder.ToString()); } catch (Exception e) { Utils.Logger.Error("GetRealtimePriceService ended with exception: " + e.Message); return(resultPrefix + @"{ ""Message"": ""Exception in VBroker app GetRealtimePriceService(). Exception: " + e.Message + @""" }" + resultPostfix); } }