Introduction
This project demonstrates how to use WPFToolkit Charting to plot Monte Carlo equity & max drawdown simulation. This is for educational purposes only. DO NOT use this tool as a trading advising tool.
Background
Monte Carlo trade simulation is based on assuming that the market condition remains unchanged in the future. Hence, the same trading method will have general similar profitable and losing trades, but the order of those trades will not be the same, otherwise, history repeats exactly and this is not likely. However, even simple order difference may wreck havoc to your portfolio. Imagine that you had 50 winning trades and 50 losing trades distributed evenly, your portfolio may have survived nicely, but the same 100 trades with losing 50 of them first may bankrupt your portfolio. Monte Carlo simulation is to gauge how much risk your portfolio has by shuffling (bootstrapping actually) trade order.
For example, if 10 percentile crosses equity at 1.1 means 90% of the time, your porfolio's final equity will be more than 1.1 times of your initial capital. If 10 percentile crosses max drawdown at 20% means 90% of time, your portfolio's max drawdown will be less than 20%.
I spent about 3 hours during a weekend writing this tool for my investment seminar. I figured I could share it with the trading & programming community.
For those who are interested in making your own tools to assist your trading, this simple simulation may be a good starting point; And for those who want to learning WPFToolkit Charting, I hope this tiny little project may serve the purpose.
Using the Code
Basically, there are only MainWindow.xaml and MainWindow.xaml.cs files. If you prefer to create your own project from scratch, remember to add System.Windows.Controls.DataVisualization.Toolkit
and WPFToolkit
to References. You may need to NuGet both.
MainWindow.xaml
-->
<Window x:Class="MonteCarloSim.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:chartingToolkit="
clr-namespace:System.Windows.Controls.DataVisualization.Charting;
assembly=System.Windows.Controls.DataVisualization.Toolkit"
Title="Monte Carlo Trade Simulator"
Height="811.539" Width="855">
<DockPanel>
-->
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem x:Name="Open"
Header="_Open" Click="Open_Click"/>
<MenuItem x:Name="Save"
Header="_Save" Click="Save_Click"/>
<MenuItem x:Name="Exit"
Header="_Exit" Click="Exit_Click"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem x:Name="Help"
Header="_Help" Click="Help_Click" />
<MenuItem x:Name="About"
Header="_About" Click="About_Click" />
</MenuItem>
</Menu>
<Grid Margin="0,0,0,0" DockPanel.Dock="Bottom">
<TextBox x:Name="tb"
Margin="0,0,119,0" TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
AcceptsReturn="True"
TextChanged="tb_TextChanged" Height="130"
VerticalAlignment="Top" />
<Button x:Name="button"
Content="Go" HorizontalAlignment="Right"
Margin="0,55,22,0" Width="75"
Click="button_Click" IsEnabled="False"
Height="20" VerticalAlignment="Top"/>
-->
<chartingToolkit:Chart Name="equity"
Title="Equity" Margin="0,131,0,317">
<chartingToolkit:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width"
Value="0" /> -->
<Setter Property="Height"
Value="0" />
</Style>
</chartingToolkit:Chart.LegendStyle>
-->
<chartingToolkit:LineSeries DependentValuePath="Value"
IndependentValuePath="Key"
ItemsSource="{Binding}" DataContext="{Binding}"
IsSelectionEnabled="False">
<chartingToolkit:LineSeries.DataPointStyle>
<Style TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Template"
Value="{x:Null}" /> -->
<Setter Property="Background"
Value="DarkGreen" />
</Style>
</chartingToolkit:LineSeries.DataPointStyle>
<chartingToolkit:LineSeries.DependentRangeAxis>
-->
<chartingToolkit:LinearAxis Orientation="Y"
Title="Equity from init capital (times)"
ShowGridLines="True" />
</chartingToolkit:LineSeries.DependentRangeAxis>
</chartingToolkit:LineSeries>
<chartingToolkit:Chart.Axes>
-->
<chartingToolkit:LinearAxis
Orientation="X" Title="Percentile"
ShowGridLines="True"
Minimum="0" Maximum="100" />
</chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>
-->
<chartingToolkit:Chart Name="drawdown"
Title="Drawdown" RenderTransformOrigin="0.446,0.463"
Height="316" VerticalAlignment="Bottom">
<chartingToolkit:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width"
Value="0" />
<Setter Property="Height"
Value="0" />
</Style>
</chartingToolkit:Chart.LegendStyle>
<chartingToolkit:LineSeries DependentValuePath="Value"
IndependentValuePath="Key"
ItemsSource="{Binding}" DataContext="{Binding}"
IsSelectionEnabled="False" Margin="0,0,0,0.5">
<chartingToolkit:LineSeries.DataPointStyle>
<Style TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Template"
Value="{x:Null}" />
<Setter Property="Background"
Value="Red" />
</Style>
</chartingToolkit:LineSeries.DataPointStyle>
<chartingToolkit:LineSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis Orientation="Y"
Title="Max Drawdown %"
ShowGridLines="True" />
</chartingToolkit:LineSeries.DependentRangeAxis>
</chartingToolkit:LineSeries>
<chartingToolkit:Chart.Axes>
<chartingToolkit:LinearAxis
Orientation="X" Title="Percentile"
ShowGridLines="True" Minimum="0"
Maximum="100" />
</chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>
</Grid>
</DockPanel>
</Window>
Part of MainWindow.xaml
Assign a List
of KeyValuePair
from the code behind CS to DataContext
of the chart (equity.DataContext
& drawdown.DataContext
), where Key
is X
axis, Value
is Y
axis. You may set IsSelectionEnabled = "True"
if you have lesser number of datapoints. I have 5000 datapoints here.
-->
<chartingToolkit:LineSeries DependentValuePath="Value"
IndependentValuePath="Key"
ItemsSource="{Binding}" DataContext="{Binding}"
IsSelectionEnabled="False">
Part of MainWindow.xaml
You may comment out this part to show series legend on the right part of the chart.
<chartingToolkit:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width"
Value="0" /> -->
<Setter Property="Height"
Value="0" />
</Style>
</chartingToolkit:Chart.LegendStyle>
Part of MainWindow.xaml
You may comment out this part to show series datapoint maker, but this will slow down the app.
<Setter Property="Template" Value="{x:Null}" />
-->
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.IO;
namespace MonteCarloSim
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
if (tb.Text != "")
{
List<List<KeyValuePair<double, double>>> resultList = go(tb.Text);
equity.DataContext = resultList[0];
drawdown.DataContext = resultList[1];
}
}
private List<List<KeyValuePair<double, double>>> go(string txt)
{
List<double> pl = new List<double>();
string[] tmp = txt.Split(new[]
{ Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach(var t in tmp)
{
double d;
if (Double.TryParse(t, out d))
{
pl.Add(d);
}
}
return MakeLineSeries(pl);
}
private List<List<KeyValuePair<double,
double>>> MakeLineSeries(List<double> pl)
{
int simNum = 5000;
List <double> eqFinal = new List<double>();
List <double> mdd = new List<double>();
List<KeyValuePair<double, double>>
eqKVlist = new List<KeyValuePair<double, double>>();
List<KeyValuePair<double, double>>
mddKVlist = new List<KeyValuePair<double, double>>();
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < simNum; i++)
{
List<double>
shuffled = shuffle(pl, rnd);
List<double> eqlist = new List<double>();
double equity = 1;
foreach (var s in shuffled)
{
eqlist.Add((s / 100 + 1) * equity);
equity = eqlist[eqlist.Count - 1];
}
eqFinal.Add(equity);
mdd.Add(Maxdrawdown(eqlist));
}
var eqFinalSorted = eqFinal.OrderBy(d => d).ToList();
var mddSorted = mdd.OrderBy(d => d).ToList();
for (int i = 0; i < simNum; i++)
{
eqKVlist.Add(new KeyValuePair<double,
double>((i + 1) / (double)simNum * 100, eqFinalSorted[i]) );
mddKVlist.Add(new KeyValuePair<double,
double>((i + 1) / (double)simNum * 100, mddSorted[i]) );
}
return new List<List<KeyValuePair<double,
double>>> { eqKVlist, mddKVlist };
}
private double Maxdrawdown(List<double> eqlist)
{
int count = eqlist.Count;
double mdd_ratio = 0;
double peak = 0;
for (int i = 0; i < count; i++)
{
if (eqlist[i] >= peak)
{
peak = eqlist[i];
}
if ((eqlist[i] - peak) /
eqlist[i] < mdd_ratio)
{
mdd_ratio = (eqlist[i] - peak) / peak;
}
}
return mdd_ratio * 100;
}
private List<double> shuffle(List<double> pl, Random rnd)
{
int count = pl.Count;
List<double> shuffled = new List<double>();
for (int i = 0; i < count; i++)
{
shuffled.Add(pl[rnd.Next(count)]);
}
return shuffled;
}
private void tb_TextChanged(object sender, TextChangedEventArgs e)
{
if (tb.Text != "")
button.IsEnabled = true;
else
button.IsEnabled = false;
}
private void Save_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.DefaultExt = ".txt";
dlg.Filter = "Text documents (.txt)|*.txt";
if (dlg.ShowDialog() == true)
{
string filename = dlg.FileName;
StreamWriter writer = new StreamWriter(filename);
writer.Write(tb.Text);
writer.Dispose();
writer.Close();
}
}
private void Open_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.DefaultExt = ".txt";
dlg.Filter = "Text documents (.txt)|*.txt";
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string filename = dlg.FileName;
tb.Text = File.ReadAllText(filename);
}
}
private void Exit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void About_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show
("Monte Carlo Trade Simulator 1.0\n\n© 2017 Mark M.H. Chan");
}
private void Help_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("1. Enter profit/loss % of trades,
and each trade per line in the Text Box. \n\n2. Click GO button");
}
}
}
Part of MainWindow.xaml.cs
You may change number of simulations by changing int simNum
, e.g., 10000
.
private List<List<KeyValuePair<double,
double>>> makeLineSeries(List<double> pl)
{
int simNum = 5000;
Running the Program
- Enter profit/loss % of trades, and each trade per line in the Text Box:
Example:
1.02
1.13
-2.4
3.5
-1.3
5
- Click GO button
Points of Interest
To find out more about Monte Carlo Simulation:
Software Disclaimer
End User License Agreement
© 2017 Mark M.H. Chan
This SOFTWARE PRODUCT is provided by Mark M.H. Chan "as is" and "with all faults." By downloading and/or using it, you agree the following:
Mark M.H. Chan makes no representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this SOFTWARE PRODUCT. There are inherent dangers in the use of any software, and you are solely responsible for determining whether this SOFTWARE PRODUCT is compatible with your equipment and other software installed on your equipment. You are also solely responsible for the protection of your equipment and backup of your data, and Mark M.H. Chan will not be liable for any damages and/or losses you may suffer in connection with using, modifying, or distributing this SOFTWARE PRODUCT. This SOFTWARE PRODUCT is for educational purpose only. It is NOT meant to be for trading advice.