Skip to main content

Timing

MesoSim's timing module lets a strategy respond to the trading calendar: enter near the start of a month, measure time in a position, or act before an options expiration. Use timing expressions in Entry, Adjustment, and Exit.

A schedule determines when MesoSim checks a rule. A timing condition determines whether the rule is satisfied at that check. Configure both in the AI Job Editor; see Schedules for the supported cadence and session offsets.

Practical example

Allow entry during the first three trading sessions of the month, then make the position eligible to exit after five calendar days:

{
"Entry": {
"Conditions": [
"timing.trading_days_after(this_month, first) < 3"
],
"VarDefines": {
"entry_minutes_after_open": "timing.minutes_after_open"
}
},
"Exit": {
"Conditions": [
"timing.days_in_trade >= 5"
],
"VarDefines": {
"exit_days_in_trade": "timing.days_in_trade"
}
}
}

Merge these fields into your strategy, keeping its schedules, structure, sizing, and other required settings. The entry window allows a trade when the other entry requirements are also met. The exit condition is evaluated on the exit schedule; weekends or the chosen check time can mean the actual exit occurs later than five calendar days after entry.

Combine an entry window with other filters

Separate Entry.Conditions items are OR alternatives. If a timing window must also satisfy an existing filter, combine them with and in one expression, preserving any existing parentheses. See Entry conditions.

Variables

Read these values as properties, without parentheses: timing.day_of_week, for example. Calendar functions take arguments and use parentheses. See Script Engine for expression syntax and variable availability.

NameTypeDescription
timing.day_of_weekintCurrent weekday: 0 = Sunday through 6 = Saturday
timing.yearintCurrent year in exchange time
timing.monthintCurrent month, 1–12, in exchange time
timing.dayintCurrent day of the month in exchange time
timing.minutes_after_openintMinutes since market open, rounded down to the backtest's market-data interval
timing.minutes_before_closeintMinutes until market close, rounded down to the backtest's market-data interval
timing.days_in_tradedecimalElapsed calendar days since this position's entry, including fractional days; requires a position

Use comparisons that allow fractional values, such as timing.days_in_trade >= 5, rather than relying on a scheduled check to land exactly on a threshold.

Functions

SignatureDescription
timing.trading_days_until(anchor, [boundary]) -> decimalTrading days from now until the selected anchor boundary
timing.trading_days_after(anchor, [boundary]) -> decimalTrading days elapsed since the selected anchor boundary, including fractional sessions
timing.calendar_days_until(anchor, [boundary]) -> decimalCalendar days from now until the selected anchor boundary
timing.calendar_days_after(anchor, [boundary]) -> decimalCalendar days elapsed since the selected anchor boundary, including fractional days
timing.trading_days_in(anchorA, [anchorB], [a_boundary], [b_boundary]) -> decimalNumber of trading sessions in a period or between the selected boundaries
timing.calendar_days_in(anchorA, [anchorB], [a_boundary], [b_boundary]) -> decimalCalendar-day length of a period or the interval between selected boundaries

Square brackets mark optional arguments; omit the brackets when writing an expression. For period anchors, boundary defaults to first. Both *_days_after functions return 0 until the selected boundary is reached; both *_days_until functions return 0 once it is reached.

With one period anchor, *_days_in measures the whole period. With two anchors, it measures from the first boundary of anchorA to the last boundary of anchorB unless you supply other boundaries. For example, timing.calendar_days_in(this_month, next_month) covers both months; timing.calendar_days_in(this_month, next_month, first, first) covers only the current month.

Calendar days and trading days

Calendar days include weekends and holidays. For example, at September 10 at noon in exchange time:

  • timing.calendar_days_after(this_month, first) is 9.5: elapsed days since September 1 at midnight.
  • timing.calendar_days_after(this_month, last) is 0: the end of the current month has not been reached.
  • timing.calendar_days_after(last_month, last) is 9.5: elapsed days since the end of August.

Trading days follow the instrument's sessions. For equity and index options, weekends and exchange holidays do not add trading days. Fractions are measured against the session's actual length, including early closes. At 10:30 ET on Tuesday, October 8, 2024, timing.trading_days_after(this_week, first) is approximately 1.153846: one complete Monday session plus one hour of Tuesday's 6.5-hour session.

Anchors and boundaries

Anchors are relative to the current simulation date. Write the names directly, without quotes.

PeriodCurrentNextPrevious
Week, starting Mondaythis_weeknext_weeklast_week
Calendar monththis_monthnext_monthlast_month
Calendar quarterthis_quarternext_quarterlast_quarter
Calendar yearthis_yearnext_yearlast_year

The boundary selects a reference point within the chosen period:

BoundaryCalendar functionsTrading functions
firstStart of the period at midnightOpening time of the first trading session in the period
lastStart of the following period at midnightOpening time of the first trading session after the period

For example, last for a calendar month means midnight at the start of the next month. With *_days_after, this boundary is the point from which elapsed time is measured, and the endpoint is always now.

Event anchors

AnchorEvent
monthly_opexCurrent month's standard monthly options expiration, normally the third Friday, adjusted for exchange holidays
vix_settlementCurrent month's VIX settlement event
triple_witchingQuarterly expiration anchor; use in March, June, September, or December
quad_witchingQuarterly expiration anchor; use in March, June, September, or December

Events select a single timestamp, so they do not need a first or last boundary. These event anchors use the opening-time reference, normally 09:30 ET, rather than the session close.

Event anchors stay in the current month

An event anchor does not advance to the next occurrence after its date passes. For example, after this month's OPEX reference time, timing.trading_days_until(monthly_opex) is 0. Include a positive lower bound if a rule should apply only before the event.

To restrict a rule to quarterly expiration months, combine it with (timing.month == 3 or timing.month == 6 or timing.month == 9 or timing.month == 12).

More timing rules

Use these expressions in the relevant condition field and choose a schedule that checks during the intended window.

IntentExpression
Enter on Mondaystiming.day_of_week == 1
Enter when at most one trading day remains before next monthtiming.trading_days_until(next_month, first) <= 1
Act within one trading day before the current month's OPEX, while it is still aheadtiming.trading_days_until(monthly_opex) > 0 and timing.trading_days_until(monthly_opex) <= 1
Allow an adjustment only after two calendar days in the positiontiming.days_in_trade >= 2

Check a timing rule

  1. Set the schedule. Choose session offsets and a cadence supported by the selected market-data resolution. A condition does not add extra evaluation times.
  2. Check the intended dates. Test a short period covering a month boundary, holiday, or expiration relevant to your rule.
  3. Inspect the result. Use the Events Viewer to confirm entry, adjustment, and exit times. Capture a needed timing value with VarDefines to inspect it alongside the events.

For help composing timing expressions, use the AI Job Editor or AI Agents. For full strategies with analysis, visit the Deltaray blog.