private static Dictionary <string, List <float> > GetTickersRates(List <string> tickers, DateTime dateStart,
                                                                          int intervalMinutes, List <string> errors)
        {
            var curs = new BacktestTickerCursor();

            if (!curs.SetupCursor(string.Format("{0}{1}", ExecutablePath.ExecPath, TerminalEnvironment.QuoteCacheFolder),
                                  tickers, dateStart))
            {
                errors.Add(Localizer.GetString("MessageErrorGettingQuotesFromFiles"));
                return(null);
            }
            var rates = tickers.ToDictionary(t => t, t => new List <float>());

            try
            {
                curs.MoveToTime(dateStart);
                // выйти на некоторую начальную точку, в которой представлены все валютные пары
                var lastTime = DateTime.MinValue;
                var quotes   = new List <Cortege2 <string, CandleData> >();
                while (true)
                {
                    if (!curs.MoveNext())
                    {
                        break;
                    }
                    quotes = curs.GetCurrentQuotes();
                    if (quotes.Count < tickers.Count)
                    {
                        continue;                               // !!
                    }
                    lastTime = quotes[0].b.timeOpen;
                    break;
                }
                if (quotes.Count < tickers.Count)
                {
                    var nullTickers = tickers.Where(t => !quotes.Any(q => q.a == t));
                    errors.Add(string.Format(Localizer.GetString("MessageNoQuotesForTickers") + ": {0}",
                                             string.Join(", ", nullTickers)));
                    return(null);
                }

                // считать приращения
                while (curs.MoveNext())
                {
                    var newQuotes = curs.GetCurrentQuotes();
                    var time      = newQuotes[0].b.timeOpen;
                    if ((time - lastTime).TotalMinutes < intervalMinutes)
                    {
                        continue;
                    }
                    lastTime = time;
                    for (var i = 0; i < quotes.Count; i++)
                    {
                        rates[quotes[i].a].Add((newQuotes[i].b.open - quotes[i].b.open) / quotes[i].b.open);
                    }
                    quotes = newQuotes;
                }
            }
            catch (Exception ex)
            {
                errors.Add(Localizer.GetString("MessageErrorReadingQuotes"));
                Logger.Error("GetTickersRates error", ex);
            }
            finally
            {
                curs.Close();
            }
            if (rates.Count == 0 || rates[tickers[0]].Count == 0)
            {
                errors.Add(Localizer.GetString("MessageErrorCalcDeltas"));
            }
            return(rates);
        }
Exemple #2
0
        public static void BuildTrack(int accountId, string targetFilePath, int intervalMinutes)
        {
            var logger = new LimitedLogger();

            var positionsOpened = new List <MarketOrder>();
            var positionClosed  = new List <MarketOrder>();
            List <BalanceChange> balances;
            string accountCurrency;

            using (var db = new TradeSharpConnection())
            {
                accountCurrency = db.ACCOUNT.First(a => a.ID == accountId).Currency;
                balances        = db.BALANCE_CHANGE.Where(bc => bc.AccountID == accountId).OrderBy(b => b.ValueDate).ToList().Select(
                    LinqToEntity.DecorateBalanceChange).ToList();
                positionsOpened = db.POSITION.Where(bc => bc.AccountID == accountId).ToList().Select(LinqToEntity.DecorateOrder).ToList();
                positionClosed  = db.POSITION_CLOSED.Where(bc => bc.AccountID == accountId).ToList().Select(LinqToEntity.DecorateOrder).ToList();
            }

            if (balances.Count < 2)
            {
                return;
            }

            var startTime = balances.Min(b => b.ValueDate);

            startTime = startTime.Date.AddMinutes(intervalMinutes *
                                                  ((int)(startTime - startTime.Date).TotalMinutes) / intervalMinutes);
            var endTime = DateTime.Now;

            var dueDeals = positionsOpened.Union(positionClosed).OrderBy(p => p.TimeEnter).ToList();

            var cursor  = new BacktestTickerCursor();
            var tickers = dueDeals.Select(p => p.Symbol).Distinct().ToList();

            startTime = TimeUtil.RoundTime(startTime, intervalMinutes);
            var track = new Strategy(startTime, (int)balances.First().SignedAmountDepo);

            try
            {
                cursor.SetupCursor(ExecutablePath.ExecPath + "\\quotes", tickers, startTime);
                decimal balance = 0;

                for (var time = startTime; time <= endTime; time = time.AddMinutes(intervalMinutes))
                {
                    if (DaysOff.Instance.IsDayOff(time))
                    {
                        continue;
                    }
                    var candles = cursor.MoveToTime(time);
                    var quotes  = candles.ToDictionary(
                        c => c.Key,
                        c => new QuoteData(c.Value.close, c.Value.close, c.Value.timeClose));

                    // учесть вводы - выводы
                    var deltaBalance = 0M;
                    var deltaDeposit = 0M;
                    var closedProfit = 0M;
                    for (var i = 0; i < balances.Count; i++)
                    {
                        if (balances[i].ValueDate > time)
                        {
                            break;
                        }
                        deltaBalance += balances[i].SignedAmountDepo;
                        if (balances[i].ChangeType == BalanceChangeType.Profit ||
                            balances[i].ChangeType == BalanceChangeType.Loss)
                        {
                            closedProfit += balances[i].SignedAmountDepo;
                        }
                        else
                        {
                            deltaDeposit += balances[i].SignedAmountDepo;
                        }
                        balances.RemoveAt(i);
                        i--;
                    }
                    balance += deltaBalance;

                    // открытый профит
                    var openProfit  = 0M;
                    var volumesDepo = new Dictionary <string, decimal>();

                    for (var i = 0; i < dueDeals.Count; i++)
                    {
                        if (dueDeals[i].TimeEnter > time)
                        {
                            break;
                        }
                        if (dueDeals[i].TimeExit.HasValue && dueDeals[i].TimeExit.Value <= time)
                        {
                            dueDeals.RemoveAt(i);
                            i--;
                            continue;
                        }
                        // профит по сделке на момент
                        var profit = DalSpot.Instance.CalculateProfitInDepoCurrency(dueDeals[i],
                                                                                    quotes,
                                                                                    accountCurrency);
                        if (profit.HasValue)
                        {
                            openProfit += (decimal)profit.Value;
                        }
                        else
                        {
                            logger.Log(LogEntryType.Error, "CONV", 20, "Невозможно конвертировать профит сделки {0}",
                                       dueDeals[i].ToStringShort() + " @" + dueDeals[i].TimeEnter.ToStringUniform());
                        }
                        // объем
                        string errStr;
                        var    volumeDepo = DalSpot.Instance.ConvertToTargetCurrency(dueDeals[i].Symbol, true,
                                                                                     accountCurrency, dueDeals[i].Volume, quotes, out errStr, true) ?? 0;
                        if (!string.IsNullOrEmpty(errStr))
                        {
                            logger.Log(LogEntryType.Error, "CONV", 20, "Невозможно конвертировать объем сделки {0}: {1}",
                                       dueDeals[i].ToStringShort() + " @" + dueDeals[i].TimeEnter.ToStringUniform(), errStr);
                        }

                        volumeDepo *= dueDeals[i].Side;

                        if (volumesDepo.ContainsKey(dueDeals[i].Symbol))
                        {
                            volumesDepo[dueDeals[i].Symbol] += volumeDepo;
                        }
                        else
                        {
                            volumesDepo.Add(dueDeals[i].Symbol, volumeDepo);
                        }
                    }

                    var sumVolumesDepo = Math.Abs(volumesDepo.Sum(v => v.Value));

                    track.AddRecord(time, balance, balance + openProfit, sumVolumesDepo, deltaDeposit, closedProfit);
                }
            }
            finally
            {
                cursor.Close();
            }

            // сохранить трек
            using (var sw = new StreamWriter(targetFilePath, false, new System.Text.UTF8Encoding(false)))
                track.Serialize(sw);
        }
        private AccountStatistics BuildEquityCurve(
            List <MarketOrder> orders,
            List <MarketOrder> openOrders,
            List <BalanceChange> balanceChanges)
        {
            var stat = new AccountStatistics
            {
                Statistics = new PerformerStat
                {
                    DealsCount = orders.Count + openOrders.Count,
                },
                DealsStillOpened = openOrders.Count,
                listEquity       = new List <EquityOnTime>()
            };

            if (worker.CancellationPending)
            {
                return(stat);
            }
            SetProgressValueSafe(40, Localizer.GetString("MessageActualizingQuoteHistory") + "...");

            if (balanceChanges.Count == 0)
            {
                MessageBox.Show(Localizer.GetString("MessageNoTransfersData"));
                return(stat);
            }
            var curxDepo = AccountStatus.Instance.AccountData.Currency;
            // запросить котировки, если стоит соотв. флаг
            var quotesDic = UpdateQuotesForStats(orders, openOrders, curxDepo);

            // построить кривую эквити
            // отсчет от первого движения на счете либо от стартовой даты
            balanceChanges = balanceChanges.OrderBy(bc => bc.ValueDate).ToList();
            var startDate = cbStartFrom.Checked ? dpStart.Value : balanceChanges[0].ValueDate;

            // начальный баланс
            var initialBalance = !cbStartFrom.Checked ? balanceChanges[0].SignedAmountDepo
                : balanceChanges.Where(bc => bc.ValueDate <= startDate).Sum(bc => bc.SignedAmountDepo);

            // движения от начального баланса с заданным интервалом дискретизации
            var dueBalances = balanceChanges.Where(bc => bc.ValueDate > startDate).ToList();
            var dueDeals    = orders.Union(openOrders).OrderBy(o => o.TimeEnter).ToList();

            var endDate = DateTime.Now;
            var balance = initialBalance;

            var cursor = new BacktestTickerCursor();
            var path   = ExecutablePath.ExecPath + TerminalEnvironment.QuoteCacheFolder;

            if (!cursor.SetupCursor(path, quotesDic.Keys.ToList(), quotesDic.Min(t => t.Value)))
            {
                MessageBox.Show(Localizer.GetString("MessageErrorGettingQuotesFromFiles"));
                return(stat);
            }
            var currentQuotes    = new Dictionary <string, QuoteData>();
            var timeframeMinutes = Timeframe;

            SetProgressValueSafe(60, Localizer.GetString("MessageCalculationInProcess") + "...");
            try
            {
                for (var time = startDate; time < endDate; time = time.AddMinutes(timeframeMinutes))
                {
                    if (worker.CancellationPending)
                    {
                        return(stat);
                    }
                    // получить баланс на дату
                    for (var i = 0; i < dueBalances.Count; i++)
                    {
                        if (dueBalances[i].ValueDate > time)
                        {
                            break;
                        }
                        balance += dueBalances[i].SignedAmountDepo;
                        dueBalances.RemoveAt(i);
                        i--;
                    }
                    var equity = balance;

                    // получить текущие котировки
                    var candles = cursor.MoveToTime(time);
                    foreach (var candle in candles)
                    {
                        var quote = new QuoteData(candle.Value.open,
                                                  DalSpot.Instance.GetAskPriceWithDefaultSpread(candle.Key,
                                                                                                candle.Value.open),
                                                  candle.Value.timeOpen);

                        if (currentQuotes.ContainsKey(candle.Key))
                        {
                            currentQuotes[candle.Key] = quote;
                        }
                        else
                        {
                            currentQuotes.Add(candle.Key, quote);
                        }
                    }

                    // уточнить результат по открытым позициям
                    for (var i = 0; i < dueDeals.Count; i++)
                    {
                        if (worker.CancellationPending)
                        {
                            return(stat);
                        }
                        if (dueDeals[i].TimeExit.HasValue)
                        {
                            if (dueDeals[i].TimeExit.Value <= time)
                            {
                                dueDeals.RemoveAt(i);
                                i--;
                                continue;
                            }
                        }
                        var deal = dueDeals[i];
                        if (deal.TimeEnter <= time)
                        {
                            // посчитать текущий результат сделки
                            // в контрвалюте
                            if (!currentQuotes.ContainsKey(deal.Symbol))
                            {
                                continue;
                            }
                            var dealTickerQuote = currentQuotes[deal.Symbol];
                            var dealTickerPrice = deal.Side > 0 ? dealTickerQuote.bid : dealTickerQuote.ask;
                            var dealResult      = deal.Volume * deal.Side * (dealTickerPrice - deal.PriceEnter);

                            // перевод прибыли в валюту депо
                            bool inverse, areSame;
                            var  dealTransferSymbol = DalSpot.Instance.FindSymbol(deal.Symbol,
                                                                                  false, curxDepo, out inverse,
                                                                                  out areSame);
                            float?baseDepoRate = null;
                            if (areSame)
                            {
                                baseDepoRate = 1f;
                            }
                            else
                            {
                                if (!string.IsNullOrEmpty(dealTransferSymbol))
                                {
                                    if (currentQuotes.ContainsKey(dealTransferSymbol))
                                    {
                                        var quote = currentQuotes[dealTransferSymbol];
                                        if (quote.time >= time)
                                        {
                                            baseDepoRate = !inverse
                                                               ? (deal.Side > 0 ? quote.bid : quote.ask)
                                                               : (deal.Side > 0 ? 1 / quote.ask : 1 / quote.bid);
                                        }
                                    }
                                }
                            }
                            if (!baseDepoRate.HasValue)
                            {
                                continue;
                            }
                            dealResult *= baseDepoRate.Value;
                            equity     += (decimal)dealResult;
                        }
                    } // for (deal ...
                    // сохранить отметку - время/доходность
                    stat.listEquity.Add(new EquityOnTime((float)equity, time));
                } // for (time ... += Timeframe
            }
            finally
            {
                cursor.Close();
            }
            // если история начинается с пустого депо - изъять эти строки из истории
            var firstNotEmpty = stat.listEquity.FindIndex(e => e.equity > 0);

            if (firstNotEmpty == stat.listEquity.Count - 1)
            {
                stat.listEquity.Clear();
            }
            else
            if (firstNotEmpty > 0)
            {
                stat.listEquity.RemoveRange(0, firstNotEmpty);
            }

            SetProgressValueSafe(70,
                                 Localizer.GetString("MessageBuildingEquityChart") + "...");
            stat.Calculate(balanceChanges, openOrders, startDate);
            return(stat);
        }
Exemple #4
0
        private void StartRoutineDoWork(object sender, DoWorkEventArgs e)
        {
            // загрузить историю котировок и отработать старт роботов
            var quotesToReceive = GetUsedTickersAndStartTime();

            if (quotesToReceive.Count == 0)
            {
                return;
            }

            var firstDate = quotesToReceive.Min(q => q.Value);

            if ((DateTime.Now - firstDate).TotalMinutes < MinMinutesToLoadQuotes)
            {
                return;
            }
            // обновить кэш котировок
            updateTickersCacheForRobots(quotesToReceive, 3);

            Logger.InfoFormat("Портфель роботов: загрузка {0} котировок c {1:dd.MM.yyyy HH:mm}",
                              quotesToReceive.Count, firstDate);
            // закачать котировки и "скормить" их роботам
            var cursor = new BacktestTickerCursor();
            // !! path !!
            var path = ExecutablePath.ExecPath + "\\quotes";

            try
            {
                if (!cursor.SetupCursor(path, quotesToReceive.Keys.ToList(), firstDate))
                {
                    Logger.DebugFormat("Старт портфеля из {0} роботов: не удалось установить курсор, путь \"{1}\"",
                                       robots.Count, path);
                    return;
                }
                do
                {
                    var candles = cursor.GetCurrentQuotes();
                    if (candles.Count == 0)
                    {
                        break;
                    }

                    var names  = candles.Select(q => q.a).ToArray();
                    var prices = candles.Select(q =>
                                                new CandleDataBidAsk(q.b, DalSpot.Instance.GetDefaultSpread(q.a))).ToArray();
                    // дать роботам котировки в работу
                    foreach (var robot in robots)
                    {
                        var robotEvents = robot.OnQuotesReceived(names, prices, true);
                        if (robotEvents == null || robotEvents.Count == 0)
                        {
                            continue;
                        }
                        ShowRobotEventsSafe(robot, robotEvents);
                    }
                } while (cursor.MoveNext());
            }
            finally
            {
                cursor.Close();
            }
        }
Exemple #5
0
        public static void OrderCalcWorkerOnDoWork(object senderWorker, DoWorkEventArgs argsWithTicker)
        {
            var worker = (BackgroundWorker)senderWorker;

            var ticker  = (string)argsWithTicker.Argument;
            var account = AccountStatus.Instance.AccountData;

            if (account == null)
            {
                return;
            }

            var orders = new List <MarketOrder>();

            try
            {
                // получить закрытые ордера порциями
                while (true)
                {
                    if (worker.CancellationPending)
                    {
                        return;
                    }
                    var startId    = orders.Count == 0 ? 0 : orders.Max(o => o.ID);
                    var ordersPart = TradeSharpAccount.Instance.proxy.GetClosedOrders(AccountStatus.Instance.accountID,
                                                                                      ticker, startId, 200) ?? new List <MarketOrder>();
                    if (ordersPart.Count == 0)
                    {
                        break;
                    }
                    orders.AddRange(ordersPart);
                }

                // получить открытые ордера
                if (worker.CancellationPending)
                {
                    return;
                }
                List <MarketOrder> openedOrders;
                TradeSharpAccount.Instance.proxy.GetMarketOrders(AccountStatus.Instance.accountID, out openedOrders);
                if (openedOrders != null && openedOrders.Count > 0)
                {
                    openedOrders = openedOrders.Where(o => o.Symbol == ticker).ToList();
                    if (openedOrders.Count > 0)
                    {
                        CalculateOpenedOrdersResult(openedOrders, account.Currency);
                        orders.AddRange(openedOrders);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка получения ордеров", ex);
                MessageBox.Show(Localizer.GetString("MessageErrorGettingOrders"),
                                Localizer.GetString("TitleError"),
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (orders.Count == 0)
            {
                return;
            }

            var balanceOnDate = new BalanceAndEquitySeriesData();
            var startDate     = orders.Min(o => o.TimeEnter).Date;
            var endDate       = DateTime.Now;

            orders.ForEach(o =>
            {
                o.TimeExit = o.TimeExit ?? endDate;
                o.State    = PositionState.Opened;
            });
            orders = orders.Where(o => o.TimeExit.HasValue).OrderBy(o => o.TimeExit).ToList();

            //==============================================
            // получить изменения баланса
            if (worker.CancellationPending)
            {
                return;
            }
            List <BalanceChange> balanceChanges;
            var status = TradeSharpAccount.Instance.proxy.GetBalanceChanges(account.ID, null, out balanceChanges);

            if (status != RequestStatus.OK || balanceChanges == null)
            {
                MessageBox.Show(string.Format(Localizer.GetString("MessageUnableToGetTransfersHistory") + ": {0}",
                                              EnumFriendlyName <RequestStatus> .GetString(status)));
                return;
            }
            var dueBalances = balanceChanges.Where(bc =>
                                                   bc.ChangeType == BalanceChangeType.Deposit || bc.ChangeType == BalanceChangeType.Withdrawal)
                              .OrderBy(x => x.ValueDate)
                              .ToList();
            //==============================================

            var balance = 0f;
            // установить "курсор" котировок на начало наблюдаемого отрезка
            var cursor  = new BacktestTickerCursor();
            var tickers = new List <string> {
                ticker
            };
            // нужна еще валюта пересчета?
            bool inverse, pairsEqual;
            var  tickerCounterDepo = DalSpot.Instance.FindSymbol(ticker, false, account.Currency,
                                                                 out inverse, out pairsEqual);

            if (!string.IsNullOrEmpty(tickerCounterDepo) && tickerCounterDepo != ticker)
            {
                tickers.Add(tickerCounterDepo);
            }
            cursor.SetupCursor(ExecutablePath.ExecPath + TerminalEnvironment.QuoteCacheFolder, tickers, startDate);
            try
            {
                for (var date = startDate; ;)
                {
                    if (date != endDate && DaysOff.Instance.IsDayOff(date))
                    {
                        date = date.AddDays(1);
                        continue;
                    }

                    for (var i = 0; i < dueBalances.Count; i++)
                    {
                        if (dueBalances[i].ValueDate > date)
                        {
                            break;
                        }
                        balance += (float)dueBalances[i].SignedAmountDepo;
                        dueBalances.RemoveAt(i);
                        i--;
                    }

                    for (var i = 0; i < orders.Count; i++)
                    {
                        if (orders[i].TimeExit > date)
                        {
                            break;
                        }
                        balance += orders[i].ResultDepo;
                        orders.RemoveAt(i);
                        i--;
                    }

                    // получить текущий результат по открытым на момент ордерам
                    var equity    = balance;
                    var curDate   = date;
                    var curOrders = orders.Where(o => o.TimeEnter <= curDate).ToList();
                    var curProfit = 0f;

                    if (curOrders.Count > 0)
                    {
                        cursor.MoveToTime(date);

                        var quotes = cursor.GetCurrentQuotes().ToDictionary(c => c.a,
                                                                            c => new QuoteData(c.b.close, c.b.close, curDate));

                        curProfit = DalSpot.Instance.CalculateOpenedPositionsCurrentResult(curOrders,
                                                                                           quotes, account.Currency);
                        equity += curProfit;


                        var errors = new List <string>();
                        var exp    = DalSpot.Instance.CalculateExposure(curOrders,
                                                                        quotes, account.Currency, errors);

                        if (errors.Count == 0)
                        {
                            var leverage = (float)Math.Abs(exp) / equity;
                            balanceOnDate.lstLeverage.Add(new TimeBalans(date, leverage));
                        }
                    }

                    balanceOnDate.lstBalance.Add(new TimeBalans(date, balance));
                    balanceOnDate.lstEquity.Add(new TimeBalans(date, equity));
                    balanceOnDate.lstDrawDown.Add(new TimeBalans(date, curProfit < 0 ? curProfit : 0));

                    if (date == endDate)
                    {
                        break;
                    }
                    date = date.AddDays(1);
                    if (date > endDate)
                    {
                        date = endDate;
                    }
                }
            }
            finally
            {
                cursor.Close();
            }
            // сместить последнюю дату кривой баланса
            balanceOnDate.ShiftLastDate();
            argsWithTicker.Result = balanceOnDate;
        }
Exemple #6
0
        private void BtnMakeIndexClick(object sender, EventArgs e)
        {
            ExpressionResolver resv;

            try
            {
                resv = new ExpressionResolver(tbTickerFormula.Text);
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка разбора выражения", ex);
                MessageBox.Show(string.Format("Ошибка разбора выражения ({0})", ex.Message));
                return;
            }

            if (saveFileDialog.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            var fileName = saveFileDialog.FileName;

            var names = resv.GetVariableNames();
            var curs  = new BacktestTickerCursor();

            try
            {
                curs.SetupCursor(quoteFolder, names, dpStart.Value);
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка установки курсора", ex);
                return;
            }

            StreamWriter sw;

            try
            {
                sw = new StreamWriter(fileName);
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка открытия файла на запись", ex);
                curs.Close();
                MessageBox.Show("Ошибка открытия файла на запись");
                return;
            }
            var saver = new QuoteSaver(tbTicker.Text);

            try
            {
                while (true)
                {
                    // посчитать индекс
                    var quotes = curs.GetCurrentQuotes();
                    if (quotes.Count == 0)
                    {
                        continue;
                    }
                    var date = quotes.Max(q => q.b.time);

                    var    quoteDic = quotes.ToDictionary(q => q.a, q => (double)q.b.bid);
                    double result;
                    resv.Calculate(quoteDic, out result);

                    // занести индекс в файл
                    saver.SaveQuote(sw, (float)result, (float)result, date);

                    if (!curs.MoveNext())
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка формирования индекса", ex);
            }
            finally
            {
                curs.Close();
                sw.Close();
            }

            MessageBox.Show("Формирование индекса завершено");
            openFileDialog.FileName = saveFileDialog.FileName;
        }