예제 #1
0
        /// <summary>
        /// Finds the primary period context for the specified document type. For example, if the document is a 10-Q, finds the primary ~90 day context.
        /// </summary>
        public XbrlContext FindNormalPeriodPrimaryContext()
        {
            //First, check if the primary period timespan already matches the document period type (i.e. if this document is a 10-Q and indeed the primary period they specified in the document is 90 days, or 3 months)
            bool        AlreadyMatches  = false;
            XbrlContext pricontext      = GetContextById(PrimaryPeriodContextId);
            TimeSpan    prispan         = pricontext.EndDate - pricontext.StartDate;
            string      strippeddoctype = DocumentType.ToLower().Replace("-", "");

            if (strippeddoctype == "10q")
            {
                if (prispan.TotalDays > 80 && prispan.TotalDays < 100) //Roughly 3 months
                {
                    AlreadyMatches = true;
                }
            }
            else if (strippeddoctype == "10k")
            {
                if (prispan.TotalDays > 350 && prispan.TotalDays < 380)
                {
                    AlreadyMatches = true;
                }
            }
            else
            {
                throw new Exception("Unable to recognize document type (not identified as a 10-K or a 10-Q).");
            }
            if (AlreadyMatches)
            {
                return(pricontext);
            }

            //Count
            ContextUseCount[] usecounts = CountContextUses();

            //Establish period bounds (i.e. quarterly is ~90, annual is ~360)
            int lowerbound = 0;
            int upperbound = 0;

            if (strippeddoctype == "10q")
            {
                lowerbound = 80;
                upperbound = 100;
            }
            else if (strippeddoctype == "10k")
            {
                lowerbound = 350;
                upperbound = 380;
            }
            else
            {
                throw new Exception("Unable to recognize document type (not identified as a 10-K or a 10-Q).");
            }


            //If a DocumentPeriodEndDate property is available for this doc (it should be), try to get
            //The proper period context that (i.e. 90 days, 360 days)
            //ends on that date
            //and is the most popular to meet the above criteria
            if (DocumentPeriodEndDate.HasValue)
            {
                foreach (ContextUseCount cuc in usecounts)
                {
                    if (cuc.Context.EndDate.ToShortDateString() == DocumentPeriodEndDate.Value.ToShortDateString()) //The end date of this period falls on the document's specified end date (what we want)
                    {
                        TimeSpan period_duration = cuc.Context.EndDate - cuc.Context.StartDate;
                        int      period_days     = period_duration.Days;
                        if (period_days > lowerbound && period_days < upperbound) //If it is in the range that is correct for this period
                        {
                            return(cuc.Context);
                        }
                    }
                }
            }

            //Now that it does not match, we need to find the most popular one that DOES match.
            XbrlContext ToReturn = null;

            foreach (ContextUseCount cuc in usecounts)
            {
                if (ToReturn == null) //Only replace if it is null (this is here to prevent this being replaced later down)
                {
                    TimeSpan thistimespan = cuc.Context.EndDate - cuc.Context.StartDate;
                    if (thistimespan.TotalDays > lowerbound && thistimespan.TotalDays < upperbound)
                    {
                        ToReturn = cuc.Context;
                    }
                }
            }

            //Check for error
            if (ToReturn == null)
            {
                throw new Exception("Unable to find normal period Context appropriate for this document.");
            }

            return(ToReturn);
        }
예제 #2
0
        public static XbrlInstanceDocument Create(Stream s)
        {
            XbrlInstanceDocument ToReturn = new XbrlInstanceDocument();

            StreamReader sr   = new StreamReader(s);
            int          loc1 = 0;
            int          loc2 = 0;


            #region "Get Contexts"
            s.Position = 0;
            List <XbrlContext> Contexts = new List <XbrlContext>();
            do
            {
                string line = sr.ReadLine();

                if (line.Contains("<context") || line.Contains("<xbrli:context"))
                {
                    XbrlContext con = new XbrlContext();
                    loc1   = line.IndexOf("id");
                    loc1   = line.IndexOf("\"", loc1 + 1);
                    loc2   = line.IndexOf("\"", loc1 + 1);
                    con.Id = line.Substring(loc1 + 1, loc2 - loc1 - 1).Trim();

                    do
                    {
                        string nl = sr.ReadLine().Trim();


                        if (nl.Contains("startDate"))
                        {
                            loc1          = nl.IndexOf("startDate");
                            loc1          = nl.IndexOf(">", loc1 + 1);
                            loc2          = nl.IndexOf("<", loc1 + 1);
                            con.StartDate = DateTime.Parse(nl.Substring(loc1 + 1, loc2 - loc1 - 1));
                            con.TimeType  = XbrlTimeType.Period;
                        }

                        if (nl.Contains("endDate"))
                        {
                            loc1         = nl.IndexOf("endDate");
                            loc1         = nl.IndexOf(">", loc1 + 1);
                            loc2         = nl.IndexOf("<", loc1 + 1);
                            con.EndDate  = DateTime.Parse(nl.Substring(loc1 + 1, loc2 - loc1 - 1));
                            con.TimeType = XbrlTimeType.Period;
                        }

                        if (nl.Contains("<instant>") || nl.Contains("<xbrli:instant"))
                        {
                            loc1            = nl.IndexOf("instant");
                            loc1            = nl.IndexOf(">", loc1 + 1);
                            loc2            = nl.IndexOf("<", loc1 + 1);
                            con.InstantDate = DateTime.Parse(nl.Substring(loc1 + 1, loc2 - loc1 - 1));
                            con.TimeType    = XbrlTimeType.Instant;
                        }

                        if (nl.Contains("</context") || nl.Contains("</xbrli:context"))
                        {
                            break;
                        }
                    } while (true);

                    Contexts.Add(con);
                }
            } while (sr.EndOfStream == false);

            ToReturn.Contexts = Contexts.ToArray();

            #endregion

            #region "Get Facts"
            s.Position = 0;
            List <XbrlFact> Facts = new List <XbrlFact>();
            do
            {
                string line = sr.ReadLine().Trim();


                if (line.Contains("<") && line.Contains(":") && line.Contains("<context") == false && line.Contains("<xbrli:context") == false && line.Contains("<!--") == false)
                {
                    string FactData = line;

                    //Get the next few lines for the remainder of the fact if it is not all on one line
                    if (FactData.Contains("</") == false && FactData.Contains("/>") == false)
                    {
                        do
                        {
                            string nl = sr.ReadLine();
                            FactData = FactData + " " + nl;
                        } while (FactData.Contains("</") == false && FactData.Contains("/>") == false);
                    }

                    //If this is indeed a fact (it appears to have all of the parts), get data from it
                    if (FactData.Contains("contextRef"))
                    {
                        XbrlFact fact = new XbrlFact();

                        //Track back from the "contextRef" because we know the contextRef tag is inside the fact itself
                        int last_contextRef = FactData.LastIndexOf("contextRef");

                        //Get the fact opening (tracked back from the last context ref)
                        int fact_open = FactData.LastIndexOf("<", last_contextRef);

                        //get the namspace
                        loc2             = FactData.IndexOf(":", fact_open);
                        fact.NamespaceId = FactData.Substring(fact_open + 1, loc2 - fact_open - 1);

                        //Get the label
                        loc1       = FactData.IndexOf(":", fact_open);
                        loc2       = FactData.IndexOf(" ", loc1 + 1);
                        fact.Label = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);

                        //Get decimals
                        loc1 = FactData.IndexOf("decimals=", fact_open);
                        if (loc1 != -1)
                        {
                            loc1 = FactData.IndexOf("\"", loc1 + 1);
                            loc2 = FactData.IndexOf("\"", loc1 + 1);
                            if (loc1 != -1 && loc2 != -1)
                            {
                                string decstr = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);
                                if (decstr.ToLower() != "inf")
                                {
                                    fact.Decimals = Convert.ToInt32(decstr);
                                }
                                else
                                {
                                    fact.Decimals = 0;
                                }
                            }
                        }

                        //Get id
                        loc1 = FactData.IndexOf("id=", fact_open);
                        loc1 = FactData.IndexOf("\"", loc1 + 1);
                        loc2 = FactData.IndexOf("\"", loc1 + 1);
                        if (loc1 != -1 && loc2 != -1)
                        {
                            fact.Id = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);
                        }

                        //Get unit ref
                        loc1 = FactData.IndexOf("unitRef=", fact_open);
                        loc1 = FactData.IndexOf("\"", loc1 + 1);
                        loc2 = FactData.IndexOf("\"", loc1 + 1);
                        if (loc1 != -1 && loc2 != -1)
                        {
                            fact.UnitId = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);
                        }

                        //Get context ref
                        loc1 = FactData.IndexOf("contextRef=", fact_open);
                        loc1 = FactData.IndexOf("\"", loc1 + 1);
                        loc2 = FactData.IndexOf("\"", loc1 + 1);
                        if (loc1 != -1 && loc2 != -1)
                        {
                            fact.ContextId = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);
                        }

                        //Get the value
                        if (FactData.Contains("/>") == false)
                        {
                            loc1 = FactData.IndexOf(">", fact_open);
                            loc2 = FactData.IndexOf("<", loc1 + 1);
                            if (loc1 != -1 && loc2 != -1 && loc2 > loc1)
                            {
                                string valstr = FactData.Substring(loc1 + 1, loc2 - loc1 - 1);
                                if (valstr != "")
                                {
                                    //This try bracket is in here because some XBRL files have more than just values in them (this is an error)... for example, XOM's 2017 filing
                                    try
                                    {
                                        fact.Value = valstr;
                                    }
                                    catch
                                    {
                                        fact.Value = null;
                                    }
                                }
                            }
                            else
                            {
                                fact.Value = null;
                            }
                        }
                        else
                        {
                            fact.Value = null;
                        }

                        Facts.Add(fact);
                    }
                }
            } while (sr.EndOfStream == false);
            ToReturn.Facts = Facts.ToArray();
            #endregion


            #region "Get Trading Symbol"
            bool TradingSymbolAlreadySet = false; //This is here
            s.Position = 0;
            do
            {
                string line = sr.ReadLine();
                //Is it the Trading Symbol?
                if (line.Contains("<dei:TradingSymbol"))
                {
                    string focus = line;
                    if (focus.Contains("</dei:TradingSymbol") == false && focus.Contains("/>") == false)
                    {
                        do
                        {
                            string nl = sr.ReadLine();
                            focus = focus + " " + nl;
                        } while (focus.Contains("</dei:TradingSymbol") == false && focus.Contains("/>") == false);
                    }
                    loc1 = focus.IndexOf("TradingSymbol");
                    loc1 = focus.IndexOf(">", loc1 + 1);
                    loc2 = focus.IndexOf("<", loc1 + 1);
                    if (loc1 > 0 && loc2 > loc1)
                    {
                        if (TradingSymbolAlreadySet == false)
                        {
                            ToReturn.TradingSymbol  = focus.Substring(loc1 + 1, loc2 - loc1 - 1);
                            ToReturn.TradingSymbol  = ToReturn.TradingSymbol.Replace("&#160;", "").Trim(); //I put this in because one of the 10-K's that I found (AMT, 2020 10-K) had this in it.  It is basically some HTML "Space" sign but I need to pull it out
                            TradingSymbolAlreadySet = true;
                        }
                    }
                }
            } while (sr.EndOfStream == false && TradingSymbolAlreadySet == false);
            #endregion

            #region "Get Document Type"

            sr         = new StreamReader(s); //Refresh the stream reader
            s.Position = 0;
            do
            {
                string line = sr.ReadLine();

                //Is it the document type
                if (line.Contains("<dei:DocumentType"))
                {
                    string Focus = line;
                    if (Focus.Contains("</dei:DocumentType") == false)
                    {
                        do
                        {
                            string nl = sr.ReadLine();
                            Focus = Focus + " " + nl;
                        } while (Focus.Contains("</dei:DocumentType") == false);
                    }
                    loc1 = Focus.IndexOf(">");
                    loc2 = Focus.IndexOf("<", loc1 + 1);
                    if (loc1 > 0 && loc2 > loc1)
                    {
                        ToReturn.DocumentType = Focus.Substring(loc1 + 1, loc2 - loc1 - 1);
                    }
                }
            } while (sr.EndOfStream == false);

            #endregion

            //This must appear BEOFRE finding the primary context instance
            #region "Get Primary Context Period"

            s.Position = 0;

            do
            {
                string line = sr.ReadLine();

                if (line.Contains("<dei:DocumentFiscalPeriodFocus") || line.Contains("<dei:CurrentFiscalYearEndDate"))
                {
                    string focus = line;
                    do
                    {
                        focus = focus + " " + sr.ReadLine();
                    } while (focus.Contains("contextRef=") == false);
                    loc1 = focus.IndexOf("contextRef=");
                    loc1 = focus.IndexOf("\"", loc1 + 1);
                    loc2 = focus.IndexOf("\"", loc1 + 1);
                    ToReturn.PrimaryPeriodContextId = focus.Substring(loc1 + 1, loc2 - loc1 - 1);
                }
            } while (sr.EndOfStream == false);

            #endregion

            //This can only occur AFTER finding the primary context period.
            #region "Get the Primary Context Instant"
            //Wrap back around and try to find the primary instant ref
            if (ToReturn.PrimaryPeriodContextId != "")
            {
                try
                {
                    XbrlContext        PrimaryPeriodContext     = ToReturn.GetContextById(ToReturn.PrimaryPeriodContextId);
                    string             doc_end_date             = PrimaryPeriodContext.EndDate.Month.ToString() + "-" + PrimaryPeriodContext.EndDate.Day.ToString() + "-" + PrimaryPeriodContext.EndDate.Year.ToString();
                    List <XbrlContext> EligibileInstantContexts = new List <XbrlContext>();
                    foreach (XbrlContext con in ToReturn.Contexts)
                    {
                        if (con.TimeType == XbrlTimeType.Instant)
                        {
                            string this_con_date = con.InstantDate.Month.ToString() + "-" + con.InstantDate.Day.ToString() + "-" + con.InstantDate.Year.ToString();
                            if (doc_end_date == this_con_date)
                            {
                                EligibileInstantContexts.Add(con);
                            }
                        }
                    }
                    if (EligibileInstantContexts.Count == 1)
                    {
                        ToReturn.PrimaryInstantContextId = EligibileInstantContexts[0].Id;
                    }
                    else if (EligibileInstantContexts.Count > 1)
                    {
                        XbrlContext WinningContext = null;
                        int         WinningCount   = -1;
                        foreach (XbrlContext con in EligibileInstantContexts)
                        {
                            int thiscount = con.GetRelevantFacts(ToReturn.Facts).Length;
                            if (thiscount > WinningCount)
                            {
                                WinningCount   = thiscount;
                                WinningContext = con;
                            }
                        }
                        ToReturn.PrimaryInstantContextId = WinningContext.Id;
                    }
                }
                catch
                {
                    ToReturn.PrimaryInstantContextId = "";
                }
            }
            #endregion

            return(ToReturn);
        }