-
Notifications
You must be signed in to change notification settings - Fork 3
/
clsSimulation.cls
226 lines (156 loc) · 7.21 KB
/
clsSimulation.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "clsSimulation"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'Purpose: This class encapsulates the entire logic for the Monte Carlo Simulation
Option Explicit
Implements INameProvider
Private mintLotSize As Integer
Private mintTotalRuns As Integer
Private mvntTradeList As Variant
Private mdblStartEquity As Double
Private mdblMargin As Double
Private mdblEquityIncrement As Double
Private mintTradesInYear As Integer
Private Const DEFAULT_RUNS As Integer = 2500
Private Const DEFAULT_LOTSIZE As Integer = 1
Private Function INameProvider_GetClassName() As String
INameProvider_GetClassName = "clsSimulation"
End Function
Property Get lotSize() As Integer
lotSize = mintLotSize
End Property
Property Get totalRuns() As Integer
totalRuns = mintTotalRuns
End Property
Private Sub Class_Initialize()
mintLotSize = DEFAULT_LOTSIZE
End Sub
Public Sub InitiateProperties(ByVal tradesInYear As Integer, ByVal TradeList As Variant, _
ByVal startEquity As Double, ByVal margin As Double, _
Optional ByVal lotSize As Integer = DEFAULT_LOTSIZE, _
Optional ByVal totalRuns As Integer = DEFAULT_RUNS)
'Purpose: This creates a method that can be used as a factory process to initialize the class
mintLotSize = lotSize
mintTotalRuns = totalRuns
mvntTradeList = TradeList
mdblMargin = margin
mdblStartEquity = startEquity
mdblEquityIncrement = mdblStartEquity / 4
mintTradesInYear = tradesInYear
End Sub
Public Function fncRunProcess() As Collection
' Purpose: this is the main routine that starts the simulation
' The results are return in a collection of clsResult objects
Dim dblBeginEquity As Double
Dim dblEnd As Double
Dim oResult As clsResult
Dim collResults As New Collection
On Error GoTo Err_Handler
dblEnd = mdblStartEquity + 10 * mdblEquityIncrement
For dblBeginEquity = mdblStartEquity To dblEnd Step mdblEquityIncrement
Set oResult = fncProcessStartEquity(dblBeginEquity, mintTotalRuns)
If Not oResult Is Nothing Then
collResults.Add oResult
End If
Next dblBeginEquity
Set fncRunProcess = collResults
Exit_Here:
Set collResults = Nothing
Set oResult = Nothing
Exit Function
Err_Handler:
MsgBox Err.Description, vbCritical, "Error"
GoTo Exit_Here
End Function
Private Function fncProcessStartEquity(ByVal dblBeginEquity As Double, Optional ByVal intTotalRuns As Integer = DEFAULT_RUNS) As clsResult
' Purpose: run the simulation for this starting equity amount
' dblBeginEquity = Base start Equity value for the output
' intTotalRuns = This is the number of times to run the simulation for each equity (default is 2500)
Dim intRiskOfRuinCount As Integer
Dim intRun As Integer
Dim oEquityCurve As New clsEquityCurve
Dim wsCalc As Worksheet
Dim oResult As New clsResult
Dim lRow As Long
Const STR_CALC_SHEET As String = "Calc"
' this creates a temporary worksheet to perform the calculations
Call DeleteSheetIfExists(STR_CALC_SHEET)
Set wsCalc = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsCalc.name = STR_CALC_SHEET
With wsCalc
intRiskOfRuinCount = 0
For intRun = 1 To intTotalRuns
Set oEquityCurve = fncBuildEquityCurve(dblBeginEquity, mintTradesInYear, mvntTradeList, mintLotSize)
If oEquityCurve.IsRuined Then
intRiskOfRuinCount = intRiskOfRuinCount + 1
End If
.Range("A" & intRun).value = oEquityCurve.EquityAmount
.Range("B" & intRun).value = oEquityCurve.Drawdown
.Range("C" & intRun).value = oEquityCurve.GetReturn
.Range("D" & intRun).value = oEquityCurve.GetReturnOverDrawdown
Next intRun
End With
wsCalc.Calculate
lRow = wsCalc.Range("A1").Cells.CurrentRegion.Rows.Count
oResult.equity = dblBeginEquity
oResult.Ruin = intRiskOfRuinCount / mintTotalRuns
'provide aggregated statistics of all the runs for this equity curve over all the simulations
oResult.MedianProfit = Application.WorksheetFunction.Median(wsCalc.Range("A1:A" & lRow)) - dblBeginEquity
oResult.MedianDrawdown = Application.WorksheetFunction.Median(wsCalc.Range("B1:B" & lRow))
oResult.MedianReturn = Application.WorksheetFunction.Median(wsCalc.Range("C1:C" & lRow))
oResult.MedianReturnDD = Application.WorksheetFunction.Median(wsCalc.Range("D1:D" & lRow))
Set fncProcessStartEquity = oResult
' this removes the temporary worksheet
Call DeleteSheetIfExists(STR_CALC_SHEET)
Set oEquityCurve = Nothing
Set wsCalc = Nothing
Set oResult = Nothing
End Function
Private Function fncBuildEquityCurve(ByVal dblBeginEquity As Double, ByVal intTradesInYear As Integer, _
ByVal vntTradeList As Variant, Optional ByVal intLotSize As Integer = DEFAULT_LOTSIZE) As clsEquityCurve
' Purpose: This takes a list of trade PNL, then it picks at random a trade PNL value in order to construct an equity curve
' it does this for the number times specified for how many trades are likely to be placed in a year (intTradesInYear)
' dblBeginEquity = this the start of the base to build the equity curve
' inTradesInYear = number of trades you expect to make per year
' vntTradeList = list of trade result PNL for existing trades made (one dimensional array)
' intLotSize = quantity of the asset traded (default is 1 per trade result)
Dim intTrades As Integer
Dim dblTradevalue As Double
Dim intTradenumber As Integer
Dim oEquityCurve As New clsEquityCurve
oEquityCurve.InitializeStartEquity (dblBeginEquity)
For intTrades = 1 To intTradesInYear
Randomize (Time)
intTradenumber = Application.WorksheetFunction.RandBetween(LBound(vntTradeList), UBound(vntTradeList))
dblTradevalue = vntTradeList(intTradenumber)
If oEquityCurve.EquityAmount < mdblMargin Then
oEquityCurve.IsRuined = True
GoTo Exit_Function
End If
oEquityCurve.Add (intLotSize * dblTradevalue)
Next
Exit_Function:
Set fncBuildEquityCurve = oEquityCurve
Set oEquityCurve = Nothing
End Function
Private Sub DeleteSheetIfExists(ByVal strName As String)
'Purpose: Removes interim sheets used in the calculation
Dim shtObj As Worksheet
Dim blnAlerts As Boolean
blnAlerts = Application.DisplayAlerts
Application.DisplayAlerts = False
For Each shtObj In ThisWorkbook.Worksheets
If shtObj.name = strName Then
shtObj.Delete
Exit For
End If
Next shtObj
Application.DisplayAlerts = blnAlerts
Set shtObj = Nothing
End Sub