mirror of
https://github.com/wassname/options_backtester.git
synced 2026-07-17 11:30:46 +08:00
81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
import pandas as pd
|
|
|
|
|
|
class Strangle:
|
|
def __init__(self,
|
|
underlying,
|
|
strike,
|
|
dte,
|
|
strike_diff,
|
|
shares_per_contract=100,
|
|
capital=1000000.0):
|
|
self.underlying = underlying
|
|
self.strike = strike
|
|
self.dte = dte
|
|
self.strike_diff = strike_diff
|
|
self.inventory = set()
|
|
self.shares_per_contract = shares_per_contract
|
|
self.capital = capital
|
|
|
|
def execute_entry(self, date, group):
|
|
calls = group.loc[(group.type == 'call')
|
|
& (group.strike >= self.strike[0]) &
|
|
(group.strike <= self.strike[1]) &
|
|
(group.dte >= self.dte[0])
|
|
& (group.dte <= self.dte[1])]
|
|
puts = group.loc[group.type == 'put']
|
|
merge = calls.merge(puts, on=['dte'], suffixes=('_call', '_put'))
|
|
merge['ask_sum'] = merge['ask_call'] + merge['ask_put']
|
|
merge['strike_diff'] = abs(merge['strike_call'] - merge['strike_put'])
|
|
merge_strangle = merge.loc[merge['strike_diff'] <= self.strike_diff]
|
|
if merge_strangle.empty:
|
|
return
|
|
entry_index = merge_strangle['ask_sum'].idxmin()
|
|
entry = merge_strangle.loc[entry_index]
|
|
cost = sum([entry['ask_sum'] * self.shares_per_contract])
|
|
if cost <= self.capital:
|
|
self.capital -= cost
|
|
self.inventory.add((entry.optionroot_call, entry.dte))
|
|
self.inventory.add((entry.optionroot_put, entry.dte))
|
|
self._update_trade_log(date, entry.optionroot_call,
|
|
entry.type_call,
|
|
-entry.ask_call * self.shares_per_contract)
|
|
self._update_trade_log(date, entry.optionroot_put, entry.type_put,
|
|
-entry.ask_put * self.shares_per_contract)
|
|
|
|
def execute_exits(self, inventory, date, group):
|
|
exits = []
|
|
remove_set = set()
|
|
for entry in inventory:
|
|
exit = group.loc[(group.optionroot == entry[0]) & (group.dte == 1)]
|
|
if not exit.empty:
|
|
exits.append(exit)
|
|
remove_set.add(entry)
|
|
for exit in exits:
|
|
profit = exit.bid.values[0] * self.shares_per_contract
|
|
contract = exit.optionroot.values[0]
|
|
type_ = exit.type.values[0]
|
|
self.capital += profit
|
|
self._update_trade_log(date, contract, type_, profit)
|
|
self.inventory.difference_update(remove_set)
|
|
|
|
def run(self, data):
|
|
self.trade_log = pd.DataFrame(
|
|
columns=["date", "contract", "type", "profit", "capital"])
|
|
|
|
for date, group in self._iter_dates(data):
|
|
self.execute_entry(date, group)
|
|
self.execute_exits(self.inventory, date, group)
|
|
|
|
return self.trade_log
|
|
|
|
def _update_trade_log(self, date, contract, type_, profit):
|
|
"""Adds entry for the given order to `self.trade_log`."""
|
|
self.trade_log.loc[len(
|
|
self.trade_log)] = [date, contract, type_, profit, self.capital]
|
|
|
|
def _iter_dates(self, data):
|
|
"""Returns `pd.DataFrameGroupBy` with the given underlying and with contracts grouped by date"""
|
|
df = data._data.loc[data._data.underlying == self.underlying]
|
|
return df.groupby(data.schema["date"])
|