michaelchu / optopsy

A nimble options backtesting library for Python

Home Page:https://github.com/michaelchu/optopsy/wiki

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to inject custom entry/exit dates?

vjtrost88 opened this issue · comments

Hi Michael,

First off, thank you for sharing your work on this package. It's the only one out there that I've seen dedicated to backtesting options strategies.

I'm curious how I can backtest strategies with my own entry/exit dates. In your wiki, you say "but theoretically, it is possible by injecting a list of entry and exit dates created from external sources based on indicators." After poking around in the code, I struggled to see where to do this and figured asking you might be faster.

Can you point me in the right direction? Thanks

hi @vjtrost88 thank you for trying my library!

In terms of using your own entry/exit dates, currently it is not supported, but I do plan on adding that function in the future.

If you wanted to modify the code yourself to see if it works, my suggestion is this:

In the function below, located inside core.py,

def _evaluate_options(data, **kwargs):

    # trim option chains with strikes too far out from current price
    data = data.pipe(_calculate_otm_pct).pipe(
        _trim, "otm_pct", lower=kwargs["max_otm_pct"] * -1, upper=kwargs["max_otm_pct"],
    )

    # remove option chains that are worthless, it's unrealistic to enter
    # trades with worthless options
    entries = _remove_min_bid_ask(data, kwargs["min_bid_ask"])

    # to reduce unnecessary computation, filter for options with the desired exit DTE
    exits = _get(data, "dte", kwargs["exit_dte"])

    return (
        entries.merge(
            right=exits,
            on=["underlying_symbol", "option_type", "expiration", "strike"],
            suffixes=("_entry", "_exit"),
        )
        # by default we use the midpoint spread price to calculate entry and exit costs
        .assign(entry=lambda r: (r["bid_entry"] + r["ask_entry"]) / 2)
        .assign(exit=lambda r: (r["bid_exit"] + r["ask_exit"]) / 2)
        .pipe(_remove_invalid_evaluated_options)
        .pipe(_calculate_profit_loss)
    )[evaluated_cols]

update the lines where entries( entries = _remove_min_bid_ask(data, kwargs["min_bid_ask"]) ) and exits (exits = _get(data, "dte", kwargs["exit_dte"])) dataframes are being filtered, and filter on the df's quote_dates to equal the entry/exit dates you want to look at (likely from some lists).

so for example, lets say you have a list of entry_dates = [...] and exit_dates = [...],

entries = data[data['quote_date'].isin(entry_dates)]
exits = data[data['quote_date'].isin(exit_dates)]

You may/may not have to figure out how to match up the option on entry to the correct exit on the correct exit dates however, since it's doing a merge between the two dataframes after.

Let me know if that works for you.