public SimulationResult Get(string start, string end, int interval, [FromUri] StrategyParameters parameters)
        {
            var startDate         = DateTime.ParseExact(start, "yyyyMMdd", CultureInfo.InvariantCulture);
            var endDate           = DateTime.ParseExact(end, "yyyyMMdd", CultureInfo.InvariantCulture);
            var intervalInMinutes = interval;

            var result = Simulate(startDate, endDate, intervalInMinutes, parameters);

            return(result);
        }
        private void BollingerBandStrategy(OHLC ohlc, DateTime date, StrategyParameters parameters)
        {
            if (DataPoints.Count < 200)
            {
                return;
            }

            var rsiPeriod     = parameters.RsiPeriod;
            var rsiThreshold  = parameters.RsiThreshold;
            var stopLossRatio = parameters.StopLossRatio;

            //Calculate indicators
            var direction  = CalculateMovingAverage(200);
            var sellSignal = CalculateMovingAverage(5);
            var rsi        = CalculateRelativeStrengthIndex(rsiPeriod);
            var bb         = CalculateBollingerBands(20, 2);
            var percentB   = bb.PercentB;
            var bandwidth  = bb.Bandwidth;

            //Long Entry
            //When the price candle closes or is already above 200 day MA and RSI closes below 5 buy
            //Sell when closes above 5-period moving average
            if (State == TradingState.Initial)
            {
                if (ohlc.High > direction && percentB > 100)
                {
                    CreateOrder(ohlc, date, "Buy", $"<br/> %b {percentB:N0} <br/> bandwidth {bandwidth:N0} <br/> upper {bb.UpperBand:N0} <br/> lower {bb.LowerBand:N0}");
                    State = TradingState.MonitoringDownTrend;
                }
            }
            else if (State == TradingState.MonitoringDownTrend || State == TradingState.WaitingToSell)
            {
                //Stop loss when BUY order loses more than 2% of its value
                var buyOrder = Orders.Last();
                if ((double)((buyOrder.Price - ohlc.Close) / buyOrder.Price) > stopLossRatio)
                {
                    CreateOrder(ohlc, date, "Sell", $"Stop Loss <br/> %b {percentB:N0} <br/> bandwidth {bandwidth:N0} <br/> upper {bb.UpperBand:N0} <br/> lower {bb.LowerBand:N0}");
                    State = TradingState.Initial;
                    return;//Otherwise might sell twice
                }


                //Limit profit on downward swing
                if (percentB < 0)
                {
                    CreateOrder(ohlc, date, "Sell", $"%b hit over 100 <br/> %b {percentB:N0} <br/> bandwidth {bandwidth:N0} <br/> upper {bb.UpperBand:N0} <br/> lower {bb.LowerBand:N0}");
                    State = TradingState.Initial;
                    return;
                }
            }
        }
        private void DonchienBreakoutStrategy(OHLC ohlc, DateTime date, StrategyParameters parameters)
        {
            if (DataPoints.Count < 55)
            {
                return;
            }

            //Calculate indicators
            var dca = CalculateDonchianChannel(55);

            //Long Entry
            //When closes at DC upper limit
            if (State == TradingState.Initial)
            {
                if (ohlc.Close >= dca.UpperBand)
                {
                    CreateOrder(ohlc, date, "Buy", $"<br/> Close {ohlc.Close:N0} > Upper {dca.UpperBand:N0}");
                    State = TradingState.WaitingToSell;
                }
            }
            else if (State == TradingState.WaitingToSell)
            {
                //Stop loss when BUY order loses 70 EUR
                var buyOrder = Orders.Last();
                var loss     = buyOrder.Price - ohlc.Close;
                if (loss > 70)
                {
                    CreateOrder(ohlc, date, "Sell", $"Stop Loss <br/> at loss {loss:N0}");
                    State = TradingState.Initial;
                    return;//Otherwise might sell twice
                }

                //Exit when closes at mid point
                if (ohlc.Close <= (dca.UpperBand - dca.LowerBand) / 2 + dca.LowerBand)
                {
                    CreateOrder(ohlc, date, "Sell", $"Closes at mid point");
                    State = TradingState.Initial;
                    return;
                }
            }
        }
        public IHttpActionResult Get(string start, string end, int interval, [FromUri] StrategyParameters parameters)
        {
            var startDate         = DateTime.ParseExact(start, "yyyyMMdd", CultureInfo.InvariantCulture);
            var endDate           = DateTime.ParseExact(end, "yyyyMMdd", CultureInfo.InvariantCulture);
            var intervalInMinutes = interval;

            var exportLines = Export(startDate, endDate, intervalInMinutes, parameters);
            var export      = exportLines.Aggregate((sum, s) => sum + Environment.NewLine + s);

            var result = new HttpResponseMessage(HttpStatusCode.OK);

            result.Content = new StringContent(export);

            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = $"marketdata.export.{DateTime.Now:yyyMMddHHmmss}.csv"
            };

            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

            var response = ResponseMessage(result);

            return(response);
        }
        private void RelativeVigorIndexStrategy(OHLC ohlc, DateTime date, StrategyParameters parameters)
        {
            if (DataPoints.Count < 40)
            {
                return;
            }

            //Calculate indicators
            var rvi               = CalculateRelativeVigorIndex(22);
            var rviBase           = CalculateRelativeVigorIndex(10);
            var percentK          = CalculateSlowStochasticOscillatorsPercentK(DataPoints, 17, 6);
            var percentD          = CalculateSlowStochasticOscillatorsPercentD(DataPoints, 13, 17, 6);
            var envelopeLowerBand = CalculateEnvelopeLowerBand(DataPoints, 10, 0.97);

            //Long Entry
            //When the RVI is greater than the signal and
            //percent K is higher than percent D
            if (State == TradingState.Initial)
            {
                if (rvi > rviBase && percentK > percentD)
                {
                    CreateOrder(ohlc, date, "Buy", $"<br/> %b {rvi:N0} <br/> > {rviBase:N0} <br/> and {percentK:N0} <br/> > {percentD:N0}");
                    State = TradingState.WaitingToSell;
                }
            }
            else if (State == TradingState.WaitingToSell || State == TradingState.MonitoringDownTrend)
            {
                //Stop loss when BUY order loses 70 EUR
                var buyOrder = Orders.Last();
                //var loss = buyOrder.Price - ohlc.Close;
                //if (loss > 70)
                //{
                //    CreateOrder(ohlc, date, "Sell", $"Stop Loss <br/> at loss {loss:N0}");
                //    State = TradingState.Initial;
                //    return;//Otherwise might sell twice
                //}

                //Exit when it closes over envelope lower band
                //after closing under envelope lower band
                if (State == TradingState.WaitingToSell)
                {
                    if (ohlc.Close < envelopeLowerBand)
                    {
                        State = TradingState.MonitoringDownTrend;
                    }
                }
                else if (State == TradingState.MonitoringDownTrend)
                {
                    if (ohlc.Close > envelopeLowerBand)
                    {
                        CreateOrder(ohlc, date, "Sell", $"Closes over envelope lower band after closing under");
                        State = TradingState.Initial;
                        return;
                    }
                }

                //Limit profit
                //by selling the asset when it brings in 200 EUR
                var profit      = ohlc.Close - buyOrder.Price;
                var profitLimit = 200;
                if (ohlc.Close - buyOrder.Price >= profitLimit)
                {
                    CreateOrder(ohlc, date, "Sell", $"Profit {profit:N0} exceeded {profitLimit} EUR limit");
                    State = TradingState.Initial;
                    return;
                }
            }
        }
        public SimulationResult Simulate(DateTime start, DateTime end, int interval, StrategyParameters parameters)
        {
            //Read the OHLC from database
            var connectionName   = ConfigurationManager.AppSettings["ConnectionName"];
            var connectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;

            List <OHLC> ohlcList = new List <OHLC>();

            try
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand("", connection))
                    {
                        command.CommandType = System.Data.CommandType.Text;
                        command.CommandText = "SELECT o.Id, o.[Open], o.High, o.Low, o.[Close], Start, Volume FROM dbo.OHLC o " +
                                              "WHERE o.[Start] >= @start AND o.[End] < @end ORDER BY o.Id ASC";
                        command.Parameters.Add(new SqlParameter("start", start));
                        command.Parameters.Add(new SqlParameter("end", end));

                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                var ohlc = new OHLC();

                                ohlc.Open  = reader.GetDecimal(1);
                                ohlc.High  = reader.GetDecimal(2);
                                ohlc.Low   = reader.GetDecimal(3);
                                ohlc.Close = reader.GetDecimal(4);
                                ohlc.Start = reader.GetDateTime(5);
                                var volume = reader.GetDecimal(6);
                                ohlc.Volume = (volume * 1000);

                                ohlcList.Add(ohlc);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            //Set the trading state
            State   = TradingState.Initial;
            Orders  = new List <Order>();
            Account = new Account()
            {
                Euro = 10000
            };

            //l h o c

            //Find the first trade
            //Find all trades within the next 5 minutes
            //If there are any trade
            //Calculate low, high, open, close
            if (ohlcList.Any())
            {
                start = ohlcList.First().Start > start?ohlcList.First().Start : start;

                for (DateTime i = start; i < end; i = i.AddMinutes(interval))
                {
                    var windowStart = i;
                    var windowEnd   = windowStart.AddMinutes(interval);

                    var ohlcInTheSameWindow = ohlcList.Where(o => o.Start >= windowStart && o.Start < windowEnd).ToList();

                    if (ohlcInTheSameWindow.Any())
                    {
                        var low    = ohlcInTheSameWindow.Select(t => t.Low).Min();
                        var high   = ohlcInTheSameWindow.Select(t => t.High).Max();
                        var open   = ohlcInTheSameWindow.First().Open;
                        var close  = ohlcInTheSameWindow.Last().Close;
                        var volume = ohlcInTheSameWindow.Sum(t => t.Volume);

                        var ohlc = new OHLC {
                            Open = open, High = high, Low = low, Close = close, Volume = volume, Start = windowStart, End = windowEnd
                        };

                        DataPoints.Insert(0, ohlc);

                        //Check for indicators and make trading decisions
                        DonchienBreakoutStrategy(ohlc, windowEnd, parameters);

                        //Add Donchian Channels
                        var dca = CalculateDonchianChannel(55);

                        UpperBand.Add((double)dca.UpperBand);
                        LowerBand.Add((double)dca.LowerBand);
                    }
                }
            }

            //Stats after simulation
            var stats = new Stats(Orders, Account, DataPoints)
            {
                Market = DataPoints.First().Close - DataPoints.Last().Close,
                MarketRiskAdjustedReturn = (double)((DataPoints.First().Close - DataPoints.Last().Close) / DataPoints.Last().Close) * 100,
                Target = (end - start).Days * 80, //80 EUR profit per day
            };

            return(new SimulationResult()
            {
                Dates = DataPoints.OrderBy(dp => dp.Start).Select(dp => dp.End).ToList(),
                Values = DataPoints.OrderBy(dp => dp.Start).Select(dp => dp.Close).ToList(),
                Volumes = DataPoints.OrderBy(dp => dp.Start).Select(dp => dp.Volume).ToList(),
                Upper = UpperBand,
                Lower = LowerBand,
                Orders = Orders,
                Events = Events,
                Stats = stats
            });
        }
        public List <string> Export(DateTime start, DateTime end, int interval, StrategyParameters parameters)
        {
            //Read the OHLC from database
            var connectionString = ConfigurationManager.ConnectionStrings["marketdata-local"].ConnectionString;

            List <OHLC> ohlcList = new List <OHLC>();

            try
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand("", connection))
                    {
                        command.CommandType = System.Data.CommandType.Text;
                        command.CommandText = "SELECT o.Id, o.[Open], o.High, o.Low, o.[Close], Start, Volume FROM dbo.OHLC o " +
                                              "WHERE o.[Start] >= @start AND o.[End] < @end ORDER BY o.Id ASC";
                        command.Parameters.Add(new SqlParameter("start", start));
                        command.Parameters.Add(new SqlParameter("end", end));

                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                var ohlc = new OHLC();

                                ohlc.Open  = reader.GetDecimal(1);
                                ohlc.High  = reader.GetDecimal(2);
                                ohlc.Low   = reader.GetDecimal(3);
                                ohlc.Close = reader.GetDecimal(4);
                                ohlc.Start = reader.GetDateTime(5);
                                var volume = reader.GetDecimal(6);
                                ohlc.Volume = (volume * 1000);

                                ohlcList.Add(ohlc);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            //l h o c

            //Find the first trade
            //Find all trades within the next 5 minutes
            //If there are any trade
            //Calculate low, high, open, close
            if (ohlcList.Any())
            {
                start = ohlcList.First().Start > start?ohlcList.First().Start : start;

                for (DateTime i = start; i < end; i = i.AddMinutes(interval))
                {
                    var windowStart = i;
                    var windowEnd   = windowStart.AddMinutes(interval);

                    var ohlcInTheSameWindow = ohlcList.Where(o => o.Start >= windowStart && o.Start < windowEnd).ToList();

                    if (ohlcInTheSameWindow.Any())
                    {
                        var low    = Math.Round(ohlcInTheSameWindow.Select(t => t.Low).Min(), 2);
                        var high   = Math.Round(ohlcInTheSameWindow.Select(t => t.High).Max(), 2);
                        var open   = Math.Round(ohlcInTheSameWindow.First().Open, 2);
                        var close  = Math.Round(ohlcInTheSameWindow.Last().Close, 2);
                        var volume = Math.Round(ohlcInTheSameWindow.Sum(t => t.Volume), 2);

                        var ohlc = new OhlcIndicators(open, high, low, close)
                        {
                            Volume = volume,
                            Start  = windowStart,
                            End    = windowEnd,
                        };

                        DataPoints.Add(ohlc);

                        var prices = DataPoints.Select(d => d.Close).ToList();

                        ohlc.Mva10  = Math.Round(CalculateMovingAverage(prices, 10), 2);
                        ohlc.Mva200 = Math.Round(CalculateMovingAverage(prices, 200), 2);
                        ohlc.Rsi2   = Math.Round(CalculateRelativeStrengthIndex(prices, 3), 2);
                        ohlc.Rsi14  = Math.Round(CalculateRelativeStrengthIndex(prices, 15), 2);
                        var bb = CalculateBollingerBands(prices, 20, 2);
                        ohlc.PercentB  = Math.Round(bb.PercentB, 2);
                        ohlc.Bandwidth = Math.Round(bb.Bandwidth, 2);
                    }
                }
            }

            //Annotate
            //Calculate action - looking backward and forward
            //which is obviously not possible in real time trading
            CalculateAction(10, 4);

            //var balanced = OverSample(10);

            var normalized = Normalize(DataPoints);

            var exportLines = normalized.Select(d => $"{d.Start:M/d/yyyy H:mm},{d.Open},{d.High},{d.Low},{d.Close},{d.Volume},{d.Mva10},{d.Mva200},{d.Rsi2},{d.Rsi14},{d.PercentB},{d.Bandwidth},{d.Action}").ToList();

            exportLines.Insert(0, "Date,Open,High,Low,Close,Volume,Mva10,Mva200,Rsi2,Rsi14,PercentB,Bandwidth,Action");

            return(exportLines);
        }