Git Product home page Git Product logo

optionsuite's Introduction

OptionSuite

Option / stock strategy backtester and live trader* framework.

Getting started decribes what you need in order to get started backtesting.

Please note that you need to purchase a data package in order to use this library since the sample data is quite limited.

Objective

The objective of the OptionSuite library is to create a general framework to backtest options strategies and to be extensible enough to handle live trading.

*Live trader is currently not supported, but the general framework is in place to enable support for live trading.

Overview of Library

The library is designed in a modular way, and several abstractions are provided which allow the user to add additional features. The directory structure of the library is as follows:

base - contains the abstract options class (option.py) and the two derived classes (call.py and put.py), which serve as the general options types for all other classes.

dataHandler - contains the abstract class (dataHandler.py), which is set up to handle loading option data from different sources. The framework has been tested with CSV data, and a CSV data handler class (csvData.py) is provided to load tick data provided through the CSV format. Any example CSV format is provided in the sampleData directory. More information on the CSV data needed for back testing is covered in the getting started section section.

dataProviders.json - this file configures the columns of the CSV data source that you are using for the backtest.

pricingConfig.json - this file configures the commission and fee structure for the brokerage used.

events - the entire library / framework is event driven, and the abstract event class (event.py) handles the creation and deletion of events. The framework currently supports two different types of events: tick events (tickEvent.py) and signal events (signalEvent.py). The tick events are used to load data from the dataHandler and create the base option types (puts and calls). Signal events are generated to indicate that the criteria for the strategy in strategyManager has been successfully met.

optionPrimitives - option primitives allow for naked puts and calls as well as combinations of puts and calls. For example, an option primtive could describe a naked put, or it could describe a strangle, which is a combination of puts and calls. Since certain trades like strangles are common, the option primitives abstract class (optionPrimitive.py) wraps the base types (calls and puts), and describes the functionality needed to create and update the primitive. The strangle primitive (strangle.py) and put vertical primitive (putVertical.py) are fully functional.

portfolioManager - the portfolio manager (portfolio.py) holds and manages all of the open positions. Potential positions are first generated by the strategyManager, and the portfolio manager opens a new positions if all risk paramters have been met. The portfolio is current held in memory, but a future implementation would ideally store the positions into a database or other non-volatile source.

sampleData - a single file aapl_sample_ivolatility.csv is provided. This file is used for several of the units tests in the different classes, and it also serves as an example of the CSV data format provided by iVolatility.

strategyManager - the strategy manager module provides an abstract class (strategy.py) which defines the basic parameters needed for an options strategy. The purpose of the strategy manager module is to filter the incoming tick data (by means of a tick event) to determine if a position should be opened (which would in turn fire a signal event). A strangle stategy (StrangleStrat.py) and put vertical strategy (putVerticalOnDownMoveStrat.py) are provided.

riskManagement - the risk management module is used to determine when the trade should be exited. The risk management flag is stored along with each trade and accessed in the portfolio.

backTester.py - this is the "main" method for the library. It sets up all parameters for a backtesting session, and initializes the dataHandler class, the portfolioManager class, the strategyManager class, and the riskManagement class. It is helpful to start with this file to see how all of the modules work together.

Getting Started

The library has been tested with Python 3.0+

To get started, you need a Python environment with pandas installed. You'll also need some historical data for the backtests.

Getting the Data

The combinedCSV.csv file used during development and testing contains SPX data from 1990 to 2017 provided by iVolatility. If you'd like to use the same dataset I did, then you want to request the EOD Raw IV dataset for SPX.

There is a 10% discount on all orders greater than $100 if you use code SupraCV10PCTOFF in the "Please tell us what data you want to receive:" field.

You can request different time periods. A large time period such as 1990 to 2017 is broken up into multiple CSVs.

Loading the Data

Once you have downloaded the data, simply update the two lines below, and you are ready to run backTester.py.

dataProvider = 'iVolatility'
filename = '/Users/msantoro/PycharmProjects/Backtester/marketData/iVolatility/SPX/combinedCSV.csv'

Troubleshooting

Please send bugs or any other issues you encounter to [email protected]. I will do my best to help you get up and running. You can also report an issue using GitHub's issue tracker.

optionsuite's People

Contributors

egoebelbecker avatar sirnfs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

optionsuite's Issues

WARNING:root:No matching PUT was found in the option chain for the strangle.

Hey guys,
Terrific package, thank you for open-sourcing!
I have been trying to make a test run with the AAPL dataset you kindly provided & the preset strangle strategy... When running it logs the following:

WARNING:root:No matching PUT was found in the option chain for the strangle.
WARNING:root:No matching CALL was found in the option chain for the strangle.
INFO:root:Net liq: 1000000.000000.
INFO:root:The AAPL position was closed.

went through the individual modules, but could not discern the problem / haven´t made any changes to the code so far.
He seems to be doing only two iterations in total when handling events
Any idea what might be the issue?

Thank you!

Using Backtester.py for put options

Michael,
Great program!
My question is how should you change the strangle strategy to just perform a put strategy? I am new to backtesting with options and I could not quite follow how to use the program with different strategies. I am interested in developing a protective put strategy with a covered call strategy, but with different delta, ivolatilities, etc. So, I can run two separate models and then combine the results.

Detection of monthly option in case of a holiday

Great library!

I don't think the detection of a monthly option is 100% correct, as the implementation is different than the doc string (which is correct).

In file StrangleStrat.py:

    def isMonthlyExp(self, dateTime):
        """
        Check if the option expiration falls on the third Friday of the month, or 
        if the third Friday is a holiday, check if the expiration falls on the Thursday that precedes it.
        :param dateTime: option expiration date in mm/dd/yy format.
        :return: True if it's a monthly option; False otherwise.
        """
        return (dateTime.weekday() == 4 and 14 < dateTime.day < 22)

The implementation identifies a Monthly expiration as a Friday in the 3rd week. So it actually won't detect if that day would be a holiday and the expiration is moved back 1 business day, which is what would happen if the 3rd Fri was a holiday per the CBOE website at least for SPX options: (https://www.cboe.com/tradable_products/sp_500/spx_options/):
"In the case of a holiday on the settlement date, the settlement date is moved back one business day (e.g. from Friday to Thursday), with the exception of Monday Weeklys, where the settlement date will move forward one business day (i.e. Monday to Tuesday). In addition, no SPX EOW, Wednesday Weeklys, or Monday Weeklys will be listed that would have an expiration date that coincides with the expiration date of a traditional SPX option or SPX EOM option."

So since a Wed Weekly would move back to Tues if Wed is a holiday (it wouldn't move to Thurs), and no Weekly can have a same expiration as a Monthly, and an EOM cannot expire in the 3rd week, it seems to me it's safe to detect a Thurs expiration in the 3rd week ALSO as a Monthly expiration, and thus OR that to the above condition in the function.

Or just use the SPXW (vs SPX) option symbol identifier in the iVolatility data to identify the Weeklys...

DTE vs expiration date

There seems to be a mixup between DTE (days to expiration) and expiration date that can lead to confusion.

In file option.py in base folder, there is a method to calculate the # of days left to expiration:

    def getNumDaysLeft(self):
        '''
        Determine the number of days between the curDateTime and the expDateTime.
        curDateTime: current date in mm/dd/yy format.
        expDateTime: option expiration date in mm/dd/yy format.
        :return: number of days between curDateTime and expDateTime.
        '''
        curDateTime = self.getDateTime()
        expDateTime = self.getDTE()
        return (expDateTime - curDateTime).days

So self.getDTE() which just returns self.__DTE should be the expiration date.
But in the unit test optionTest.py the DTE argument is interpreted as 'days to expiration' (45).

classObj = Option('SPY', 250, 'PUT', 0.3, 45)

DTE is unused in the test.

Based on how the data is read in, in dataHandler/csvData.py DTE is a EST-to-UTC-converted expiration date/time:

                local = pytz.timezone('US/Eastern')
                # Convert time zone of data 'US/Eastern' to UTC time.
                # Try and except here to handle two or four digit year format.
                try:
                    DTE = datetime.datetime.strptime(inputData['option_expiration'], "%m/%d/%y")
                except:
                    DTE = datetime.datetime.strptime(inputData['option_expiration'], "%m/%d/%Y")

                DTE = local.localize(DTE, is_dst=None)
                DTE = DTE.astimezone(pytz.utc)

So DTE seems a misnomer and would better be called expDateTime to avoid confusion.
Similarly in option.py: getDTE() would be better named getExpDateTime()

Note that for the strangle strategy the code actually works since result from option.getDTE() is interpreted as expDateTime in the arguments to hasMinimumDTE, and self.getMinimumDTE actually returns a 'days to expiration' within this method:

if not self.getMinimumDTE() == None:
                    # we have a min DTE requirement -> does this option meet min DTE requirement?
                    if not self.hasMinimumDTE(option.getDateTime(), option.getDTE()):
                        continue
  def hasMinimumDTE(self, curDateTime, expDateTime):
      """"
      Determine if the current expiration date of the option is >= self.minimumDTE days from the current date.
      :param curDateTime: current date in mm/dd/yy format.
      :param expDateTime: option expiration date in mm/dd/yy format.
      :return: True if difference between current date and dateTime is >= self.minimumDTE; else False.
      """
      return (expDateTime - curDateTime).days >= self.getMinimumDTE()

KeyError: 'symbol'

Hi Thanks for this software, I am excited to get it to work. Currently having this issue. Not sure why. Can anyone help? I used the sample iVolatility data and just trying to get the default backTester.py to work.


Empty Traceback (most recent call last)
in run(session)
3 try:
----> 4 event = session.eventQueue.get(False)
5 except queue.Empty:

/opt/anaconda3/lib/python3.8/queue.py in get(self, block, timeout)
166 if not self._qsize():
--> 167 raise Empty
168 elif timeout is None:

Empty:

During handling of the above exception, another exception occurred:

KeyError Traceback (most recent call last)
/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
3079 try:
-> 3080 return self._engine.get_loc(casted_key)
3081 except KeyError as err:

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'symbol'

The above exception was the direct cause of the following exception:

KeyError Traceback (most recent call last)
in
33
34 # Run the session.
---> 35 run(session)
36
37 # Write position monitoring to CSV file.

in run(session)
5 except queue.Empty:
6 #Get data for tick event.
----> 7 if not session.dataHandler.getNextTick():
8 # Get out of infinite while loop; no more data available.
9 break

~/OptionSuite-master 2/dataHandler/csvData.py in getNextTick(self)
229 return False
230 # Convert optionChain from a dataframe to Option class objects.
--> 231 optionChainObjs = self.__createBaseType(optionChain)
232 # Create tick event with option chain objects.
233 event = tickEvent.TickEvent()

~/OptionSuite-master 2/dataHandler/csvData.py in __createBaseType(self, optionChain)
175 raise ValueError('Symbol for put / call in dataProviders.json not found in optionType dataframe column.')
176 else:
--> 177 optionFieldDict[option_column_name] = row[dataframe_column_name]
178
179 if optionFieldDict['bidPrice'] is not None and optionFieldDict['askPrice'] is not None:

/opt/anaconda3/lib/python3.8/site-packages/pandas/core/series.py in getitem(self, key)
851
852 elif key_is_scalar:
--> 853 return self._get_value(key)
854
855 if is_hashable(key):

/opt/anaconda3/lib/python3.8/site-packages/pandas/core/series.py in _get_value(self, label, takeable)
959
960 # Similar to Index.get_value, but we do not fall back to positional
--> 961 loc = self.index.get_loc(label)
962 return self.index._get_values_for_loc(self, loc, label)
963

/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
3080 return self._engine.get_loc(casted_key)
3081 except KeyError as err:
-> 3082 raise KeyError(key) from err
3083
3084 if tolerance is not None:

KeyError: 'symbol'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.