mirror of
https://github.com/wassname/options_backtester.git
synced 2026-06-27 19:31:36 +08:00
Fixed monthly iteration. Changed default enter and exit profit thresholds to math.inf
This commit is contained in:
@@ -35,7 +35,12 @@ class HistoricalOptionsData:
|
||||
|
||||
def iter_months(self):
|
||||
"""Returns `pd.DataFrameGroupBy` that groups contracts by month"""
|
||||
return self._data.groupby(pd.Grouper(key=self.schema["date"], freq="MS"))
|
||||
date_col = self.schema["date"]
|
||||
iterator = self._data.groupby(
|
||||
pd.Grouper(key=date_col,
|
||||
freq="MS")).apply(lambda g: g[g[date_col] == g[date_col].min()]).reset_index(drop=True).groupby(
|
||||
pd.Grouper(key=date_col, freq="MS"))
|
||||
return iterator
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""Pass method invocation to `self._data`"""
|
||||
@@ -51,8 +56,11 @@ class HistoricalOptionsData:
|
||||
return method
|
||||
|
||||
def __getitem__(self, item):
|
||||
key = self.schema[item]
|
||||
return self._data[key]
|
||||
if isinstance(item, pd.Series):
|
||||
return self._data[item]
|
||||
else:
|
||||
key = self.schema[item]
|
||||
return self._data[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._data[key] = value
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import math
|
||||
from collections import namedtuple
|
||||
from functools import reduce
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
from functools import reduce
|
||||
from backtester.datahandler import Schema
|
||||
from backtester.option import Direction
|
||||
from .strategy_leg import StrategyLeg
|
||||
@@ -17,7 +18,6 @@ class Strategy:
|
||||
Takes in a number of `StrategyLeg`'s (option contracts), and filters that determine
|
||||
entry and exit conditions.
|
||||
"""
|
||||
|
||||
def __init__(self, schema, qty=1, shares_per_contract=100):
|
||||
assert isinstance(schema, Schema)
|
||||
self.schema = schema
|
||||
@@ -62,12 +62,12 @@ class Strategy:
|
||||
self.conditions.append(Condition(fields, legs, tolerance))
|
||||
return self
|
||||
|
||||
def add_exit_thresholds(self, profit_pct=0.0, loss_pct=0.0):
|
||||
def add_exit_thresholds(self, profit_pct=math.inf, loss_pct=math.inf):
|
||||
"""Adds maximum profit/loss thresholds. Both **must** be >= 0.0
|
||||
|
||||
Args:
|
||||
profit_pct (float, optional): Max profit level. Defaults to 0.0
|
||||
loss_pct (float, optional): Max loss level. Defaults to 0.0
|
||||
profit_pct (float, optional): Max profit level. Defaults to math.inf
|
||||
loss_pct (float, optional): Max loss level. Defaults to math.inf
|
||||
"""
|
||||
assert profit_pct >= 0
|
||||
assert loss_pct >= 0
|
||||
@@ -85,10 +85,8 @@ class Strategy:
|
||||
"""
|
||||
|
||||
# Remove contracts already in inventory
|
||||
inventory_contracts = pd.concat(
|
||||
[inventory[leg.name]['contract'] for leg in self.legs])
|
||||
subset_options = options[~options[self.schema['contract']].
|
||||
isin(inventory_contracts)]
|
||||
inventory_contracts = pd.concat([inventory[leg.name]['contract'] for leg in self.legs])
|
||||
subset_options = options[~options[self.schema['contract']].isin(inventory_contracts)]
|
||||
|
||||
return self._filter_legs(subset_options, Signal.ENTRY)
|
||||
|
||||
@@ -103,20 +101,16 @@ class Strategy:
|
||||
pd.DataFrame: Exit signals
|
||||
"""
|
||||
|
||||
underlying_col, spot_col = self.schema['underlying'], self.schema[
|
||||
'underlying_last']
|
||||
underlying_symbols = options.loc[:, (
|
||||
underlying_col, spot_col)].drop_duplicates(underlying_col)
|
||||
underlying_col, spot_col = self.schema['underlying'], self.schema['underlying_last']
|
||||
underlying_symbols = options.loc[:, (underlying_col, spot_col)].drop_duplicates(underlying_col)
|
||||
spot_prices = underlying_symbols.set_index(underlying_col).to_dict()
|
||||
|
||||
leg_candidates = [
|
||||
self._exit_candidates(l.direction, inventory[l.name], options,
|
||||
spot_prices) for l in self.legs
|
||||
self._exit_candidates(l.direction, inventory[l.name], options, spot_prices) for l in self.legs
|
||||
]
|
||||
|
||||
total_costs = sum([l['cost'] for l in leg_candidates])
|
||||
threshold_exits = self._filter_thresholds(inventory['totals']['cost'],
|
||||
total_costs)
|
||||
threshold_exits = self._filter_thresholds(inventory['totals']['cost'], total_costs)
|
||||
|
||||
filter_mask = []
|
||||
for i, leg in enumerate(self.legs):
|
||||
@@ -124,12 +118,11 @@ class Strategy:
|
||||
filter_mask.append(flt(leg_candidates[i]))
|
||||
fields = self._signal_fields((~leg.direction).value)
|
||||
leg_candidates[i] = leg_candidates[i].loc[:, fields.values()]
|
||||
leg_candidates[i].columns = pd.MultiIndex.from_product(
|
||||
[["leg_{}".format(i + 1)], leg_candidates[i].columns])
|
||||
leg_candidates[i].columns = pd.MultiIndex.from_product([["leg_{}".format(i + 1)],
|
||||
leg_candidates[i].columns])
|
||||
|
||||
totals = pd.DataFrame.from_dict({"cost": total_costs})
|
||||
totals.columns = pd.MultiIndex.from_product([["totals"],
|
||||
totals.columns])
|
||||
totals.columns = pd.MultiIndex.from_product([["totals"], totals.columns])
|
||||
leg_candidates.append(totals)
|
||||
filter_mask = reduce(lambda x, y: x | y, filter_mask)
|
||||
exits_mask = threshold_exits | filter_mask
|
||||
@@ -212,12 +205,10 @@ class Strategy:
|
||||
|
||||
cost = sum(leg["cost"] for leg in dfs)
|
||||
totals = pd.DataFrame.from_dict({"cost": cost})
|
||||
totals.columns = pd.MultiIndex.from_product([["totals"],
|
||||
totals.columns])
|
||||
totals.columns = pd.MultiIndex.from_product([["totals"], totals.columns])
|
||||
|
||||
for i in range(len(dfs)):
|
||||
dfs[i].columns = pd.MultiIndex.from_product(
|
||||
[["leg_{}".format(i + 1)], dfs[i].columns])
|
||||
dfs[i].columns = pd.MultiIndex.from_product([["leg_{}".format(i + 1)], dfs[i].columns])
|
||||
|
||||
dfs.append(totals)
|
||||
|
||||
@@ -242,9 +233,7 @@ class Strategy:
|
||||
# the daily data the values will all be NaN and the filters should all yield False.
|
||||
fields = self._signal_fields((~direction).value)
|
||||
options = options.rename(columns=fields)
|
||||
candidates = inventory_leg[['contract']].merge(options,
|
||||
how='left',
|
||||
on='contract')
|
||||
candidates = inventory_leg[['contract']].merge(options, how='left', on='contract')
|
||||
|
||||
order = get_order(direction, Signal.EXIT)
|
||||
candidates['order'] = order.name
|
||||
@@ -276,5 +265,4 @@ class Strategy:
|
||||
return (excess_return >= profit_pct) | (excess_return <= -loss_pct)
|
||||
|
||||
def __repr__(self):
|
||||
return "Strategy(legs={}, conditions={})".format(
|
||||
self.legs, self.conditions)
|
||||
return "Strategy(legs={}, conditions={})".format(self.legs, self.conditions)
|
||||
|
||||
Reference in New Issue
Block a user