예제 #1
0
        private void MainGatewayJustConnected()
        {
            if (m_isSupportPreStreamRealtimePrices && m_mainGateway != null)
            {
                // getting prices of SPY (has dividend, but liquid) or VXX (no dividend, but less liquids) is always a must. An Agent would always look that price. So, subscribe to that on the MainGateway
                // see what is possible to call: "g:\temp\_programmingTemp\TWS API_972.12(2016-02-26)\samples\CSharp\IBSamples\IBSamples.sln"

                // for NeuralSniffer
                // 2020-06: NeuralSniffer is not traded at the moment.
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("^RUT"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("UWM"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("TWM"));

                // for UberVXX
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("VXX"));
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("SVXY"));
                //m_mainGateway.BrokerWrapper.ReqMktDataStream(new Contract() { Symbol = "SPY", SecType = "STK", Currency = "USD", Exchange = "SMART" }); // for TotM forecast, but it is not needed just yet

                // for HarryLong
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("TQQQ"));
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("TMV"));
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("VXZ"));
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("SCO"));  // 2020-04-02: use SCO (2x); instead of short USO (1x), short UWT (-3x) was used, but it was delisted, because it went to penny stock
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("UNG"));

                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("TMF")); // Can be commented out: TMF (3x) is for Agy,
                m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("USO")); // Can be commented out: Agy uses partial SCO, partial USO for diversifying and because SCO is not a good tracker of USO

                // for TAA, but it is only temporary. We will not stream this unnecessary data all day long, as TAA can take its time. It only trades MOC. Extra 2-3 seconds doesn't matter.
                // "TLT"+ "MDY","ILF","FEZ","EEM","EPP","VNQ","IBB"  +  "MVV", "URE", "BIB"
                // 2020-06: TAA is not traded at the moment.
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("TLT"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("MDY"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("ILF"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("FEZ"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("EEM"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("EPP"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("VNQ"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("IBB"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("MVV"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("URE"));
                // m_mainGateway.BrokerWrapper.ReqMktDataStream(VBrokerUtils.ParseSqTickerToContract("BIB"));
            }
        }
예제 #2
0
        // 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);
            }
        }