//file naming convention: exchange_symbol_interval_index.txt
        public async Task <bool> Update(IQuoteBasicDownloader download, string symbol, int interval, int timeout = 50000)
        {
            //lock (this)
            {
                var files = this.FindQuoteFiles(symbol, interval);
                if (files != null && files.Count > 0)
                {
                    files.Sort();
                    var readtream = new FileStream(files.Last(), FileMode.Open);
                    var q1        = QuoteBasicBase.InitByStream(readtream);
                    readtream.Close();

                    //if time difference is too small => skip download
                    var utcNow = DateTime.UtcNow.GetUnixTimeFromUTC();
                    if (utcNow - q1.LastTime <= interval)
                    {
                        return(false);
                    }

                    var q = await download.Download(symbol, interval, timeout);

                    if (q == null || q.Count == 0)
                    {
                        return(false);
                    }

                    if (q1.Count < _maxNumBarsInFile)
                    {
                        q1.Append(q, false);
                        var writestream = new FileStream(files.Last(), FileMode.Truncate);
                        q1.AppendStream(writestream);
                        writestream.Close();
                        OnQuoteSaved?.Invoke(this, this.Exchange, files.Last());
                    }
                    else
                    {
                        var index       = int.Parse(files.Last().Split('_')[4].Split('.')[0]);
                        var fn          = this.GetQuoteFileName(symbol, interval, index + 1);
                        var writestream = new FileStream(fn, FileMode.OpenOrCreate);
                        q.AppendStream(writestream);
                        writestream.Close();

                        OnQuoteSaved?.Invoke(this, this.Exchange, fn);
                    }
                }
                else
                {
                    var q = await download.Download(symbol, interval, timeout);

                    if (q == null || q.Count == 0)
                    {
                        return(false);
                    }

                    var fn          = this.GetQuoteFileName(symbol, interval, 0);
                    var writestream = new FileStream(fn, FileMode.OpenOrCreate);
                    q.AppendStream(writestream);
                    writestream.Close();

                    OnQuoteSaved?.Invoke(this, this.Exchange, fn);
                }

                return(true);
            }
        }
        //file naming convention: exchange_symbol_interval_index.txt
        public bool Save(IQuoteBasicBase quote, int numBarsToRemoveGap = -1)
        {
            lock (this)
            {
                if (quote == null || quote.Count == 0)
                {
                    return(false);
                }
                var files = this.FindQuoteFiles(quote.Symbol, quote.Interval);

                if (files != null && files.Count > 0)
                {
                    files.Sort();
                    var q1 = QuoteBasicExension.LoadFile(files.Last());

                    if (numBarsToRemoveGap > 0)
                    {
                        for (int i = Math.Max(0, q1.Count - numBarsToRemoveGap); i < q1.Count - 1; i++)
                        {
                            if (q1.Time[i + 1] - q1.Time[i] > q1.Interval)
                            {
                                q1.Clear(i + 1, q1.Count - 1);
                                break;
                            }
                        }
                    }

                    //var readtream = new FileStream(files.Last(), FileMode.Open);
                    //var q1 = QuoteBasicBase.InitByStream(readtream);
                    //readtream.Close();

                    if (quote.LastTime <= q1.LastTime)
                    {
                        return(false);
                    }

                    if (q1.Count < _maxNumBarsInFile)
                    {
                        var fn = files.Last();
                        q1.Append(quote);
                        q1.SaveToFile(fn);
                        //var writestream = new FileStream(files.Last(), FileMode.Truncate);
                        //q1.AppendStream(writestream);
                        //writestream.Close();
                        OnQuoteSaved?.Invoke(this, this.Exchange, fn);
                    }
                    else
                    {
                        var last  = files.Last().Split('_').Last();
                        var index = int.Parse(last.Split('.')[0]);
                        var fn    = this.GetQuoteFileName(quote.Symbol, quote.Interval, index + 1);

                        //var writestream = new FileStream(fn, FileMode.OpenOrCreate);
                        if (q1.LastTime >= quote.FirstTime)  //remove redundant sticks from new files
                        {
                            var sind = quote.FindIndexForGivenTime(q1.LastTime);
                            var q2   = new QuoteBasicBase(quote);
                            q2.Clear(0, sind);
                            q2.SaveToFile(fn);
                            //q2.AppendStream(writestream);
                        }
                        else
                        {
                            quote.SaveToFile(fn);
                            //quote.AppendStream(writestream);
                        }
                        //writestream.Close();
                        OnQuoteSaved?.Invoke(this, this.Exchange, fn);
                    }
                }
                else
                {
                    var fn = this.GetQuoteFileName(quote.Symbol, quote.Interval, 0);
                    quote.SaveToFile(fn);
                    //var writestream = new FileStream(fn, FileMode.OpenOrCreate);
                    //quote.AppendStream(writestream);
                    //writestream.Close();
                    OnQuoteSaved?.Invoke(this, this.Exchange, fn);
                }

                return(true);
            }
        }