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); }