public void Evaluate() { if (!_evaluatable) { throw new InvalidOperationException("Evaluate() can be called only once"); } // initialize context _strategy.Initialize(_context, _strategyParameterValues); // Get all trading objects _allTradingObjects = _provider.GetAllTradingObjects(); // Get last warm up data periods _firstNonWarmupDataPeriods = _provider.GetFirstNonWarmupDataPeriods(); // evaluating var lastPeriodTime = DateTime.MinValue; Bar[] lastPeriodData = null; var periods = _provider.GetAllPeriodsOrdered(); bool fullTradingDataReached = false; for (var periodIndex = 0; periodIndex < periods.Length; ++periodIndex) { var thisPeriodTime = periods[periodIndex]; var thisPeriodData = _provider.GetDataOfPeriod(thisPeriodTime); if (thisPeriodData.Length != _allTradingObjects.Length) { throw new InvalidOperationException("the number of data returned does not match the number of trading object"); } // process pure warm up data. if (_firstNonWarmupDataPeriods.All(t => t > thisPeriodTime)) { // update metrics that registered by strategy _context.MetricManager.BeginUpdateMetrics(); _context.MetricManager.UpdateMetrics(_allTradingObjects, thisPeriodData); _context.MetricManager.EndUpdateMetrics(); continue; } // remove warm up data if necessary var originalThisPeriodData = thisPeriodData; if (!fullTradingDataReached) { if (_firstNonWarmupDataPeriods.All(t => t <= thisPeriodTime)) { // from now on, all data are useful trading data fullTradingDataReached = true; } else { thisPeriodData = new Bar[originalThisPeriodData.Length]; Array.Copy(originalThisPeriodData, thisPeriodData, thisPeriodData.Length); for (int i = 0; i < thisPeriodData.Length; ++i) { if (thisPeriodData[i].Time < _firstNonWarmupDataPeriods[i]) { thisPeriodData[i].Time = Bar.InvalidTime; } } } } // set current period data in context _context.SetCurrentPeriodData(thisPeriodData); // start a new period _strategy.StartPeriod(thisPeriodTime); // assign all instructions in next period to current period because new period starts _currentPeriodInstructions.Clear(); _currentPeriodInstructions.AddRange(_nextPeriodInstructions); _nextPeriodInstructions.Clear(); // run instructions left over from previous period RunCurrentPeriodInstructions(lastPeriodData, thisPeriodData, thisPeriodTime); #if DEBUG // check data if (thisPeriodData.Any(bar => bar.Time != Bar.InvalidTime && bar.Time != thisPeriodTime)) { throw new InvalidOperationException("Time in bar data is different with the time returned by data provider"); } #endif // update metrics that registered by strategy. // here we should always use original data to update metrics. _context.MetricManager.BeginUpdateMetrics(); _context.MetricManager.UpdateMetrics(_allTradingObjects, originalThisPeriodData); _context.MetricManager.EndUpdateMetrics(); // update position lasted period count foreach (var code in _context.GetAllPositionCodes()) { int tradingObjectIndex = _provider.GetIndexOfTradingObject(code); if (thisPeriodData[tradingObjectIndex].Time != Bar.InvalidTime) { foreach (var position in _context.GetPositionDetails(code)) { if (position.BuyTime < thisPeriodTime) { position.IncreaseLastedPeriodCount(); } } } } // evaluate bar data _strategy.Evaluate(_allTradingObjects, thisPeriodData); // get instructions and add them to pending instruction list var instructions = _strategy.RetrieveInstructions().ToArray(); if (instructions.Any(i => i.Action == TradingAction.OpenLong)) { // update active account only if there is open long instruction _activeAccountId++; _activeAccountId %= _numberOfAccounts; // decide if instructions should be excuted in current account if (_activeAccountId != _accountId) { // remove OpenLong instructions instructions = instructions.Where(i => i.Action != TradingAction.OpenLong).ToArray(); } } if (instructions.Any()) { foreach (var instruction in instructions) { UpdateInstructionWithDefaultPriceWhenNecessary(instruction); if (instruction.Price.Period == TradingPricePeriod.CurrentPeriod) { _currentPeriodInstructions.Add(instruction); } else if (instruction.Price.Period == TradingPricePeriod.NextPeriod) { _nextPeriodInstructions.Add(instruction); } else { throw new InvalidProgramException("unsupported price period"); } } // run instructions for current period RunCurrentPeriodInstructions(lastPeriodData, thisPeriodData, thisPeriodTime); } // end period _strategy.EndPeriod(); // reset current period data in context _context.SetCurrentPeriodData(null); // update last period time and data lastPeriodTime = thisPeriodTime; lastPeriodData = thisPeriodData; // update progress event if (OnEvaluationProgress != null) { OnEvaluationProgress( this, new EvaluationProgressEventArgs( thisPeriodTime, (double)(periodIndex + 1) / periods.Length)); } } // finish evaluation _strategy.Finish(); // Sell all equities forcibly. ClearEquityForcibly(lastPeriodTime); // clear all pending instructions. _currentPeriodInstructions.Clear(); _nextPeriodInstructions.Clear(); // update 'evaluatable' flag to avoid this function be called twice _evaluatable = false; }
public void Predicate() { if (!_predicatable) { throw new InvalidOperationException("Predicate() can be called only once"); } // initialize context _strategy.Initialize(_context, _strategyParameterValues); // Get all trading objects _allTradingObjects = _provider.GetAllTradingObjects(); // Get last warm up data periods _firstNonWarmupDataPeriods = _provider.GetFirstNonWarmupDataPeriods(); // evaluating var lastPeriodTime = DateTime.MinValue; Bar[] lastPeriodData = null; var periods = _provider.GetAllPeriodsOrdered(); bool fullTradingDataReached = false; for (var periodIndex = 0; periodIndex < periods.Length; ++periodIndex) { var thisPeriodTime = periods[periodIndex]; var thisPeriodData = _provider.GetDataOfPeriod(thisPeriodTime); if (thisPeriodData.Length != _allTradingObjects.Length) { throw new InvalidOperationException("the number of data returned does not match the number of trading object"); } // process pure warm up data. if (_firstNonWarmupDataPeriods.All(t => t > thisPeriodTime)) { // update metrics that registered by strategy _context.MetricManager.BeginUpdateMetrics(); _context.MetricManager.UpdateMetrics(_allTradingObjects, thisPeriodData); _context.MetricManager.EndUpdateMetrics(); continue; } // remove warm up data if necessary var originalThisPeriodData = thisPeriodData; if (!fullTradingDataReached) { if (_firstNonWarmupDataPeriods.All(t => t <= thisPeriodTime)) { // from now on, all data are useful trading data fullTradingDataReached = true; } else { thisPeriodData = new Bar[originalThisPeriodData.Length]; Array.Copy(originalThisPeriodData, thisPeriodData, thisPeriodData.Length); for (int i = 0; i < thisPeriodData.Length; ++i) { if (thisPeriodData[i].Time < _firstNonWarmupDataPeriods[i]) { thisPeriodData[i].Time = Bar.InvalidTime; } } } } // set current period data in context _context.SetCurrentPeriodData(thisPeriodData); // start a new period _strategy.StartPeriod(thisPeriodTime); // assign all instructions in next period to current period because new period starts _currentPeriodInstructions.Clear(); _currentPeriodInstructions.AddRange(_nextPeriodInstructions); _nextPeriodInstructions.Clear(); // run instructions left over from previous period RunCurrentPeriodInstructions(lastPeriodData, thisPeriodData, thisPeriodTime); #if DEBUG // check data if (thisPeriodData.Any(bar => bar.Time != Bar.InvalidTime && bar.Time != thisPeriodTime)) { throw new InvalidOperationException("Time in bar data is different with the time returned by data provider"); } #endif // update metrics that registered by strategy. // here we should always use original data to update metrics. _context.MetricManager.BeginUpdateMetrics(); _context.MetricManager.UpdateMetrics(_allTradingObjects, originalThisPeriodData); _context.MetricManager.EndUpdateMetrics(); // add active positions if (_unprocessedActivePositions.Any()) { var positions = _unprocessedActivePositions.Where(p => p.BuyTime <= thisPeriodTime); if (positions.Any()) { foreach (var position in positions) { _equityManager.ManualAddPosition(position); } _unprocessedActivePositions = _unprocessedActivePositions.Where(p => p.BuyTime > thisPeriodTime).ToList(); } } // evaluate bar data _strategy.Evaluate(_allTradingObjects, thisPeriodData); // get instructions and add them to pending instruction list var instructions = _strategy.RetrieveInstructions().ToArray(); if (instructions.Any()) { if (thisPeriodTime == periods[periods.Length - 1]) { _predicatedTransactions = instructions .Select(ins => BuildTransactionFromInstruction(ins, thisPeriodTime, thisPeriodData[ins.TradingObject.Index])) .ToList(); } else { foreach (var instruction in instructions) { UpdateInstructionWithDefaultPriceWhenNecessary(instruction); if (instruction.Price.Period == TradingPricePeriod.CurrentPeriod) { _currentPeriodInstructions.Add(instruction); } else if (instruction.Price.Period == TradingPricePeriod.NextPeriod) { _nextPeriodInstructions.Add(instruction); } else { throw new InvalidProgramException("unsupported price period"); } } // run instructions for current period RunCurrentPeriodInstructions(lastPeriodData, thisPeriodData, thisPeriodTime); } } // end period _strategy.EndPeriod(); // reset current period data in context _context.SetCurrentPeriodData(null); // update last period time and data lastPeriodTime = thisPeriodTime; lastPeriodData = thisPeriodData; } // finish evaluation _strategy.Finish(); // clear all pending instructions. _currentPeriodInstructions.Clear(); _nextPeriodInstructions.Clear(); // update flag to avoid this function be called twice _predicatable = false; }