Пример #1
0
 public void FinalizeTest()
 {
     if (testCursor != null)
     {
         testCursor.Close();
     }
     testCursor          = null;
     AccountInfo.Balance = initialTestBalance;
     lastTestEnd         = DateTime.Now;
 }
Пример #2
0
 public void FinalizeTest()
 {
     if (testCursor != null)
     {
         finalCandles = testCursor.GetCurrentQuotes();
         testCursor.Close();
     }
     testCursor          = null;
     AccountInfo.Balance = initialTestBalance;
     lastTestEnd         = DateTime.Now;
     CalcProfitForOpenOrder();
 }
Пример #3
0
        public bool InitiateTest()
        {
            nextOrderId      = 1;
            lastTestStart    = DateTime.Now;
            firstDateOfTest  = null;
            startModelTime   = null;
            endModelTime     = null;
            hwm              = 0;
            maxAbsDrawdown   = 0;
            maxDailyDrawdown = 0;
            maxDrawdown      = 0;
            // начальная инициализация
            robotLogEntries.Clear();
            previousQuotes = new UnsafeStorage <string, QuoteData>();
            quotesStorage  = new UnsafeStorage <string, QuoteData>();
            InitTradeLib();

            // запомнить баланс торгового счета и вернуть его в исходное состояние
            // по завершению теста
            initialTestBalance    = AccountInfo.Balance;
            historyStartoffPassed = false;

            OnRobotMessage -= LogRobotMessages;
            if (LogRobots)
            {
                OnRobotMessage += LogRobotMessages;
            }

            // получить от гроботов используемые тикеры и старт истории
            // роботы сами указывают используемые тикеры, плюс также берутся тикеры из параметра Graphics
            var usedTickers = GetUserTickersAndStartTime();

            if (usedTickers.Count == 0)
            {
                Logger.DebugFormat("Бэк-тест портфеля из {0} роботов: не указаны используемые и торгуемые котировки",
                                   listRobots.Count);
                return(false);
            }

            if (UpdateTickerCache)
            {
                updateTickersCacheForRobots(usedTickers, 3);
            }

            // открыть файловые потоки
            testCursor = new BacktestTickerCursor();
            // !! path !!
            var path = ExecutablePath.ExecPath + "\\quotes";

            try
            {
                var cursorStart = usedTickers.Min(t => t.Value);
                if (!testCursor.SetupCursor(path, usedTickers.Keys.ToList(), cursorStart))
                {
                    testCursor.Close();
                    testCursor = null;
                    Logger.DebugFormat(
                        "Бэк-тест портфеля из {0} роботов: не удалось установить курсор, путь \"{1}\"",
                        listRobots.Count, path);
                    return(false);
                }
            }
            catch (Exception ex)
            {
                if (testCursor != null)
                {
                    testCursor.Close();
                }
                testCursor = null;
                Logger.Error("Ошибка в SetupTest", ex);
                return(false);
            }
            return(true);
        }
        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;
        }
Пример #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();

            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(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 < 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();
                    if (curOrders.Count > 0)
                    {
                        cursor.MoveToTime(date);
                        var curProfit = DalSpot.Instance.CalculateOpenedPositionsCurrentResult(curOrders,
                            cursor.GetCurrentQuotes().ToDictionary(c => c.a,
                                c => new QuoteData(c.b.close, c.b.close, curDate)), account.Currency);
                        equity += curProfit;
                    }

                    balanceOnDate.lstBalance.Add(new TimeBalans(date, balance));
                    balanceOnDate.lstEquity.Add(new TimeBalans(date, equity));

                    if (date == endDate) break;
                    date = date.AddDays(1);
                    if (date > endDate) date = endDate;
                }
            }
            finally
            {
                cursor.Close();
            }
            // сместить последнюю дату кривой баланса
            balanceOnDate.ShiftLastDate();
            argsWithTicker.Result = balanceOnDate;
        }
        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;
        }
Пример #7
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();
            }
        }
Пример #8
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();
            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))
                {
                    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);
        }