private static void TryCloseOpenTrades(TradeWithIndexingCollection trades, Candle m1Candle) { if (!trades.AnyOpen) { return; } foreach (var trade in trades.OpenTrades) { if (trade.Trade.EntryDateTime != null && m1Candle.OpenTimeTicks >= trade.Trade.EntryDateTime.Value.Ticks) { trade.Trade.SimulateTrade(m1Candle, out _); } if (trade.Trade.CloseDateTime != null) { trades.MoveOpenToClose(trade); } } }
private void LogProgress(TradeWithIndexingCollection trades, long currentDateTimeTicks) { var closedTrades = new List <Trade>(5000); closedTrades.AddRange(trades.ClosedTrades.Select(x => x.Trade)); var orderTrades = new List <Trade>(5000); orderTrades.AddRange(trades.OrderTradesAsc.Select(x => x.Trade)); var tradesForExpectancy = closedTrades.Where(x => x.RMultiple != null).ToList(); var expectancy = TradingCalculator.CalculateExpectancy(tradesForExpectancy); Log.Info( $"{_market.Name} Up to: {new DateTime(currentDateTimeTicks): dd-MM-yy HH:mm} "// /* {r.PercentComplete:0.00}% */. Running: {r.SecondsRunning}s. " + $"Created: {orderTrades.Count() + closedTrades.Count + trades.OpenTrades.Count()} " + $"Open: {trades.OpenTrades.Count()}. Orders: {orderTrades.Count()} " + $"Closed: {closedTrades.Count} (Hit stop: {closedTrades.Count(x => x.CloseReason == TradeCloseReason.HitStop)} " + $"Hit limit: {closedTrades.Count(x => x.CloseReason == TradeCloseReason.HitLimit)} " + $"Hit expiry: {closedTrades.Count(x => x.CloseReason == TradeCloseReason.HitExpiry)} " + $"Cached trades: {trades.CachedTradesCount}) " + $"Expectancy: {expectancy:0.00}"); }
private static void FillOrders(TradeWithIndexingCollection trades, Candle m1Candle) { foreach (var order in trades.OrderTradesAsc) { var candleCloseTimeTicks = m1Candle.CloseTimeTicks; if (order.Trade.OrderDateTime != null && candleCloseTimeTicks < order.Trade.OrderDateTime.Value.Ticks) { break; } order.Trade.SimulateTrade(m1Candle, out _); if (order.Trade.EntryDateTime != null) { trades.MoveOrderToOpen(order); } else if (order.Trade.CloseDateTime != null) { trades.MoveOrderToClosed(order); } } }
public List <Trade> Run(StrategyBase strategy, Func <bool> getShouldStopFunc, DateTime?startTime = null, DateTime?endTime = null) { var logIntervalSeconds = 5; var candleTimeframes = strategy.Timeframes; var smallestNonM1Timeframe = candleTimeframes.First(); var candleTimeframesExcSmallest = candleTimeframes.Where(x => x != smallestNonM1Timeframe).ToList(); var m1Candles = _candleService.GetCandles(_broker, _market.Name, Timeframe.M1, false); var allCandles = GetCandles(candleTimeframes); var timeframeCandleIndexes = new TimeframeLookup <int>(); var currentCandles = new TimeframeLookup <List <Candle> >(); var trades = new TradeWithIndexingCollection(); var nextLogTime = DateTime.UtcNow.AddSeconds(logIntervalSeconds); var calls = 0; strategy.SetSimulationParameters(trades, currentCandles, _market); foreach (var tf in candleTimeframes) { currentCandles[tf] = new List <Candle>(10000); } var smallestNonM1TimeframeCount = allCandles[smallestNonM1Timeframe].Count; var m1CandleIndex = 0; Candle smallestNonM1Candle; var startTimeTicks = startTime != null ? (long?)startTime.Value.Ticks : null; var endTimeTicks = endTime != null ? (long?)endTime.Value.Ticks : null; strategy.SetInitialised(); // Ignore M1 candles for (var smallestNonM1TfIndex = 0; smallestNonM1TfIndex < smallestNonM1TimeframeCount; smallestNonM1TfIndex++) { if (getShouldStopFunc != null && getShouldStopFunc()) { return(null); } // Progress smallest non-M1 candle smallestNonM1Candle = allCandles[smallestNonM1Timeframe][smallestNonM1TfIndex]; currentCandles[smallestNonM1Timeframe].Add(smallestNonM1Candle); var newCandleTimeframes = new List <Timeframe> { smallestNonM1Timeframe }; if (DateTime.UtcNow > nextLogTime) { LogProgress(trades, smallestNonM1Candle.CloseTimeTicks); nextLogTime = DateTime.UtcNow.AddSeconds(logIntervalSeconds); } // Progress other timeframes foreach (var tf in candleTimeframesExcSmallest) { for (var i = timeframeCandleIndexes[tf]; i < allCandles[tf].Count; i++) { var c = allCandles[tf][i]; if (c.CloseTimeTicks <= smallestNonM1Candle.CloseTimeTicks) { currentCandles[tf].Add(c); newCandleTimeframes.Add(tf); timeframeCandleIndexes[tf] = i + 1; } else { break; } } } // Process M1 candles if any trades are open or and orders if ((trades.AnyOpen || trades.AnyOrders) && m1CandleIndex < m1Candles.Count) { // Include M1 candles var nextIndex = m1Candles.BinarySearchGetItem( i => m1Candles[i].CloseTimeTicks, m1CandleIndex, smallestNonM1TfIndex > 0 ? allCandles[smallestNonM1Timeframe][smallestNonM1TfIndex - 1].CloseTimeTicks : smallestNonM1Candle.OpenTimeTicks, BinarySearchMethod.NextHigherValue); if (nextIndex != -1 && m1Candles[nextIndex].CloseTimeTicks <= m1Candles[m1CandleIndex].CloseTimeTicks) { throw new ApplicationException("M1 candles are not running in order"); } if (nextIndex != -1 && m1Candles[nextIndex].CloseTimeTicks <= smallestNonM1Candle.CloseTimeTicks) { m1CandleIndex = nextIndex; for (var i = m1CandleIndex; i < m1Candles.Count; i++) { var m1 = m1Candles[i]; if (m1.CloseTimeTicks > smallestNonM1Candle.CloseTimeTicks) { break; } m1CandleIndex = i; if (!trades.AnyOpen && !trades.AnyOrders) { break; } if (trades.AnyOrders) { FillOrders(trades, m1); } if (trades.AnyOpen) { TryCloseOpenTrades(trades, m1); calls++; } } } } // Process new completed candles in strategy try { strategy.UpdateIndicators(newCandleTimeframes); strategy.NewTrades.Clear(); if (startTimeTicks == null || (smallestNonM1Candle.CloseTimeTicks >= startTimeTicks && smallestNonM1Candle.CloseTimeTicks <= endTimeTicks)) { strategy.ProcessCandles(newCandleTimeframes); } } catch (Exception ex) { Log.Error("Error processing new candles", ex); return(null); } RemoveInvalidTrades(strategy.NewTrades, smallestNonM1Candle.CloseBid, smallestNonM1Candle.CloseAsk); // Add new trades foreach (var t in strategy.NewTrades) { if (t.CloseDateTime != null) { trades.AddClosedTrade(t); } else if (t.EntryDateTime == null && t.OrderDateTime != null) { trades.AddOrderTrade(t); } else if (t.EntryDateTime != null) { trades.AddOpenTrade(t); } } } LogProgress(trades, allCandles[smallestNonM1Timeframe][allCandles[smallestNonM1Timeframe].Count - 1].CloseTimeTicks); var ret = trades.AllTrades.Select(x => x.Trade).ToList(); foreach (var t in ret) { TradeCalculator.UpdateInitialStopPips(t); TradeCalculator.UpdateInitialLimitPips(t); TradeCalculator.UpdateRMultiple(t); } return(ret); }
public ReadOnlyTradeCollection(TradeWithIndexingCollection trades) { _trades = trades; }