The Lua Script engine in MesoSim empowers our users to dynamically set simulator parameters. Due to the exceptional flexibility we are proud to call our Job Definition a Domain Specific Language (DSL) for Options Trading.
In this post we will showcase some of the more complex setups that can be used to create more dynamic strategies.
Calculations using VarDefines
The VarDefines section has been present since the early days of MesoSim and has become one of the cornerstones of strategy analysis. You can define and update variables on Entry, Adjustment, Exit, and at any point in time using the UpdateVarsAdjustment.
Daily PnL
If you would like to track the daily profit and loss for your positions you can do so by setting up and updating two variables:
pnl_until_yesterday
: tracking the PnL until yesterday
daily_pnl
: capturing today's profit.
Later, these variables can be analyzed using the DataVoyager ('Analyze' tab).
Use Entry.VarDefines
to set the initial state of the variables:
"Entry": {
...
"VarDefines": {
"pnl_until_yesterday": "0",
"daily_pnl": "0"
},
...
}
Then set UpdateVarsAdjustment
to calculate the daily PnL:
"Adjustment": {
...,
"ConditionalAdjustments": {
"true -- execute always": {
"UpdateVarsAdjustment": {
"VarDefines": {
"daily_pnl": "pos_pnl - pnl_until_yesterday",
"pnl_until_yesterday": "pos_pnl"
}
}
}
},
"MaxAdjustmentCount": null
}
Example run: https://portal.deltaray.io/backtests/5ca7ee68-93bc-42b6-94c7-fdc0f3dcb92d
Trailing Stop
We already dedicated a full blog post to Trailing Stop, which helped a sideways strategy to become profitable. Here is a quick refresher:
- We keep track of the highest profit achieved by a position (high watermark) using UpdateVarsAdjustment.
- We exit whenever the current position's profit becomes less than x% of the high watermark.
Example run: https://portal.deltaray.io/backtests/b9aff006-60fb-49dc-9a71-9737dd380395
For all the details please check out the Improving Volatility Risk Premium with Trailing Stops blog post.
Maximum Favorable and Adverse Excursion
[Contributed by Rafael M - Thank you!]
MFE and MAE are often used to study the trade's potential.These metrics capture the maximum amount of loss and profit experienced by a trade during it's lifecycle.
Calculating these metrics requires us to keep two variables (MAE and MFE) updated throughout the position. Similar to the High Watermark in a Trailing Stop, the variables should be updated conditionally: whenever a new high or low is reached.
Rafael has chosen to do the conditional variable setting using Lua Ternary operator (more on this later):
"ConditionalAdjustments": {
"true -- execute always": {
"UpdateVarsAdjustment": {
"VarDefines": {
"MFE": "(pos_pnl - MFE > 0) and pos_pnl or MFE",
"MAE": "(pos_pnl - MAE < 0) and pos_pnl or MAE"
}
}
}
},
Example run: https://portal.deltaray.io/backtests/cdf03e3e-cd80-4705-b609-4047f0ad54ac
Higher order greeks with Black Scholes
[ Motivated by Carlos P - Thank you! ]
MesoSim provides the five common Greeks (delta, gamma, theta, vega, rho) as well as Weighted Vega out of the box. What if you would like to use other higher-order Greeks, such as Vomma, Speed, or Charm?
These metrics can be calculated easily using the VarDefines section in Entry, Adjustment, and Exit. Let's take Vomma as an example:
"Entry": {
...
"VarDefines": {
"S": "underlying_price",
"K": "leg_short_put_strike",
"r": "0.045",
"v": "leg_short_put_iv",
"t": "leg_short_put_dte/365",
"q": "0",
"d1": "((log(S / K) + (r - q + 0.5*v*v)*t)) / (v*sqrt(t))",
"d2": "d1 - v * sqrt(t)",
"vomma": "(leg_short_put_vega * d1*d2) / v"
},
...
}
In this example, we're leveraging that VarDefines are evaluated in order of appearance.
With sequential evaluation, we can refer to previously set variables in our calculations.
Example run: https://portal.deltaray.io/backtests/864241b3-33e6-41f6-8010-e3c358a1d9e8
Marking legs for later entry
Some strategies, like the Black Swan Hedge by Ron Bertino, rely on the market state at initiation to construct the full trade structure at a later time. To support this setup we introduced the concept of 'marker leg', which captures the initial state for a subsequently added leg.
Marker legs can be created by specifying Qty=0
at entry:
"Legs": [
...
{
"Name": "long_put_marker",
"Qty": "0",
"ExpirationName": "exp1",
"StrikeSelector": {
"Delta": "15"
}
}
]
Later, the variables associated with the marker leg can be used to enter the real leg when the conditions become favorable. For example:
Enter short_put and mark long_put at a specific delta. Enter when the marker long_put price becomes less than the credit received for short_put leg.
"ConditionalAdjustments": {
"open_trades_cnt == 2 and leg_long_put_marker_price < short_put_entry_price": {
"AddLegsAdjustment": {
"Legs": [
{
"Name": "long_put",
"Qty": "1",
"ExpirationName": "exp1",
"StrikeSelector": {
"Statement": "leg_long_put_marker_strike"
},
"OptionType": "Put"
}
]
}
}
}
You need to set the LegSelectionConstraint
(in SimSettings) to None for this approach to work.
Example run: https://portal.deltaray.io/backtests/af590e6d-792f-44d4-8394-49f817b506abPlease
That the Black Swan Hedge is a proprietary trade and therefore it is not shared here. The above run just shows the required building
blocks to build the BSH.
If - Then - Else in Statements
Probably the most frequently asked question by our seasoned users:"Can I specify a variable conditionally?"
The answer is yes, using Lua's ternary operator.
Consider the following example:
We would like to target $100 as a Profit target in the first 10 days of the trade, then after the 11th day in trade, we reduce our Profit Target to $50.
In Lua the if-then-else type of constructs can be expressed using the ternary semantics:
(condition) and (statement-for-true-condition) or (statement-for-false-condition)
Expressing the above example in Exit.Conditions
:
"Exit": {
...
"Conditions": [
"(days_in_trade <= 10) and (pos_pnl >= 100) or (pos_pnl >= 50)"
]
}
Example run: https://portal.deltaray.io/backtests/82a87cba-96b2-48e1-aed9-a7058e43cc5a
Ternary operator is probably the most versatile construct. It allows you for example to select deltas based on other variables (indicators, internal or external data), enter or exit conditionally.
Randomizing trading schedule
With version v2.10 we converted all the fields in the job definition to Lua Statements. Doing so you are now allowed to change the Schedule programmatically and create schedules dynamically.
As part of strategy robustness test it might be useful to know how your strategy performs if you enter / exit / adjust different times of the day. This can easily be achieved by setting the respective Schedule to random:
"Schedule": {
"AfterMarketOpenMinutes": null,
"BeforeMarketCloseMinutes": "random(30, 210)",
"Every": "day"
},
The statement random(30, 210)
instructs the Script Engine to draw a random number from the range of 30 to 210. We use 210 minutes here to support early closes of the exchange.
With this approach, you just need to run your strategy a couple of times and see how its performance changes with the randomized schedules. This approach is also useful if you would encounter any Missing Data related problems in your runs.
Example run: https://portal.deltaray.io/backtests/3875f455-0639-4463-8b8c-9077acc03624
Processing Event Log for custom analysis
Our users with a paid subscription are capable of extracting Event Logs and OptionNet Explorer exports from runs. The Event Log is particularly interesting if you would like to roll your own analytics as it captures the simulation state in every processing step.
[ Contributed by Brent P - Thank you! ]
To study how Event Log processing can be done we recommend taking a look at Brent's mesosim-stuff repository. It contains post-processers for events and NAVs:
- NAV Combiner to see performance profile of multiple runs combined
- Hedging power studies for various long puts
- PnL by entry to see if vega/theta/delta/rho drives the final PnL
- Delta Hedging using SPX prices (needs modification)
Final words
We hope the above patterns will help your studies related to Options Backtesting in MesoSim.