Fixed monthly iteration. Changed default enter and exit profit thresholds to math.inf

This commit is contained in:
Juan Pablo Amoroso
2019-12-27 13:05:11 -03:00
parent 0bbeb18d35
commit 59f16bac08
2 changed files with 29 additions and 33 deletions
@@ -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
+18 -30
View File
@@ -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)