void addBar(StatisticsCollector collector, IEnumerable <Position> positions, double close, double fxRate) { var weightedPositions = O.convert(positions, p => new WeightedPosition(p)); collector.addBar(weightedPositions, O.dictionaryOne(symbol(), new Bar(close, close, close, close, currentDate)), O.dictionaryOne(symbol(), fxRate)); currentDate = currentDate.AddDays(1); }
static double expectancy(StatisticsCollector collector) { var expectedWin = AVERAGE_WIN(collector) * WINNING_TRADES_PERCENT(collector); var expectedLoss = AVERAGE_LOSS(collector) * (1 - WINNING_TRADES_PERCENT(collector)); return((expectedLoss + expectedWin) / Math.Abs(AVERAGE_LOSS(collector))); }
public static double yearsInCollector(StatisticsCollector collector) { var firstDate = first(collector.dates()); var lastDate = last(collector.dates()); return(lastDate.Subtract(firstDate).TotalDays / 365.25); }
internal static List <Drawdown> allDrawdowns(StatisticsCollector collector) { var equities = cumulativeSum(collector.pnl()); var highWaterMarks = cumulativeMax(equities); Drawdown current = null; var drawdowns = new List <Drawdown>(); each(equities, highWaterMarks, (equity, highWaterMark) => { if (equity == highWaterMark) { if (current != null) { current.closed(); } current = null; return; } if (current == null) { current = new Drawdown(); drawdowns.Add(current); } current.add(equity, highWaterMark); }); return(drawdowns); }
static Position addTrade(Order order, StatisticsCollector collector, double fillPrice, double slippage, double fxRate) { var trade = new Trade(order, fillPrice, order.size, slippage, fxRate); var position = order.fill(trade); collector.addOrder(new WeightedPosition(position), new WeightedTrade(trade)); return(position); }
public Dictionary <string, double> values(StatisticsCollector collector) { Bomb.when(collector.pnl().Count == 0, () => "must have at least one observation"); collector.cacheAllDrawdowns(allDrawdowns(collector)); var result = dictionary(calculators.Keys, name => calculators[name](collector)); collector.cacheAllDrawdowns(null); return(result); }
void addClosedPosition(StatisticsCollector collector, double pnl, int barsHeld) { var order = symbol().buy("buy one", market(), 1, oneBar()).placed(); var position = addTrade(order, collector, -0.015625, 0.015625, 1); O.zeroTo(barsHeld, i => position.newBar()); var exit = position.exit("get out", market(), oneBar()).placed(); addTrade(exit, collector, pnl + 0.015625, 0.015625, 1); }
static double kappaRatio(StatisticsCollector collector, int moment) { var pnl = collector.pnl(); if (all(pnl, p => p == 0)) { return(0); } return(average(pnl) / Math.Pow(lowerPartialMoment(pnl, moment), 1.0 / moment)); }
static double averageDrawdownRecoveryTime(StatisticsCollector collector) { var drawdowns = closedDrawdowns(collector); if (drawdowns.Count == 0) { return(0); } return(drawdowns.Count == 0 ? 0 : average(convert(drawdowns, d => (double)d.recoveryTime()))); }
static double averageDrawdown(StatisticsCollector collector) { var drawdowns = collector.allDrawdowns(); if (drawdowns.Count == 0) { return(0); } return(-average(convert(drawdowns, d => d.size()))); }
public void addOrder(StatisticsCollector collector, Position position, Trade trade) { if (!collects(position)) { return; } var w = weight(position); collector.addOrder(new WeightedPosition(position, w), new WeightedTrade(trade, w)); }
static IEnumerable <double> collapsedPnl(StatisticsCollector collector, Converter <DateTime, DateTime> toKey) { var bucketed = new Dictionary <DateTime, double>(); each(collector.dates(), collector.pnl(), (barDate, pnl) => { var key = toKey(barDate); double total; bucketed.TryGetValue(key, out total); bucketed[key] = total + pnl; }); return(convert(sort(bucketed.Keys), key => bucketed[key])); }
public void testMaxDrawdown() { var collector = new StatisticsCollector(arguments()); var position = addOpenPosition(collector, 0.0, 1); oneBar(collector, position, 10, 10000, 0); oneBar(collector, position, 8, 8000, -2000); oneBar(collector, position, 11, 11000, -2000); oneBar(collector, position, 8, 8000, -3000); oneBar(collector, position, 6, 6000, -5000); oneBar(collector, position, 8, 8000, -5000); oneBar(collector, position, 12, 12000, -5000); }
static double maxDrawdown(StatisticsCollector collector) { var pnl = collector.pnl(); if (isEmpty(pnl)) { return(0); } var equity = cumulativeSum(pnl); var highWaterMark = cumulativeMax(equity); return(min(subtract(equity, highWaterMark))); }
static List <Drawdown> closedDrawdowns(StatisticsCollector collector) { var drawdowns = copy(collector.allDrawdowns()); if (isEmpty(drawdowns)) { return(drawdowns); } if (last(drawdowns).isOpen()) { drawdowns.RemoveAt(drawdowns.Count - 1); } return(drawdowns); }
public void addBar(StatisticsCollector collector, System system, Dictionary <Symbol, Bar> bars, Dictionary <Symbol, double> fxRates) { var symbols = weights.Keys; var myBars = dictionary(accept(bars, entry => weights.ContainsKey(entry.Key))); if (isEmpty(myBars)) { return; } var ours = collect(symbols, symbol => system.positions(symbol)); var weighted = convert(ours, p => new WeightedPosition(p, weight(p))); collector.addBar(weighted, myBars, fxRates); }
void doSomeTrades(StatisticsCollector collector) { var first = addTrade(symbol().buy("buy", market(), 3, oneBar()).placed(), collector, 100, 0, 1.05); addBar(collector, O.list(first), 99, 1); addBar(collector, O.list(first), 103, 1.2); addBar(collector, O.list(first), 97, 1.1); addTrade(first.exit("out", market(), oneBar()).placed(), collector, 101, 0, 1.1); var second = addTrade(symbol().sell("sell", market(), 7, oneBar()).placed(), collector, 102, 0, 1.1); addBar(collector, O.list(second), 100, 1.4); addBar(collector, O.list(second), 101, 1.3); addTrade(second.exit("out", market(), oneBar()).placed(), collector, 99, 0, 1.3); addBar(collector, 99, 0.9); }
public void testForeignExchangeFXConversion() { symbol().setBigPointValue(5000); var collector = new StatisticsCollector(arguments()); doSomeTrades(collector); O.each(O.list(-90000.0, 369000, -253500, -907000, 304500, 91000), collector.pnl(), (a, b) => AlmostEqual(a, b, 1e-8)); var metrics = collector.metrics(); AreEqual(1, metrics["QWinningTrades"]); AlmostEqual(91500, metrics["QAverageWin"], 1e-8); AlmostEqual(-577500, metrics["QAverageLoss"], 1e-8); AlmostEqual(-486000, metrics["QNetProfit"], 1e-8); }
// conditional: average one to n.alpha instead of n.alppha alone static double percentileDrawdown(StatisticsCollector collector, double percentile, bool isConditional) { var drawdowns = collector.allDrawdowns(); var notEnough = percentile == 0.1 ? drawdowns.Count < 10 : drawdowns.Count < 5; if (notEnough) { return(0); } var nAlpha = (int)Math.Floor(drawdowns.Count * percentile) - 1; if (isConditional) { return(-average(convert(sort(drawdowns).GetRange(0, nAlpha + 1), dd => dd.size()))); } return(-sort(drawdowns)[nAlpha].size()); }
static double kRatio(StatisticsCollector collector) { var pnl = collector.pnl(); if (all(pnl, p => p == 0)) { return(0); } var equity = cumulativeSum(pnl); var regression = new SimpleRegression(); eachIt(equity, (i, p) => regression.addData(i + 1, p)); var b1 = regression.getSlope(); var stdError = regression.getSlopeStdErr(); return(b1 / (stdError * Math.Sqrt(pnl.Count))); }
public void testForeignExchangeFXConversionCanBeTurnedOff() { symbol().setBigPointValue(5000); var args = arguments(); args.runInNativeCurrency = true; var collector = new StatisticsCollector(args); doSomeTrades(collector); O.each(O.list(-15000.0, 60000, -90000, 130000, -35000, 70000), collector.pnl(), (a, b) => AlmostEqual(a, b, 1e-8)); var metrics = collector.metrics(); AreEqual(2, metrics["QWinningTrades"]); AlmostEqual(60000, metrics["QAverageWin"], 1e-8); AlmostEqual(0, metrics["QAverageLoss"], 1e-8); AlmostEqual(120000, metrics["QNetProfit"], 1e-8); }
static double losingTradesPercent(StatisticsCollector collector) { return(safeDivide(0, LOSING_TRADES(collector), TOTAL_FINISHED_TRADES(collector))); }
public void testMetricCalcs() { var collector = new StatisticsCollector(arguments()); addClosedPosition(collector, 100.0, 1); addClosedPosition(collector, 90.0, 2); addClosedPosition(collector, -90.25, 3); addClosedPosition(collector, 0, 4); addClosedPosition(collector, -98.25, 5); var position = addOpenPosition(collector, 95.0, 10); addBar(collector, position, 96, 1); AreEqual(11.50 * symbol().bigPointValue, collector.netProfit()); addBar(collector, position, 97, 1); AreEqual(21.50 * symbol().bigPointValue, collector.netProfit()); addBar(collector, position, 92, 1); addBar(collector, position, 97, 1); var expectedMetrics = new Dictionary <string, double> { { "QDownsideDeviation", 50000 }, { "QStandardDeviation", 41298.4563876182 }, { "QStandardDeviationWeekly", 0 }, { "QStandardDeviationMonthly", 0 }, { "QSharpeRatio", 3.99276123210182 }, { "QSharpeRatioWeekly", 0 }, { "QSharpeRatioMonthly", 0 }, { "QNetProfitPerMaxDrawdown", 21500 / 50000.0 }, { "QLargestLossPerAverageLoss", 98250 / 94250.0 }, { "QLargestLossPerGrossLoss", 98250 / 188500.0 }, { "QLargestWinPerAverageWin", 100000 / 95000.0 }, { "QLargestWinPerGrossProfit", 100000 / 190000.0 }, { "QLargestWinPerNetProfit", 100000 / 21500.0 }, { "QWinLossRatio", 1.00795755968170 }, { "QAverageTrade", 21500 / 6.0 }, { "QExpectancy", -0.196816976127321 }, { "QTradesPerBar", 1.5 }, { "QExpectancyScore", -0.295225464190981 }, { "QLargestWinningTrade", 100000 }, { "QLargestLosingTrade", -98250 }, { "QAverageLoss", -94250 }, { "QAverageWin", 95000 }, { "QRealizedNetProfit", 1500 }, { "QRealizedGrossLoss", -188500 }, { "QRealizedGrossProfit", 190000 }, { "QAverageProfit", 300 }, { "QNetProfit", 21500 }, { "QUnrealizedNetProfit", 20000 }, { "QWinningTradesPct", 0.40 }, { "QLosingTradesPct", 0.40 }, { "QNeutralTrades", 1 }, { "QWinningTrades", 2 }, { "QLosingTrades", 2 }, { "QLongWinningTrades", 2 }, { "QLongLosingTrades", 2 }, { "QShortWinningTrades", 0 }, { "QShortLosingTrades", 0 }, { "QMaxConsecutiveWinningTrades", 2 }, { "QMaxConsecutiveLosingTrades", 1 }, { "QTotalFinishedTrades", 5 }, { "QTotalTrades", 6 }, { "QAverageLosingBarsHeld", 4 }, { "QAverageWinningBarsHeld", 1.5 }, { "QAverageBarsHeld", 3 }, { "QWinningBarsHeld", 3 }, { "QLosingBarsHeld", 8 }, { "QTotalBarsHeld", 15 }, { "QMaxDrawdown", -50000 }, { "QKRatio", -0.077151674981046 }, { "QAnnualizedNetProfit", 2617625 }, { "QCalmarRatio", 52.3525 }, { "QConditionalTenPercentileCalmarRatio", 0 }, { "QConditionalTwentyPercentileCalmarRatio", 0 }, { "QAverageDrawdown", -50000 }, { "QAverageDrawdownTime", 2 }, { "QAverageDrawdownRecoveryTime", 1 }, { "QTenPercentileDrawdown", 0 }, { "QConditionalTenPercentileDrawdown", 0 }, { "QTwentyPercentileDrawdown", 0 }, { "QConditionalTwentyPercentileDrawdown", 0 }, { "QSortinoRatio", 4.74463644550349 }, { "QOmegaRatio", 1.43 }, { "QUpsidePotentialRatio", 0.476666666666667 }, { "QTotalSlippage", 156.25 }, { "QAverageSlippagePerWinningTrade", 31.25 }, { "QAverageSlippagePerLosingTrade", 31.25 } }; var metrics = collector.metrics(); O.each(expectedMetrics, (name, value) => AlmostEqual(value, Bomb.missing(metrics, name), 1e-10)); AreEqual(expectedMetrics.Count, metrics.Count); }
static double annualizedNetProfit(StatisticsCollector collector) { return(NET_PROFIT(collector) / yearsInCollector(collector)); }
void oneBar(StatisticsCollector collector, Position position, double close, double expectedProfit, double expectedDrawdown) { addBar(collector, position, close, 1); AreEqual(expectedProfit, collector.netProfit()); AreEqual(expectedDrawdown, Metrics.MAX_DRAWDOWN(collector)); }
void addBar(StatisticsCollector collector, Position position, double close, double fxRate) { addBar(collector, O.list(position), close, fxRate); }
static double annualizationFactor(StatisticsCollector collector) { return(Math.Sqrt(count(collector.pnl()) / yearsInCollector(collector))); }
static double upsidePotentialRatio(StatisticsCollector collector) { var pnl = collector.pnl(); return(semiDeviation(pnl, 1, true) / semiDeviation(pnl, 2, false)); }
void addBar(StatisticsCollector collector, double close, double fxRate) { addBar(collector, new List <Position>(), close, fxRate); }
Position addOpenPosition(StatisticsCollector collector, double price, int size) { var order = symbol().buy("buy one", market(), size, oneBar()).placed(); return(addTrade(order, collector, price - 0.015625, 0.015625, 1)); }