Skip to content

Commit

Permalink
Fixed Short put spread ratio issue (#15)
Browse files Browse the repository at this point in the history
* Minor bug fixes, and code improvements

* added trade numbers to results dataframe

* refactored data.py, filter.py, fixed short put spreads
  • Loading branch information
michaelchu authored Nov 16, 2018
1 parent fc83398 commit c3ff033
Show file tree
Hide file tree
Showing 21 changed files with 614 additions and 629 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ MANIFEST

/data/
/tests/.pytest_cache/
/strategies/data/
/strategies/results/

13 changes: 10 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
env:
global:
- CC_TEST_REPORTER_ID=9a990fea3fb57063b45010735b989c837fbf4b5da5d4bdfeafb89539e2b61d19
language: python
python:
- "3.6"
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
# command to install dependencies
install:
- pip install -r requirements.txt
- pip install codecov
- pip install coverage
- pip install pytest pytest-cov
# command to run tests
script:
- pytest --cov=./

after_success:
- codecov
after_script:
- ./cc-test-reporter after-build -t coverage.py --debug --exit-code $TRAVIS_TEST_RESULT
38 changes: 15 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![Downloads](https://pepy.tech/badge/optopsy)](https://pepy.tech/project/optopsy)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2de8f5b3fa2742de93fb60b3a1ae5683)](https://app.codacy.com/app/michaelchu/optopsy?utm_source=github.com&utm_medium=referral&utm_content=michaelchu/optopsy&utm_campaign=badger)
[![Maintainability](https://api.codeclimate.com/v1/badges/37b11e992a6900d30310/maintainability)](https://codeclimate.com/github/michaelchu/optopsy/maintainability)
[![Build Status](https://travis-ci.org/michaelchu/optopsy.svg?branch=master)](https://travis-ci.org/michaelchu/optopsy)
[![codecov](https://codecov.io/gh/michaelchu/optopsy/branch/master/graph/badge.svg)](https://codecov.io/gh/michaelchu/optopsy)
[![Test Coverage](https://api.codeclimate.com/v1/badges/37b11e992a6900d30310/test_coverage)](https://codeclimate.com/github/michaelchu/optopsy/test_coverage)

# Optopsy

Expand Down Expand Up @@ -35,23 +35,20 @@ the rapid development of complex options trading strategies.
* Spread delta
* Spread price

### Planned Features
* Indicator Support - Create entry and exit rules based on indicators
* Optimizer - Allows users to run multiple backtests with different combinations of parameters
* Option strategy support:
* Single Calls/Puts
* Vertical Spreads
* Iron Condors (Iron Butterflies)
* Covered Stock
* Combos (Synthetics/Collars)
* Diagonal Spreads
* Calendar Spreads
* Custom Spreads
* Strangles
* Straddles
### Option strategy support
* Single Calls/Puts
* Vertical Spreads
* (Coming Soon) Iron Condors (Iron Butterflies)
* (Coming Soon) Covered Stock
* (Coming Soon) Combos (Synthetics/Collars)
* (Coming Soon) Diagonal Spreads
* (Coming Soon) Calendar Spreads
* (Coming Soon) Custom Spreads
* (Coming Soon) Strangles
* (Coming Soon) Straddles

### Dependencies
You will need Python 3.6.x. It is recommended to install [Miniconda3](https://conda.io/miniconda.html). See [requirements.txt](https://github.com/michaelchu/optopsy/blob/master/requirements.txt) for full details.
You will need Python 3.6.x and Pandas 0.23.1 or newer. It is recommended to install [Miniconda3](https://conda.io/miniconda.html). See [requirements.txt](https://github.com/michaelchu/optopsy/blob/master/requirements.txt) for full details.

### Installation
```
Expand All @@ -69,7 +66,6 @@ In order to use it, you will need to define the struct variable to map the colum
First we import the library and other nessesary libaries:
```python
import optopsy as op
import os
from datetime import datetime
```

Expand Down Expand Up @@ -112,13 +108,9 @@ def run_strategy():
start = datetime(2016, 1, 1)
end = datetime(2016, 12, 31)

# create the option spread that matches the entry filters
# create the option spreads that matches the entry filters
trades = op.strategies.short_call_spread(data, start, end, filters)

# we get a dataframe of our orders based on the entry filter rules, let's export
# this to csv file for reference
trades.to_csv('./strategies/results/trades.csv')

# call the run method with our data, option spreads and filters to run the backtest
backtest = op.run(data, trades, filters)

Expand Down
1 change: 0 additions & 1 deletion optopsy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from .data import get as get
from .data import gets as gets
from .enums import *
from .backtest import run
import optopsy.option_strategies as strategies
106 changes: 44 additions & 62 deletions optopsy/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,73 +22,60 @@
from .option_queries import opt_type
from .statistics import *

pd.set_option('display.expand_frame_repr', False)

sort_by = [
'underlying_symbol',
'quote_date',
'option_type',
'expiration',
'strike'
]
pd.set_option("display.expand_frame_repr", False)

on = [
'underlying_symbol',
'option_type',
'expiration',
'strike'
]

on = ["underlying_symbol", "option_type", "expiration", "strike"]

default_entry_filters = {
"std_expr": False,
"contract_size": 10,
"entry_dte": (27, 30, 31),
"exit_dte": None
"exit_dte": None,
}

output_cols = {
'quote_date_entry': 'entry_date',
'quote_date_exit': 'exit_date',
'delta_entry': 'entry_delta',
'underlying_price_entry': 'entry_stk_price',
'underlying_price_exit': 'exit_stk_price',
'dte_entry': 'DTE'
"quote_date_entry": "entry_date",
"quote_date_exit": "exit_date",
"delta_entry": "entry_delta",
"underlying_price_entry": "entry_stk_price",
"underlying_price_exit": "exit_stk_price",
"dte_entry": "dte",
}

output_format = [
'entry_date',
'exit_date',
'expiration',
'DTE',
'ratio',
'contracts',
'option_type',
'strike',
'entry_delta',
'entry_stk_price',
'exit_stk_price',
'entry_opt_price',
'exit_opt_price',
'entry_price',
'exit_price',
'profit'
"entry_date",
"exit_date",
"expiration",
"underlying_symbol",
"dte",
"ratio",
"contracts",
"option_type",
"strike",
"entry_delta",
"entry_stk_price",
"exit_stk_price",
"entry_opt_price",
"exit_opt_price",
"entry_price",
"exit_price",
"profit",
]


def _create_legs(data, leg):
return (
data
.pipe(opt_type, option_type=leg[0])
.assign(ratio=leg[1])
)
return data.pipe(opt_type, option_type=leg[0]).assign(ratio=leg[1])


def _apply_filters(legs, filters):
if not filters:
return legs
else:
return [reduce(lambda l, f: getattr(fil, f)(l, filters[f], idx), filters, leg)
for idx, leg in enumerate(legs)]
return [
reduce(lambda l, f: getattr(fil, f)(l, filters[f], idx), filters, leg)
for idx, leg in enumerate(legs)
]


def _filter_data(data, filters):
Expand All @@ -101,37 +88,32 @@ def create_spread(data, leg_structs, filters):

# merge and apply leg filters to create spread
filters = {**default_entry_filters, **filters}
entry_filters = {f: filters[f]
for f in filters if (not f.startswith('entry_spread') and
not f.startswith('exit_'))}
entry_filters = {
f: filters[f]
for f in filters
if (not f.startswith("entry_spread") and not f.startswith("exit_"))
}
spread = _filter_data(legs, entry_filters)

# apply spread level filters to spread
spread_filters = {f: filters[f]
for f in filters if f.startswith('entry_spread')}
return _filter_data(spread, spread_filters).sort_values(sort_by)
spread_filters = {f: filters[f] for f in filters if f.startswith("entry_spread")}
return _filter_data(spread, spread_filters)


# this is the main function that runs the backtest engine
def run(data, trades, filters, init_balance=10000, mode='midpoint'):
trades = trades if isinstance(trades, list) else [trades]

# merge trades from multiple underlying symbols if applicable
all_trades = pd.concat(trades).sort_values(sort_by)

def run(data, trades, filters, init_balance=10000, mode="midpoint"):
# for each option to be traded, determine the historical price action
filters = {**default_entry_filters, **filters}
exit_filters = {f: filters[f] for f in filters if f.startswith('exit_')}
exit_filters = {f: filters[f] for f in filters if f.startswith("exit_")}
res = (
pd
.merge(all_trades, data, on=on, suffixes=('_entry', '_exit'))
pd.merge(trades, data, on=on, suffixes=("_entry", "_exit"))
.pipe(_filter_data, exit_filters)
.pipe(calc_entry_px, mode)
.pipe(calc_exit_px, mode)
.pipe(calc_pnl)
# .pipe(calc_running_balance, init_balance)
.rename(columns=output_cols)
.sort_values(['entry_date', 'expiration', 'underlying_symbol', 'strike'])
.sort_values(["entry_date", "expiration", "underlying_symbol", "strike"])
.pipe(assign_trade_num)
)

return calc_total_profit(res), res[output_format]
Loading

0 comments on commit c3ff033

Please sign in to comment.