Trade Report CSV & Markdown generator

Fu510n

Member
Joined
Jan 7, 2025
Posts
15
Likes
10
While having the ability to export backtest results in Excel format is great, it's always bugged me (and apparently a few others) as to why replay metrics weren't given the same capability. I spent a few days trying to figure out a way to implement it on my own and have at least the Study (and standalone TradeMetrics class) framework finally working. Methods like onPositionClose() (and any others except getAccount*() methods) aren't available so I had to implement all the order fill logic on my own using just the current account position and price changes via onTick() (a pain, but it is what it is).

It's no where near complete but I have other tasks that have higher priority at the moment. I'll revisit it at some point and finish all the stats that are currently being generated in the Trade Report (and fixing MAE/MFE while I'm at it since those don't appear to be accurate in MotiveWave at the moment). I wanted to be able to use the TradeMetrics class in other strategies hence having that available separately - you'll probably want to change package names, etc. as you see fit.

TradeMetricsMarkdown.png
 

Attachments

Still work-in-progress but much further along...

I integrated the TradeMetrics class in my other strategies (partial snippet below); e.g.
Java:
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
import javafx.scene.control.Button;
import javafx.scene.Scene;
import javafx.stage.Stage;
import reshlabs.TradeMetrics.Trade;

private Button reportButton;
private Stage reportStage = null;
TradeMetrics tradeMetrics = null;

tradeMetrics = new TradeMetrics(ctx, logDir, generateCSV, generateMarkdown);

reportButton = new Button("R");
reportButton.setOnAction(e -> {
  if (reportStage != null && reportStage.isShowing()) {
    reportStage.toFront();
    return;
  }
  if (tradeMetrics == null) { return; }  // FIXME

  MutableDataSet options = new MutableDataSet();
  options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create()));

  Parser parser = Parser.builder(options).build();
  HtmlRenderer renderer = HtmlRenderer.builder(options).build();

  String mdMetrics = tradeMetrics.toMarkdown("M");
  Node node = parser.parse(mdMetrics);
  String htmlMetrics = renderer.render(node);

  String mdPeriods = tradeMetrics.toMarkdown("P");
  node = parser.parse(mdPeriods);
  String htmlPeriods = renderer.render(node);

  String mdTrades = tradeMetrics.toMarkdown("T");
  node = parser.parse(mdTrades);
  String htmlTrades = renderer.render(node);

  String html = """
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/bootstrap-table.min.css">
<style>
  body { font-family: Arial, sans-serif; line-height: 1.6; }
  h1 { color: #2F5C84; }
  pre { background: #f4f4f4; padding: 10px; }
  /*table { width: 100%; border-collapse: collapse; }*/
  th, td { padding: 3px; border: 1px solid #ddd; }
  th { background: #2F5C84; color: white; text-align: left; }
  td:nth-child(2), td:nth-child(3), td:nth-child(4), td:nth-child(5), td:nth-child(6), td:nth-child(7), td:nth-child(8), td:nth-child(9), td:nth-child(10) { text-align: right; }
  td:nth-child(11), td:nth-child(12), td:nth-child(13), td:nth-child(14), td:nth-child(15), td:nth-child(16), td:nth-child(17), td:nth-child(18), td:nth-child(19) { text-align: right; }
</style>
<ul class="nav nav-tabs" id="myTab" role="tablist">
  <li class="nav-item" role="presentation">
    <button class="nav-link active" id="metrics-tab" data-bs-toggle="tab" data-bs-target="#metrics" type="button" role="tab">Metrics</button>
  </li>
  <li class="nav-item" role="presentation">
    <button class="nav-link" id="periods-tab" data-bs-toggle="tab" data-bs-target="#periods" type="button" role="tab">Periods</button>
  </li>
  <li class="nav-item" role="presentation">
    <button class="nav-link" id="trades-tab" data-bs-toggle="tab" data-bs-target="#trades" type="button" role="tab">Trades</button>
  </li>
</ul>
<div class="tab-content">
  <div class="tab-pane fade show active" id="metrics" role="tabpanel">
""" + htmlMetrics + """
  </div>
  <div class="tab-pane fade" id="periods" role="tabpanel">
""" + htmlPeriods + """
  </div>
  <div class="tab-pane fade" id="trades" role="tabpanel">
""" + htmlTrades + """
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
""";
  WebView webView = new WebView();
  webView.getEngine().loadContent(html);

  Scene scene = new Scene(webView, 1920, 1200 - 30);
  reportStage = new Stage();
  reportStage.setTitle("Trade Report - " + symbol);
  reportStage.setScene(scene);
  reportStage.show();
});

HBox actions = new HBox(5);
actions.getChildren().addAll(reportButton);
setGraphic(actions);

ReshLabsMotiveWaveMetrics.png
 

Attachments

Last edited:
Top