private SimulationResult SimulateEMACrossing(IList<OHLC> candles, int shortEMAPeriod, int longEMAPeriod, double buyThreshold, double sellThreshold, bool showLog) { decimal item = 100m; decimal currency = 0m; decimal buyAndHoldCurrency = 0m; //decimal last = 0m; //decimal lastBuyAndHold = 0m; SimulationResult result = new SimulationResult() { ShortMA = CalculateEMA(candles, shortEMAPeriod), LongMA = CalculateEMA(candles, longEMAPeriod), BuyThreshold = buyThreshold, SellThreshold = sellThreshold }; if (showLog) lstMessages.Items.Clear(); if (fee == 0) fee = 0.2m; if (result.ShortMA != null && result.LongMA != null) { int min = Math.Max(result.ShortMA.Begin, result.LongMA.Begin); decimal lastPrice = 0m; IList<Action> actions = new List<Action>(); Action lastAction = null; for (int i = 0; i < candles.Count; i++) { // Get current candle OHLC candle = candles[i]; lastAction = actions.LastOrDefault(); // Simulate buy (used in buy and hold) on first candle if (i == 0) { if (showLog) SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} Simulation Started...", candle.Date)); decimal curFee = Math.Round(item / candle.Close * fee / 100m, 8); decimal buy = Math.Round(item / candle.Close, 8); buyAndHoldCurrency = buy - curFee; } // Analyze moving average starting from a certain point (where all MA have a value) if (i >= min) { var diff = 100 * (result.ShortMA.Output[i - min] - result.LongMA.Output[i - min]) / ((result.ShortMA.Output[i - min] + result.LongMA.Output[i - min]) / 2); //var diff = 100 * (result.LongMA.Output[i - min] - result.ShortMA.Output[i - min]) / ((result.LongMA.Output[i - min] + result.ShortMA.Output[i - min]) / 2); if (item >= 0.1m) { if (diff > buyThreshold) { if (showLog) SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} we are currently in uptrend ({1:0.###}%)", candle.Date, diff)); { decimal curFee = Math.Round(item / candle.Close * fee / 100m, 8); decimal buy = Math.Round(item / candle.Close, 8); currency += (buy - curFee); item -= Math.Round(buy * candle.Close, 4); actions.Add(new Action() { ActionType = ActionType.Bid, Date = candle.Date, AmountCurrency = currency, AmountItem = item }); if (showLog) { if (actions.Count < 1 || lastPrice > candle.Close) SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} bought {1} @ {2}", candle.Date, buy, buy * candle.Close), Color.LightGreen); else SetText(MessageType.Warning, string.Format(CultureInfo.InvariantCulture, "{0} bought {1} @ {2}", candle.Date, buy, buy * candle.Close), Color.LightGreen); } lastPrice = candle.Close; } } } if (currency >= 0.1m) { if (diff < sellThreshold) { if (showLog) SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} we are currently in a downtrend ({1:0.###}%)", candle.Date, diff)); { decimal itemFee = Math.Round(currency * candle.Close * fee / 100m, 4); decimal sell = Math.Round(currency * candle.Close, 8); item += (sell - itemFee); currency -= Math.Round(sell / candle.Close, 8); actions.Add(new Action() { ActionType = ActionType.Ask, Date = candle.Date, AmountCurrency = currency, AmountItem = item }); if (showLog) { if (actions.Count < 1 || lastPrice < candle.Close) SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} sold {1} @ {2}", candle.Date, Math.Round(sell / candle.Close, 8), sell), Color.LightSkyBlue); else SetText(MessageType.Warning, string.Format(CultureInfo.InvariantCulture, "{0} sold {1} @ {2}", candle.Date, Math.Round(sell / candle.Close, 8), sell), Color.LightSkyBlue); } lastPrice = candle.Close; } } } //if (showLog) // SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "{0} we are currently not in an up or down trend ({1:0.###}%)", candle.Date, diff)); } result.Actions = actions; result.Performance = currency > item ? currency * candle.Close : item; result.BuyAndHold = buyAndHoldCurrency * candle.Close; } if (showLog) { SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "EMA Strategy Result (ShortEMA:{0}) (LongEMA:{1}) Performance:{2:0.####}% (vs B&H:{5:0.####}%) BuyThreshold:{3:0.###}% SellThreshold:{4:0.###}%", result.ShortMA.Period, result.LongMA.Period, Math.Round(result.Performance, 4), result.BuyThreshold, result.SellThreshold, result.BuyAndHold)); SetText(MessageType.Message, string.Format("Buy and Hold wallet have {0:0.########} BTC", buyAndHoldCurrency)); SetText(MessageType.Message, string.Format("Strategy wallet have {0:0.########} {1}", currency > item ? currency : item, currency > item ? "BTC" : "EUR")); } } else if (showLog) SetText(MessageType.Warning, string.Format("Something wrong calculating EMAs...")); return result; }
private void CalculateBestEMACrossing(IList<OHLC> candles) { if (candles.Count == 0) { SetText(MessageType.Warning, "Candles not loaded"); return; } decimal best = 0; for (decimal sellInc = -0.02m; sellInc > -2.0m; sellInc -= 0.01m) for (decimal buyInc = 0.02m; buyInc < 2.0m; buyInc += 0.01m) for (int k = 0; k < 24; k++) for (int j = k; j < 60; j++) { SimulationResult simulation = SimulateEMACrossing(candles, 2 + k, 3 + j, (double)buyInc, (double)sellInc, false); if ((k * j) % 20 == 0) UpdateStatus(string.Format(CultureInfo.InvariantCulture, "Simulation Result:{4:0.###}% ShortEMA:{0} LongEMA:{1} BuyThreshold:{2:0.###}% SellThreshold:{3:0.###}%", simulation.ShortMA.Period, simulation.LongMA.Period, buyInc, sellInc, simulation.Performance)); if (simulation.Performance > best) { best = simulation.Performance; lastSimulation = simulation; UpdateParams(simulation.ShortMA.Period, simulation.LongMA.Period, buyInc, sellInc); SetText(MessageType.Message, string.Format(CultureInfo.InvariantCulture, "Best strategy {2:0.####}% vs Buy & Hold:{5:0.####}% => ShortEMA:{0} LongEMA:{1} BuyThreshold:{3:0.###}% SellThreshold:{4:0.###}% ", simulation.ShortMA.Period, simulation.LongMA.Period, Math.Round(best, 4), (double)buyInc, (double)sellInc, simulation.BuyAndHold)); } if (ctsEma != null && ctsEma.IsCancellationRequested) return; } }
private void OnSimulateEMA(object sender, EventArgs e) { if (rbRealtimeSim.Checked) { lastSimulation = SimulateEMACrossing(realtimeCandles, (int)nupShortEMA.Value, (int)nupLongEMA.Value, (double)nupBuyDiff.Value, (double)nupSellDiff.Value, true); if (realtimeCandles.Count > 0) DrawChart(realtimeCandles, "price"); } else if (rbHistoricalSim.Checked) { lastSimulation = SimulateEMACrossing(historyCandles, (int)nupShortEMA.Value, (int)nupLongEMA.Value, (double)nupBuyDiff.Value, (double)nupSellDiff.Value, true); if (historyCandles.Count > 0) DrawChart(historyCandles, "priceHistory"); } }