├── CHANGELOG ├── LICENSE.txt ├── MANIFEST.in ├── README ├── README.md ├── build_sdist.sh ├── cleanup.sh ├── coverage.cfg ├── doc ├── Makefile ├── _templates │ └── layout.html ├── bar.rst ├── barfeed.rst ├── bitcoin.rst ├── bitcoincharts.rst ├── bitcoincharts_example.rst ├── bitcoincharts_ref.rst ├── bitstamp.rst ├── bitstamp_example.rst ├── bitstamp_ref.rst ├── broker.rst ├── code.rst ├── compinvpart1.rst ├── conf.py ├── dataseries.rst ├── eventprofiler.rst ├── feed.rst ├── images │ ├── execution_running_1.png │ └── queue_execution.png ├── index.rst ├── intro.rst ├── marketsession.rst ├── optimizer.rst ├── plotter.rst ├── sample_bbands.rst ├── sample_market_timing.rst ├── sample_quandl.rst ├── sample_rsi2.rst ├── sample_sma_crossover.rst ├── sample_statarb_erniechan.rst ├── sample_vwap_momentum.rst ├── samples.rst ├── stratanalyzer.rst ├── strategy.rst ├── talib.rst ├── technical.rst ├── tools.rst ├── tutorial.rst ├── twitter.rst ├── twitter_example.rst └── twitter_ref.rst ├── docker ├── Dockerfile └── build.sh ├── orcl-2000.csv ├── pyalgotrade ├── __init__.py ├── bar.py ├── barfeed │ ├── __init__.py │ ├── common.py │ ├── csvfeed.py │ ├── dbfeed.py │ ├── googlefeed.py │ ├── membf.py │ ├── ninjatraderfeed.py │ ├── quandlfeed.py │ ├── resampled.py │ ├── sqlitefeed.py │ └── yahoofeed.py ├── bitcoincharts │ ├── __init__.py │ └── barfeed.py ├── bitstamp │ ├── __init__.py │ ├── barfeed.py │ ├── broker.py │ ├── common.py │ ├── httpclient.py │ ├── livebroker.py │ ├── livefeed.py │ └── wsclient.py ├── broker │ ├── __init__.py │ ├── backtesting.py │ ├── fillstrategy.py │ └── slippage.py ├── dataseries │ ├── __init__.py │ ├── aligned.py │ ├── bards.py │ └── resampled.py ├── dispatcher.py ├── dispatchprio.py ├── eventprofiler.py ├── feed │ ├── __init__.py │ ├── csvfeed.py │ └── memfeed.py ├── logger.py ├── marketsession.py ├── observer.py ├── optimizer │ ├── __init__.py │ ├── base.py │ ├── local.py │ ├── server.py │ ├── worker.py │ └── xmlrpcserver.py ├── plotter.py ├── resamplebase.py ├── stratanalyzer │ ├── __init__.py │ ├── drawdown.py │ ├── returns.py │ ├── sharpe.py │ └── trades.py ├── strategy │ ├── __init__.py │ └── position.py ├── talibext │ ├── __init__.py │ └── indicator.py ├── technical │ ├── __init__.py │ ├── atr.py │ ├── bollinger.py │ ├── cross.py │ ├── cumret.py │ ├── highlow.py │ ├── hurst.py │ ├── linebreak.py │ ├── linreg.py │ ├── ma.py │ ├── macd.py │ ├── ratio.py │ ├── roc.py │ ├── rsi.py │ ├── stats.py │ ├── stoch.py │ └── vwap.py ├── tools │ ├── __init__.py │ ├── googlefinance.py │ ├── quandl.py │ ├── resample.py │ └── yahoofinance.py ├── twitter │ ├── __init__.py │ └── feed.py ├── utils │ ├── __init__.py │ ├── collections.py │ ├── csvutils.py │ ├── dt.py │ └── stats.py ├── warninghelpers.py └── websocket │ ├── __init__.py │ ├── client.py │ └── pusher.py ├── release-checklist.txt ├── samples ├── bbands.output ├── bbands.png ├── bbands.py ├── bccharts_example_1.py ├── bccharts_example_2.png ├── bccharts_example_2.py ├── compinv-1.output ├── compinv-1.py ├── compinv-3.py ├── csvfeed_1.output ├── csvfeed_1.py ├── eventstudy.output ├── eventstudy.png ├── eventstudy.py ├── market_timing.output ├── market_timing.png ├── market_timing.py ├── quandl_sample.output ├── quandl_sample.png ├── quandl_sample.py ├── rsi2.py ├── rsi2_sample.output ├── rsi2_sample.png ├── rsi2_sample.py ├── sample-strategy-analyzer.output ├── sample-strategy-analyzer.py ├── sma_crossover.output ├── sma_crossover.png ├── sma_crossover.py ├── sma_crossover_sample.py ├── statarb_erniechan.output ├── statarb_erniechan.png ├── statarb_erniechan.py ├── technical-1.output ├── technical-1.py ├── tutorial-1.output ├── tutorial-1.py ├── tutorial-2.output ├── tutorial-2.py ├── tutorial-3.output ├── tutorial-3.py ├── tutorial-4.output ├── tutorial-4.py ├── tutorial-5.png ├── tutorial-5.py ├── tutorial-optimizer-local.py ├── tutorial-optimizer-server.py ├── tutorial-optimizer-worker.py ├── tutorial_bitstamp_1.py ├── tutorial_twitter_bitstamp.output ├── tutorial_twitter_bitstamp.py ├── vwap_momentum.output ├── vwap_momentum.png └── vwap_momentum.py ├── setup.cfg ├── setup.py ├── test.py ├── testcases ├── __init__.py ├── bar_test.py ├── barfeed_test.py ├── bitstamp_test.py ├── broker_backtesting_test.py ├── broker_test.py ├── btcharts_test.py ├── common.py ├── csvfeed_test.py ├── dataseries_test.py ├── dbfeed_test.py ├── doc_test.py ├── drawdown_analyzer_test.py ├── eventprofiler_test.py ├── feed_test.py ├── fill_strategy_test.py ├── google_test.py ├── http_server.py ├── logger_test.py ├── logger_test_1.py ├── logger_test_2.py ├── logger_test_3.py ├── memfeed_test.py ├── multi_instrument_strategy_test.py ├── ninjatraderfeed_test.py ├── observer_test.py ├── optimizer_testcase.py ├── plotter_test.py ├── position_test.py ├── pusher_test.py ├── quandl_test.py ├── resample_test.py ├── returns_analyzer_test.py ├── sharpe_analyzer_test.py ├── slippage_model_test.py ├── smacrossover_strategy_test.py ├── strategy_test.py ├── talib_test.py ├── technical_atr_test.py ├── technical_bollinger_test.py ├── technical_cross_test.py ├── technical_cumret_test.py ├── technical_highlow_test.py ├── technical_hurst_test.py ├── technical_linebreak_test.py ├── technical_linreg_test.py ├── technical_ma_test.py ├── technical_macd_test.py ├── technical_ratio_test.py ├── technical_roc_test.py ├── technical_rsi_test.py ├── technical_stats_test.py ├── technical_stoch_test.py ├── technical_test.py ├── technical_trend_test.py ├── technical_vwap_test.py ├── test_strategy.py ├── trades_analyzer_test.py ├── twitter_test.py ├── utils_test.py ├── websocket_server.py ├── yahoo_test.py └── yahoofeed_test.py ├── tools ├── symbols │ ├── get_merval_symbols.py │ ├── get_nasdaq_symbols.py │ ├── get_nyse_symbols.py │ ├── get_sp500_symbols.py │ ├── merval-symbols.txt │ ├── merval.xml │ ├── nasdaq.xml │ ├── nyse.xml │ ├── sp500.xml │ └── symbolsxml.py └── yahoodbfeed │ ├── analyze_gaps.py │ ├── download_data.py │ └── merval_calendar.py └── travis ├── Dockerfile └── run_tests.sh /LICENSE.txt: -------------------------------------------------------------------------------- 1 | PyAlgoTrade 2 | 3 | Copyright 2011 Gabriel Martin Becedillas Ruiz 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | global-exclude .DS_Store *.pyc *.pyo 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/README -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyAlgoTrade3 2 | =========== 3 | 4 | [![Build Status](https://travis-ci.org/gbeced/pyalgotrade.png?branch=master)](https://travis-ci.org/gbeced/pyalgotrade) 5 | [![Coverage Status](https://coveralls.io/repos/gbeced/pyalgotrade/badge.svg?branch=master)](https://coveralls.io/r/gbeced/pyalgotrade?branch=master) 6 | 7 | 8 | This repository is based on [pyalgotrade](https://github.com/gbeced/pyalgotrade). PyAlgoTrade3 is an **event driven algorithmic trading** Python3 library. Although the initial focus 9 | was on **backtesting**, **paper trading** is now possible using: 10 | 11 | * [Bitstamp](https://www.bitstamp.net/) for Bitcoins 12 | 13 | and **live trading** is now possible using: 14 | 15 | * [Bitstamp](https://www.bitstamp.net/) for Bitcoins 16 | 17 | To get started with PyAlgoTrade3 take a look at the [tutorial](http://gbeced.github.io/pyalgotrade/docs/v0.18/html/tutorial.html) and the [full documentation](http://gbeced.github.io/pyalgotrade/docs/v0.18/html/index.html). 18 | 19 | Main Features 20 | ------------- 21 | 22 | * Event driven. 23 | * Supports Market, Limit, Stop and StopLimit orders. 24 | * Supports any type of time-series data in CSV format like Yahoo! Finance, Google Finance, Quandl and NinjaTrader. 25 | * Bitcoin trading support through [Bitstamp](https://www.bitstamp.net/). 26 | * Technical indicators and filters like SMA, WMA, EMA, RSI, Bollinger Bands, Hurst exponent and others. 27 | * Performance metrics like Sharpe ratio and drawdown analysis. 28 | * Handling Twitter events in realtime. 29 | * Event profiler. 30 | * TA-Lib integration. 31 | 32 | Installation 33 | ------------ 34 | 35 | PyAlgoTrade3 is developed using Python 3 and depends on: 36 | 37 | * [NumPy and SciPy](http://numpy.scipy.org/). 38 | * [pytz](http://pytz.sourceforge.net/). 39 | * [dateutil](https://dateutil.readthedocs.org/en/latest/). 40 | * [requests](http://docs.python-requests.org/en/latest/). 41 | * [matplotlib](http://matplotlib.sourceforge.net/) for plotting support. 42 | * [ws4py](https://github.com/Lawouach/WebSocket-for-Python) for Bitstamp support. 43 | * [tornado](http://www.tornadoweb.org/en/stable/) for Bitstamp support. 44 | * [tweepy](https://github.com/tweepy/tweepy) for Twitter support. 45 | 46 | 47 | -------------------------------------------------------------------------------- /build_sdist.sh: -------------------------------------------------------------------------------- 1 | export LC_ALL=en_US.UTF-8 2 | export LANG=en_US.UTF-8 3 | 4 | python setup.py sdist 5 | 6 | -------------------------------------------------------------------------------- /cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | find . -name *.pyc -delete 4 | find . -name .coverage -delete 5 | find . -name .noseids -delete 6 | find . -name htmlcov -exec rm -rf {} \; 7 | 8 | find . -name *.egg-info -exec rm -rf {} \; 9 | find . -name dist -exec rm -rf {} \; 10 | 11 | -------------------------------------------------------------------------------- /coverage.cfg: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | raise NotImplementedError() 5 | -------------------------------------------------------------------------------- /doc/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% block extrahead %} 4 | {{ super() }} 5 | 6 | 7 | 20 | 21 | 22 | {% endblock %} 23 | 24 | -------------------------------------------------------------------------------- /doc/bar.rst: -------------------------------------------------------------------------------- 1 | bar -- Instrument prices 2 | ======================== 3 | 4 | .. automodule:: pyalgotrade.bar 5 | :members: 6 | :member-order: bysource 7 | :special-members: 8 | :exclude-members: __weakref__ 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /doc/barfeed.rst: -------------------------------------------------------------------------------- 1 | .. _barfeed-label: 2 | 3 | barfeed -- Bar providers 4 | ======================== 5 | 6 | .. automodule:: pyalgotrade.barfeed 7 | :members: BaseBarFeed 8 | :member-order: bysource 9 | :special-members: 10 | :exclude-members: __weakref__ 11 | :show-inheritance: 12 | 13 | CSV 14 | --- 15 | .. automodule:: pyalgotrade.barfeed.csvfeed 16 | :members: BarFeed, GenericBarFeed 17 | :show-inheritance: 18 | 19 | Yahoo! Finance 20 | -------------- 21 | .. automodule:: pyalgotrade.barfeed.yahoofeed 22 | :members: Feed 23 | :show-inheritance: 24 | 25 | Google Finance 26 | -------------- 27 | .. automodule:: pyalgotrade.barfeed.googlefeed 28 | :members: Feed 29 | :show-inheritance: 30 | 31 | Quandl 32 | ------ 33 | .. automodule:: pyalgotrade.barfeed.quandlfeed 34 | :members: Feed 35 | :show-inheritance: 36 | 37 | Ninja Trader 38 | ------------ 39 | .. automodule:: pyalgotrade.barfeed.ninjatraderfeed 40 | :members: Feed 41 | :show-inheritance: 42 | 43 | -------------------------------------------------------------------------------- /doc/bitcoin.rst: -------------------------------------------------------------------------------- 1 | Bitcoin 2 | ======= 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | bitstamp 10 | bitcoincharts 11 | -------------------------------------------------------------------------------- /doc/bitcoincharts.rst: -------------------------------------------------------------------------------- 1 | Bitcoin Charts support 2 | ====================== 3 | 4 | The bitcoincharts package adds support for integrating with historical trade data supplied by http://www.bitcoincharts.com/ 5 | for backtesting Bitcoin strategies. 6 | 7 | Historical trade data in CSV format is described in http://www.bitcoincharts.com/about/markets-api/, and files can be 8 | downloaded from http://api.bitcoincharts.com/v1/csv/. 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | bitcoincharts_ref 16 | bitcoincharts_example 17 | -------------------------------------------------------------------------------- /doc/bitcoincharts_example.rst: -------------------------------------------------------------------------------- 1 | Bitcoin Charts example 2 | ====================== 3 | 4 | Although it is absolutely possible to backtest a strategy with tick data as supplied by 5 | http://www.bitcoincharts.com/about/markets-api/ using :class:`pyalgotrade.bitcoincharts.barfeed.CSVTradeFeed`, 6 | you may want to to backtest using summarized bars at a different frequency to make backtesting faster. 7 | 8 | As of 12-Aug-2014, http://api.bitcoincharts.com/v1/csv/bitstampUSD.csv.gz has 4588830 events so we'll transform a portion of 9 | it into 30 minute bars for backtesting purposes with the following script: 10 | 11 | .. literalinclude:: ../samples/bccharts_example_1.py 12 | 13 | It will take some time to execute, so be patient. The resampled file should look like this: :: 14 | 15 | Date Time,Open,High,Low,Close,Volume,Adj Close 16 | 2014-01-01 00:00:00,732.0,738.25,729.01,734.81,266.17955488, 17 | 2014-01-01 00:30:00,734.81,739.9,734.47,739.02,308.96802502, 18 | 2014-01-01 01:00:00,739.02,739.97,737.65,738.11,65.66924473, 19 | 2014-01-01 01:30:00,738.0,742.0,737.65,741.89,710.27165024, 20 | 2014-01-01 02:00:00,741.89,757.99,741.89,752.23,1085.13335011, 21 | 2014-01-01 02:30:00,752.23,755.0,747.0,747.2,272.03949342, 22 | 2014-01-01 04:00:00,744.98,748.02,744.98,747.19,104.65989075, 23 | . 24 | . 25 | 26 | We can now take advantage of :class:`pyalgotrade.barfeed.csvfeed.GenericBarFeed` to load the resampled file and backtest a 27 | Bitcoin strategy. We'll be using a VWAP momentum strategy for illustration purposes: 28 | 29 | .. literalinclude:: ../samples/bccharts_example_2.py 30 | 31 | This is what the plot looks like: 32 | 33 | .. image:: ../samples/bccharts_example_2.png 34 | -------------------------------------------------------------------------------- /doc/bitcoincharts_ref.rst: -------------------------------------------------------------------------------- 1 | bitcoincharts -- Bitcoin Charts reference 2 | ========================================= 3 | 4 | Feeds 5 | ----- 6 | 7 | .. automodule:: pyalgotrade.bitcoincharts.barfeed 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /doc/bitstamp.rst: -------------------------------------------------------------------------------- 1 | Bitstamp support 2 | ================ 3 | 4 | The bitstamp package adds support for paper trading and live trading Bitcoin strategies through Bitstamp 5 | (https://www.bitstamp.net/). 6 | 7 | Bitstamp support depends on: 8 | * **ws4py** (https://github.com/Lawouach/WebSocket-for-Python) 9 | * **tornado** (http://www.tornadoweb.org/en/stable/) 10 | 11 | so be sure to have those installed before moving forward. 12 | 13 | Contents: 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | bitstamp_ref 19 | bitstamp_example 20 | -------------------------------------------------------------------------------- /doc/bitstamp_ref.rst: -------------------------------------------------------------------------------- 1 | bitstamp -- Bitstamp reference 2 | ============================== 3 | 4 | WebSocket 5 | --------- 6 | 7 | This package has classes for the events emitted by Bitstamp's streaming service. 8 | Check https://www.bitstamp.net/websocket/ for more information. 9 | 10 | .. automodule:: pyalgotrade.bitstamp.wsclient 11 | :members: 12 | :show-inheritance: 13 | 14 | Feeds 15 | ----- 16 | 17 | .. automodule:: pyalgotrade.bitstamp.barfeed 18 | :members: LiveTradeFeed 19 | :show-inheritance: 20 | 21 | Brokers 22 | ------- 23 | 24 | .. automodule:: pyalgotrade.bitstamp.broker 25 | :members: BacktestingBroker, PaperTradingBroker, LiveBroker 26 | :show-inheritance: 27 | 28 | -------------------------------------------------------------------------------- /doc/broker.rst: -------------------------------------------------------------------------------- 1 | broker -- Order management classes 2 | ================================== 3 | 4 | Base module and classes 5 | ------------------------ 6 | 7 | .. automodule:: pyalgotrade.broker 8 | :members: Order, MarketOrder, LimitOrder, StopOrder, StopLimitOrder, OrderExecutionInfo, Broker 9 | :member-order: bysource 10 | :show-inheritance: 11 | 12 | Backtesting module and classes 13 | ------------------------------ 14 | 15 | .. automodule:: pyalgotrade.broker.backtesting 16 | :members: Commission, NoCommission, FixedPerTrade, TradePercentage, Broker 17 | :show-inheritance: 18 | 19 | .. automodule:: pyalgotrade.broker.slippage 20 | :members: SlippageModel, NoSlippage, VolumeShareSlippage 21 | :show-inheritance: 22 | 23 | .. automodule:: pyalgotrade.broker.fillstrategy 24 | :members: FillStrategy, DefaultStrategy 25 | :show-inheritance: 26 | -------------------------------------------------------------------------------- /doc/code.rst: -------------------------------------------------------------------------------- 1 | Documentation for the code 2 | ========================== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | bar 10 | dataseries 11 | feed 12 | barfeed 13 | technical 14 | broker 15 | strategy 16 | stratanalyzer 17 | plotter 18 | optimizer 19 | marketsession 20 | 21 | -------------------------------------------------------------------------------- /doc/compinvpart1.rst: -------------------------------------------------------------------------------- 1 | Computational Investing Part I 2 | ============================== 3 | 4 | As I was taking the `Computational Investing Part I `_ course in 2012 5 | I had to work on a set of assignments and for some of them I used PyAlgoTrade. 6 | 7 | Homework 1 8 | ---------- 9 | 10 | For this assignment I had to pick 4 stocks, invest a total of $100000 during 2011, and calculate: 11 | 12 | * Final portfolio value 13 | * Anual return 14 | * Average daily return 15 | * Std. dev. of daily returns 16 | * Sharpe ratio 17 | 18 | Download the data with the following commands: :: 19 | 20 | python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('aeti', 2011, 'aeti-2011-yahoofinance.csv')" 21 | python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('egan', 2011, 'egan-2011-yahoofinance.csv')" 22 | python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('glng', 2011, 'glng-2011-yahoofinance.csv')" 23 | python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('simo', 2011, 'simo-2011-yahoofinance.csv')" 24 | 25 | Although the deliverable was an Excel spreadsheet, I validated the results using this piece of code: 26 | 27 | .. literalinclude:: ../samples/compinv-1.py 28 | 29 | The results were: 30 | 31 | .. literalinclude:: ../samples/compinv-1.output 32 | 33 | Homework 3 and 4 34 | ---------------- 35 | 36 | For these assignments I had to build a market simulation tool that loads orders from a file, executes those, 37 | and prints the results for each day. 38 | 39 | The orders file for homework 3 look like this: :: 40 | 41 | 2011,1,10,AAPL,Buy,1500, 42 | 2011,1,13,AAPL,Sell,1500, 43 | 2011,1,13,IBM,Buy,4000, 44 | 2011,1,26,GOOG,Buy,1000, 45 | 2011,2,2,XOM,Sell,4000, 46 | 2011,2,10,XOM,Buy,4000, 47 | 2011,3,3,GOOG,Sell,1000, 48 | 2011,3,3,IBM,Sell,2200, 49 | 2011,6,3,IBM,Sell,3300, 50 | 2011,5,3,IBM,Buy,1500, 51 | 2011,6,10,AAPL,Buy,1200, 52 | 2011,8,1,GOOG,Buy,55, 53 | 2011,8,1,GOOG,Sell,55, 54 | 2011,12,20,AAPL,Sell,1200, 55 | 56 | This is the market simulation tool that I built: 57 | 58 | .. literalinclude:: ../samples/compinv-3.py 59 | 60 | The output for homework 3 looks like this: :: 61 | 62 | First date 2011-01-10 00:00:00 63 | Last date 2011-12-20 00:00:00 64 | Symbols ['AAPL', 'IBM', 'GOOG', 'XOM'] 65 | 2011-01-10 00:00:00: Portfolio value: $1000000.00 66 | 2011-01-11 00:00:00: Portfolio value: $998785.00 67 | 2011-01-12 00:00:00: Portfolio value: $1002940.00 68 | 2011-01-13 00:00:00: Portfolio value: $1004815.00 69 | . 70 | . 71 | . 72 | 2011-12-15 00:00:00: Portfolio value: $1113532.00 73 | 2011-12-16 00:00:00: Portfolio value: $1116016.00 74 | 2011-12-19 00:00:00: Portfolio value: $1117444.00 75 | 2011-12-20 00:00:00: Portfolio value: $1133860.00 76 | Final portfolio value: $1133860.00 77 | Anual return: 13.39 % 78 | Average daily return: 0.05 % 79 | Std. dev. daily return: 0.0072 80 | Sharpe ratio: 1.21 81 | 82 | -------------------------------------------------------------------------------- /doc/dataseries.rst: -------------------------------------------------------------------------------- 1 | dataseries -- Basic dataseries classes 2 | ====================================== 3 | 4 | Data series are abstractions used to manage time-series data. 5 | 6 | .. automodule:: pyalgotrade.dataseries 7 | :members: DataSeries, SequenceDataSeries 8 | :special-members: 9 | :exclude-members: __weakref__ 10 | :show-inheritance: 11 | 12 | .. automodule:: pyalgotrade.dataseries.aligned 13 | :members: datetime_aligned 14 | :special-members: 15 | :exclude-members: __weakref__ 16 | :show-inheritance: 17 | 18 | .. automodule:: pyalgotrade.dataseries.bards 19 | :members: BarDataSeries 20 | :special-members: 21 | :exclude-members: __weakref__ 22 | :show-inheritance: 23 | 24 | .. automodule:: pyalgotrade.dataseries.resampled 25 | :members: ResampledBarDataSeries 26 | :special-members: 27 | :exclude-members: __weakref__ 28 | :show-inheritance: 29 | 30 | -------------------------------------------------------------------------------- /doc/eventprofiler.rst: -------------------------------------------------------------------------------- 1 | Event profiler 2 | ============== 3 | 4 | Inspired in QSTK (http://wiki.quantsoftware.org/index.php?title=QSTK_Tutorial_9), the **eventprofiler** module is a tool to analyze, 5 | statistically, how events affect future equity prices. 6 | The event profiler scans over historical data for a specified event and then calculates the impact of that event on the equity prices in the past 7 | and the future over a certain lookback period. 8 | 9 | **The goal of this tool is to help you quickly validate an idea, before moving forward with the backtesting process.** 10 | 11 | .. automodule:: pyalgotrade.eventprofiler 12 | :members: 13 | :member-order: bysource 14 | :show-inheritance: 15 | 16 | Example 17 | ------- 18 | 19 | The following example is inspired on the 'Buy-on-Gap Model' from Ernie Chan's book: 20 | 'Algorithmic Trading: Winning Strategies and Their Rationale': 21 | 22 | * The idea is to select a stock near the market open whose returns from their previous day's lows 23 | to today's open are lower that one standard deviation. The standard deviation is computed using 24 | the daily close-to-close returns of the last 90 days. These are the stocks that "gapped down". 25 | * This is narrowed down by requiring the open price to be higher than the 20-day moving average 26 | of the closing price. 27 | 28 | .. literalinclude:: ../samples/eventstudy.py 29 | 30 | The code is doing 4 things: 31 | 32 | 1. Declaring a :class:`Predicate` that implements the 'Buy-on-Gap Model' event identification. 33 | 2. Loading bars for some stocks. 34 | 3. Running the analysis. 35 | 4. Plotting the results. 36 | 37 | This is what the output should look like: 38 | 39 | .. image:: ../samples/eventstudy.png 40 | 41 | .. literalinclude:: ../samples/eventstudy.output 42 | 43 | Note that **Cummulative returns are normalized to the time of the event**. 44 | -------------------------------------------------------------------------------- /doc/feed.rst: -------------------------------------------------------------------------------- 1 | feed -- Basic feeds 2 | =================== 3 | 4 | Feeds are time series data providing abstractions. 5 | When these are included in the event dispatch loop, they emit an event as new data is available. 6 | Feeds are also responsible for updating the :class:`pyalgotrade.dataseries.DataSeries` associated 7 | with each piece of data that the feed provides. 8 | 9 | **This package has basic feeds. For bar feeds refer to the** :ref:`barfeed-label` **section.** 10 | 11 | .. automodule:: pyalgotrade.feed 12 | :members: BaseFeed 13 | :special-members: 14 | :exclude-members: __weakref__ 15 | :show-inheritance: 16 | 17 | CSV support 18 | ----------- 19 | 20 | .. automodule:: pyalgotrade.feed.csvfeed 21 | :members: Feed 22 | :special-members: 23 | :exclude-members: __weakref__ 24 | :show-inheritance: 25 | 26 | CSV support Example 27 | ------------------- 28 | A file with the following format :: 29 | 30 | Date,USD,GBP,EUR 31 | 2013-09-29,1333.0,831.203,986.75 32 | 2013-09-22,1349.25,842.755,997.671 33 | 2013-09-15,1318.5,831.546,993.969 34 | 2013-09-08,1387.0,886.885,1052.911 35 | . 36 | . 37 | . 38 | 39 | can be loaded like this: 40 | 41 | .. literalinclude:: ../samples/csvfeed_1.py 42 | 43 | and the output should look like this: 44 | 45 | .. literalinclude:: ../samples/csvfeed_1.output 46 | 47 | -------------------------------------------------------------------------------- /doc/images/execution_running_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/doc/images/execution_running_1.png -------------------------------------------------------------------------------- /doc/images/queue_execution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/doc/images/queue_execution.png -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. PyAlgoTrade documentation master file, created by 2 | sphinx-quickstart on Fri Nov 4 21:48:31 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | PyAlgoTrade documentation 7 | ========================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 3 13 | 14 | intro 15 | tutorial 16 | code 17 | tools 18 | eventprofiler 19 | bitcoin 20 | twitter 21 | talib 22 | compinvpart1 23 | samples 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | 32 | -------------------------------------------------------------------------------- /doc/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | PyAlgoTrade is an event driven algorithmic trading Python library with support for: 5 | * Backtesting with historical data from CSV files. 6 | * Paper trading using :ref:`Bitstamp ` live feeds. 7 | * Real trading on Bitstamp. 8 | 9 | It should also make it easy to optimize a strategy using multiple computers. 10 | 11 | PyAlgoTrade is developed using Python 2.7 and depends on: 12 | * NumPy and SciPy (http://numpy.scipy.org/). 13 | * pytz (http://pytz.sourceforge.net/). 14 | * matplotlib (http://matplotlib.sourceforge.net/) for plotting support. 15 | * ws4py (https://github.com/Lawouach/WebSocket-for-Python) for Bitstamp support. 16 | * tornado (http://www.tornadoweb.org/en/stable/) for Bitstamp support. 17 | * tweepy (https://github.com/tweepy/tweepy) for Twitter support. 18 | 19 | so you need to have those installed in order to use this library. 20 | 21 | You can install PyAlgoTrade using pip like this: :: 22 | 23 | pip install pyalgotrade 24 | 25 | -------------------------------------------------------------------------------- /doc/marketsession.rst: -------------------------------------------------------------------------------- 1 | marketsession -- Market sessions 2 | ================================ 3 | 4 | .. automodule:: pyalgotrade.marketsession 5 | :members: 6 | :member-order: bysource 7 | :show-inheritance: 8 | 9 | -------------------------------------------------------------------------------- /doc/optimizer.rst: -------------------------------------------------------------------------------- 1 | optimizer -- Parallel optimizers 2 | ================================ 3 | 4 | .. automodule:: pyalgotrade.optimizer.server 5 | :members: 6 | :member-order: bysource 7 | :show-inheritance: 8 | 9 | .. automodule:: pyalgotrade.optimizer.worker 10 | :members: 11 | :member-order: bysource 12 | :show-inheritance: 13 | 14 | .. automodule:: pyalgotrade.optimizer.local 15 | :members: 16 | :member-order: bysource 17 | :show-inheritance: 18 | 19 | .. note:: 20 | * The server component will split strategy executions in chunks which are distributed among the different workers. **pyalgotrade.optimizer.server.Server.defaultBatchSize** controls the chunk size. 21 | * The :meth:`pyalgotrade.strategy.BaseStrategy.getResult` method is used to select the best strategy execution. You can override that method to rank executions using a different criteria. 22 | 23 | -------------------------------------------------------------------------------- /doc/plotter.rst: -------------------------------------------------------------------------------- 1 | plotter -- Strategy plotter 2 | =========================== 3 | 4 | .. automodule:: pyalgotrade.plotter 5 | :members: Subplot, InstrumentSubplot, StrategyPlotter 6 | :show-inheritance: 7 | 8 | -------------------------------------------------------------------------------- /doc/sample_bbands.rst: -------------------------------------------------------------------------------- 1 | Bollinger Bands 2 | =============== 3 | 4 | This example is based on: 5 | * http://www.investopedia.com/articles/trading/07/bollinger.asp 6 | 7 | .. literalinclude:: ../samples/bbands.py 8 | 9 | this is what the output should look like: 10 | 11 | .. literalinclude:: ../samples/bbands.output 12 | 13 | and this is what the plot should look like: 14 | 15 | .. image:: ../samples/bbands.png 16 | 17 | You can get better returns by tunning the Bollinger Bands period as well as the entry and exit points. 18 | -------------------------------------------------------------------------------- /doc/sample_market_timing.rst: -------------------------------------------------------------------------------- 1 | Market Timing Using Moving-Average Crossovers 2 | ============================================= 3 | 4 | 5 | This example is inspired on the Market Timing / GTAA model described in: 6 | * http://mebfaber.com/timing-model/ 7 | * http://papers.ssrn.com/sol3/papers.cfm?abstract_id=962461 8 | 9 | The stragegy supports analyzing more than one instrument per asset class, and selects the one that has highest 10 | returns in the last month. 11 | 12 | .. literalinclude:: ../samples/market_timing.py 13 | 14 | This is what the output should look like: 15 | 16 | .. literalinclude:: ../samples/market_timing.output 17 | 18 | This is what the plot should look like: 19 | 20 | .. image:: ../samples/market_timing.png 21 | -------------------------------------------------------------------------------- /doc/sample_quandl.rst: -------------------------------------------------------------------------------- 1 | Quandl integration 2 | ================== 3 | 4 | The purpose of this example is to show how to integrate price data along with any 5 | time-series data in CSV format from Quandl into a strategy. 6 | 7 | We'll use the following CSV data from Quandl: 8 | http://www.quandl.com/OFDP-Open-Financial-Data-Project/GOLD_2-LBMA-Gold-Price-London-Fixings-P-M 9 | 10 | .. literalinclude:: ../samples/quandl_sample.py 11 | 12 | This is what the output should look like: 13 | 14 | .. literalinclude:: ../samples/quandl_sample.output 15 | 16 | and this is what the plot should look like: 17 | 18 | .. image:: ../samples/quandl_sample.png 19 | -------------------------------------------------------------------------------- /doc/sample_rsi2.rst: -------------------------------------------------------------------------------- 1 | RSI2 2 | ==== 3 | 4 | This example is based on a strategy known as RSI2 (http://stockcharts.com/school/doku.php?id=chart_school:trading_strategies:rsi2) 5 | which requires the following parameters: 6 | 7 | * An SMA period for trend identification. We’ll call this entrySMA. 8 | * A smaller SMA period for the exit point. We’ll call this exitSMA. 9 | * An RSI period for entering both short/long positions. We’ll call this rsiPeriod. 10 | * An RSI oversold threshold for long position entry. We’ll call this overSoldThreshold. 11 | * An RSI overbought threshold for short position entry. We’ll call this overBoughtThreshold. 12 | 13 | Save this code as rsi2.py: 14 | 15 | .. literalinclude:: ../samples/rsi2.py 16 | 17 | and use the following code to execute the strategy: 18 | 19 | .. literalinclude:: ../samples/rsi2_sample.py 20 | 21 | This is what the output should look like: 22 | 23 | .. literalinclude:: ../samples/rsi2_sample.output 24 | 25 | and this is what the plot should look like: 26 | 27 | .. image:: ../samples/rsi2_sample.png 28 | 29 | You can get better returns by tunning the different parameters. 30 | -------------------------------------------------------------------------------- /doc/sample_sma_crossover.rst: -------------------------------------------------------------------------------- 1 | SMA Crossover 2 | ============= 3 | 4 | Save this code as sma_crossover.py: 5 | 6 | .. literalinclude:: ../samples/sma_crossover.py 7 | 8 | and use the following code to execute the strategy: 9 | 10 | .. literalinclude:: ../samples/sma_crossover_sample.py 11 | 12 | This is what the output should look like: 13 | 14 | .. literalinclude:: ../samples/sma_crossover.output 15 | 16 | and this is what the plot should look like: 17 | 18 | .. image:: ../samples/sma_crossover.png 19 | 20 | You can get better returns by tunning the sma period. 21 | -------------------------------------------------------------------------------- /doc/sample_statarb_erniechan.rst: -------------------------------------------------------------------------------- 1 | Ernie Chan's Gold vs. Gold Miners 2 | ================================= 3 | 4 | This example is based on: 5 | * http://epchan.blogspot.com.ar/2006/11/gold-vs-gold-miners-another-arbitrage.html 6 | * https://www.quantopian.com/posts/ernie-chans-gold-vs-gold-miners-stat-arb 7 | 8 | .. literalinclude:: ../samples/statarb_erniechan.py 9 | 10 | this is what the output should look like: 11 | 12 | .. literalinclude:: ../samples/statarb_erniechan.output 13 | 14 | and this is what the plot should look like: 15 | 16 | .. image:: ../samples/statarb_erniechan.png 17 | 18 | You can get better returns by tunning the window size as well as the entry and exit values for the z-score. 19 | 20 | -------------------------------------------------------------------------------- /doc/sample_vwap_momentum.rst: -------------------------------------------------------------------------------- 1 | VWAP Momentum Trade 2 | =================== 3 | 4 | This example is based on: 5 | * https://www.quantopian.com/posts/momentum-trade 6 | 7 | .. literalinclude:: ../samples/vwap_momentum.py 8 | 9 | this is what the output should look like: 10 | 11 | .. literalinclude:: ../samples/vwap_momentum.output 12 | 13 | and this is what the plot should look like: 14 | 15 | .. image:: ../samples/vwap_momentum.png 16 | 17 | You can get better returns by tunning the VWAP and threshold parameters. 18 | 19 | -------------------------------------------------------------------------------- /doc/samples.rst: -------------------------------------------------------------------------------- 1 | .. _samples-label: 2 | 3 | Sample strategies 4 | ================= 5 | 6 | Momentum 7 | -------- 8 | .. toctree:: 9 | sample_vwap_momentum 10 | sample_sma_crossover 11 | sample_market_timing 12 | 13 | Mean Reversion 14 | -------------- 15 | .. toctree:: 16 | sample_statarb_erniechan 17 | sample_bbands 18 | sample_rsi2 19 | 20 | Others 21 | ------ 22 | .. toctree:: 23 | sample_quandl 24 | -------------------------------------------------------------------------------- /doc/stratanalyzer.rst: -------------------------------------------------------------------------------- 1 | stratanalyzer -- Strategy analyzers 2 | =================================== 3 | 4 | Strategy analyzers provide an extensible way to attach different calculations to strategy executions. 5 | 6 | .. automodule:: pyalgotrade.stratanalyzer 7 | :members: StrategyAnalyzer 8 | :show-inheritance: 9 | 10 | Returns 11 | ------- 12 | .. automodule:: pyalgotrade.stratanalyzer.returns 13 | :members: Returns 14 | :show-inheritance: 15 | 16 | Sharpe Ratio 17 | ------------ 18 | .. automodule:: pyalgotrade.stratanalyzer.sharpe 19 | :members: SharpeRatio 20 | :show-inheritance: 21 | 22 | DrawDown 23 | -------- 24 | .. automodule:: pyalgotrade.stratanalyzer.drawdown 25 | :members: DrawDown 26 | :show-inheritance: 27 | 28 | Trades 29 | ------ 30 | .. automodule:: pyalgotrade.stratanalyzer.trades 31 | :members: Trades 32 | :member-order: bysource 33 | :show-inheritance: 34 | 35 | Example 36 | ------- 37 | Save this code as sma_crossover.py: 38 | 39 | .. literalinclude:: ../samples/sma_crossover.py 40 | 41 | and save this code in a different file: 42 | 43 | .. literalinclude:: ../samples/sample-strategy-analyzer.py 44 | 45 | The output should look like this: 46 | 47 | .. literalinclude:: ../samples/sample-strategy-analyzer.output 48 | -------------------------------------------------------------------------------- /doc/strategy.rst: -------------------------------------------------------------------------------- 1 | strategy -- Basic strategy classes 2 | ================================== 3 | 4 | Strategies are the classes that you define that implement the trading logic, when to buy, when to sell, etc. 5 | 6 | Buying and selling can be done in two ways: 7 | 8 | * Placing individual orders using any of the following methods: 9 | 10 | * :meth:`pyalgotrade.strategy.BaseStrategy.marketOrder` 11 | * :meth:`pyalgotrade.strategy.BaseStrategy.limitOrder` 12 | * :meth:`pyalgotrade.strategy.BaseStrategy.stopOrder` 13 | * :meth:`pyalgotrade.strategy.BaseStrategy.stopLimitOrder` 14 | 15 | * Using a higher level interface that wrap a pair of entry/exit orders: 16 | 17 | * :meth:`pyalgotrade.strategy.BaseStrategy.enterLong` 18 | * :meth:`pyalgotrade.strategy.BaseStrategy.enterShort` 19 | * :meth:`pyalgotrade.strategy.BaseStrategy.enterLongLimit` 20 | * :meth:`pyalgotrade.strategy.BaseStrategy.enterShortLimit` 21 | 22 | Positions are higher level abstractions for placing orders. They are escentially a pair of entry-exit orders and provide 23 | easier tracking for returns and PnL than using individual orders. 24 | 25 | 26 | Strategy 27 | -------- 28 | 29 | .. automodule:: pyalgotrade.strategy 30 | :members: BaseStrategy, BacktestingStrategy 31 | :show-inheritance: 32 | :member-order: bysource 33 | 34 | Position 35 | -------- 36 | 37 | .. automodule:: pyalgotrade.strategy.position 38 | :members: Position 39 | :show-inheritance: 40 | :member-order: bysource 41 | -------------------------------------------------------------------------------- /doc/talib.rst: -------------------------------------------------------------------------------- 1 | TA-Lib integration 2 | ================== 3 | 4 | The **pyalgotrade.talibext.indicator** module provides integration with Python wrapper for TA-Lib (http://mrjbq7.github.com/ta-lib/) 5 | to enable calling TA-Lib functions directly with :class:`pyalgotrade.dataseries.DataSeries` or :class:`pyalgotrade.dataseries.bards.BarDataSeries` 6 | instances instead of numpy arrays. 7 | 8 | If you're familiar with the **talib** module, then using the **pyalgotrade.talibext.indicator** module should be straightforward. 9 | When using **talib** standalone you do something like this: :: 10 | 11 | import numpy 12 | import talib 13 | 14 | data = numpy.random.random(100) 15 | upper, middle, lower = talib.BBANDS(data, matype=talib.MA_T3) 16 | 17 | To use the **pyalgotrade.talibext.indicator** module in your strategies you should do something like this: :: 18 | 19 | def onBars(self, bars): 20 | closeDs = self.getFeed().getDataSeries("orcl").getCloseDataSeries() 21 | upper, middle, lower = pyalgotrade.talibext.indicator.BBANDS(closeDs, 100, matype=talib.MA_T3) 22 | if upper != None: 23 | print "%s" % upper[-1] 24 | 25 | Every function in the **pyalgotrade.talibext.indicator** module receives one or more dataseries (most receive just one) and the 26 | number of values to use from the dataseries. In the example above, we're calculating Bollinger Bands over the last 100 closing prices. 27 | 28 | If the parameter name is **ds**, then you should pass a regular :class:`pyalgotrade.dataseries.DataSeries` instance, like the one 29 | shown in the example above. 30 | 31 | If the parameter name is **barDs**, then you should pass a :class:`pyalgotrade.dataseries.bards.BarDataSeries` instance, like in the next 32 | example: :: 33 | 34 | def onBars(self, bars): 35 | barDs = self.getFeed().getDataSeries("orcl") 36 | sar = indicator.SAR(barDs, 100) 37 | if sar != None: 38 | print "%s" % sar[-1] 39 | 40 | The following TA-Lib functions are available through the **pyalgotrade.talibext.indicator** module: 41 | 42 | .. automodule:: pyalgotrade.talibext.indicator 43 | :members: 44 | :member-order: bysource 45 | :show-inheritance: 46 | 47 | -------------------------------------------------------------------------------- /doc/technical.rst: -------------------------------------------------------------------------------- 1 | technical -- Technical indicators 2 | ================================= 3 | 4 | .. automodule:: pyalgotrade.technical 5 | :members: EventWindow, EventBasedFilter 6 | :show-inheritance: 7 | 8 | Example 9 | ------- 10 | 11 | The following example shows how to combine an :class:`EventWindow` and an :class:`EventBasedFilter` to build a custom filter: 12 | 13 | .. literalinclude:: ../samples/technical-1.py 14 | 15 | The output should be: 16 | 17 | .. literalinclude:: ../samples/technical-1.output 18 | 19 | Moving Averages 20 | --------------- 21 | 22 | .. automodule:: pyalgotrade.technical.ma 23 | :members: SMA, EMA, WMA 24 | :show-inheritance: 25 | 26 | .. automodule:: pyalgotrade.technical.vwap 27 | :members: VWAP 28 | :show-inheritance: 29 | 30 | Momentum Indicators 31 | ------------------- 32 | 33 | .. automodule:: pyalgotrade.technical.macd 34 | :members: MACD 35 | :show-inheritance: 36 | 37 | .. automodule:: pyalgotrade.technical.rsi 38 | :members: RSI 39 | :show-inheritance: 40 | 41 | .. automodule:: pyalgotrade.technical.stoch 42 | :members: StochasticOscillator 43 | :show-inheritance: 44 | 45 | .. automodule:: pyalgotrade.technical.roc 46 | :members: RateOfChange 47 | :show-inheritance: 48 | 49 | Other Indicators 50 | ---------------- 51 | 52 | .. automodule:: pyalgotrade.technical.atr 53 | :members: ATR 54 | :show-inheritance: 55 | 56 | .. automodule:: pyalgotrade.technical.bollinger 57 | :members: BollingerBands 58 | :show-inheritance: 59 | 60 | .. automodule:: pyalgotrade.technical.cross 61 | :members: cross_above, cross_below 62 | :show-inheritance: 63 | 64 | .. automodule:: pyalgotrade.technical.cumret 65 | :members: CumulativeReturn 66 | :show-inheritance: 67 | 68 | .. automodule:: pyalgotrade.technical.highlow 69 | :members: High, Low 70 | :show-inheritance: 71 | 72 | .. automodule:: pyalgotrade.technical.hurst 73 | :members: HurstExponent 74 | :show-inheritance: 75 | 76 | .. automodule:: pyalgotrade.technical.linebreak 77 | :members: Line, LineBreak 78 | :show-inheritance: 79 | 80 | .. automodule:: pyalgotrade.technical.linreg 81 | :members: LeastSquaresRegression, Slope 82 | :show-inheritance: 83 | 84 | .. automodule:: pyalgotrade.technical.stats 85 | :members: StdDev, ZScore 86 | :show-inheritance: 87 | 88 | -------------------------------------------------------------------------------- /doc/tools.rst: -------------------------------------------------------------------------------- 1 | Tools 2 | ===== 3 | 4 | Yahoo! Finance 5 | -------------- 6 | 7 | .. automodule:: pyalgotrade.tools.yahoofinance 8 | :members: 9 | :show-inheritance: 10 | 11 | Quandl 12 | ------ 13 | 14 | .. automodule:: pyalgotrade.tools.quandl 15 | :members: 16 | :show-inheritance: 17 | 18 | 19 | BarFeed resampling 20 | ------------------ 21 | 22 | .. automodule:: pyalgotrade.tools.resample 23 | :members: 24 | :show-inheritance: 25 | 26 | -------------------------------------------------------------------------------- /doc/twitter.rst: -------------------------------------------------------------------------------- 1 | Twitter support 2 | =============== 3 | 4 | The twitter package adds support for receiving Twitter events in your strategy and incorporate 5 | those in your trading decisions. Note that since this is a realtime feed, it only makes sense 6 | in paper trading or real trading scenarios, but not in backtesting. 7 | 8 | Twitter support depends on **tweepy** (https://github.com/tweepy/tweepy) so be sure 9 | to have it installed before moving forward. 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | twitter_ref 17 | twitter_example 18 | -------------------------------------------------------------------------------- /doc/twitter_example.rst: -------------------------------------------------------------------------------- 1 | Twitter Example 2 | =============== 3 | 4 | This goal of this simple example is to show you how to put all the pieces together to incorporate 5 | Twitter events in a strategy. 6 | We will be using Bitstamp's live feed since backtesting with Twitter is not supported so please 7 | take a look at the :ref:`bitstamp-tutorial-label` section before moving forward. 8 | 9 | In order to connect to Twitter's API you'll need: 10 | * Consumer key 11 | * Consumer secret 12 | * Access token 13 | * Access token secret 14 | 15 | Go to http://dev.twitter.com and create an app. The consumer key and secret will be generated for you after that. 16 | Then you'll need to create an access token under the "Your access token" section. 17 | 18 | The key things to highlight are: 19 | 20 | 1. We're using :class:`pyalgotrade.strategy.BaseStrategy` instead of :class:`pyalgotrade.strategy.BacktestingStrategy` 21 | as the base class. This is not a backtest. 22 | 2. The :class:`pyalgotrade.twitter.feed.TwitterFeed` instance has to be included in the strategy event dispatch loop 23 | before running the strategy. 24 | 25 | .. literalinclude:: ../samples/tutorial_twitter_bitstamp.py 26 | 27 | The output should look like this: 28 | 29 | .. literalinclude:: ../samples/tutorial_twitter_bitstamp.output 30 | -------------------------------------------------------------------------------- /doc/twitter_ref.rst: -------------------------------------------------------------------------------- 1 | twitter -- Twitter feed reference 2 | ================================= 3 | 4 | Feed 5 | ---- 6 | 7 | .. automodule:: pyalgotrade.twitter.feed 8 | :members: TwitterFeed 9 | :show-inheritance: 10 | 11 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | MAINTAINER Gabriel Martin Becedillas Ruiz 3 | 4 | RUN apt-get update 5 | 6 | RUN apt-get install -y build-essential 7 | RUN apt-get install -y python-setuptools python-dev 8 | RUN apt-get install -y python-pip 9 | 10 | RUN apt-get install -y gfortran libopenblas-dev liblapack-dev 11 | RUN apt-get install -y pkg-config 12 | RUN apt-get install -y wget 13 | 14 | RUN pip install numpy 15 | RUN pip install scipy 16 | RUN pip install pandas 17 | RUN pip install patsy 18 | RUN pip install statsmodels 19 | RUN apt-get install -y libfreetype6-dev; \ 20 | pip install matplotlib 21 | RUN pip install ws4py 22 | RUN pip install tornado 23 | RUN pip install tweepy 24 | RUN pip install cython 25 | 26 | # TA-Lib 27 | RUN cd /tmp; \ 28 | wget http://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz; \ 29 | tar -xzf ta-lib-0.4.0-src.tar.gz; \ 30 | cd ta-lib; \ 31 | ./configure ; make; make install; \ 32 | cd ..; \ 33 | rm ta-lib-0.4.0-src.tar.gz; \ 34 | rm -rf ta-lib 35 | RUN pip install TA-Lib 36 | 37 | RUN pip install pyalgotrade==0.18 38 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker build -t pyalgotrade:0.18 . 4 | # docker rmi $(docker images --quiet --filter "dangling=true") 5 | 6 | # docker tag -f pyalgotrade:0.18 gbecedillas/pyalgotrade:0.18 7 | # docker login --username=gbecedillas --email=gbecedillas@gmail.com 8 | # docker push gbecedillas/pyalgotrade:0.18 9 | 10 | -------------------------------------------------------------------------------- /pyalgotrade/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | __version__ = "0.18" 22 | -------------------------------------------------------------------------------- /pyalgotrade/barfeed/common.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | 22 | def sanitize_ohlc(open_, high, low, close): 23 | if low > open_: 24 | low = open_ 25 | if low > close: 26 | low = close 27 | if high < open_: 28 | high = open_ 29 | if high < close: 30 | high = close 31 | return open_, high, low, close 32 | -------------------------------------------------------------------------------- /pyalgotrade/barfeed/dbfeed.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | 22 | class Database(object): 23 | def addBars(self, bars, frequency): 24 | for instrument in bars.getInstruments(): 25 | bar = bars.getBar(instrument) 26 | self.addBar(instrument, bar, frequency) 27 | 28 | def addBarsFromFeed(self, feed): 29 | for dateTime, bars in feed: 30 | if bars: 31 | self.addBars(bars, feed.getFrequency()) 32 | 33 | def addBar(self, instrument, bar, frequency): 34 | raise NotImplementedError() 35 | 36 | def getBars(self, instrument, frequency, timezone=None, fromDateTime=None, toDateTime=None): 37 | raise NotImplementedError() 38 | -------------------------------------------------------------------------------- /pyalgotrade/barfeed/quandlfeed.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade.barfeed import csvfeed 22 | from pyalgotrade import bar 23 | 24 | 25 | class Feed(csvfeed.GenericBarFeed): 26 | """A :class:`pyalgotrade.barfeed.csvfeed.BarFeed` that loads bars from CSV files downloaded from Quandl. 27 | 28 | :param frequency: The frequency of the bars. Only **pyalgotrade.bar.Frequency.DAY** or **pyalgotrade.bar.Frequency.WEEK** 29 | are supported. 30 | :param timezone: The default timezone to use to localize bars. Check :mod:`pyalgotrade.marketsession`. 31 | :type timezone: A pytz timezone. 32 | :param maxLen: The maximum number of values that the :class:`pyalgotrade.dataseries.bards.BarDataSeries` will hold. 33 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 34 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 35 | :type maxLen: int. 36 | 37 | .. note:: 38 | When working with multiple instruments: 39 | 40 | * If all the instruments loaded are in the same timezone, then the timezone parameter may not be specified. 41 | * If any of the instruments loaded are in different timezones, then the timezone parameter must be set. 42 | """ 43 | 44 | def __init__(self, frequency=bar.Frequency.DAY, timezone=None, maxLen=None): 45 | if frequency not in [bar.Frequency.DAY, bar.Frequency.WEEK]: 46 | raise Exception("Invalid frequency.") 47 | 48 | super(Feed, self).__init__(frequency, timezone, maxLen) 49 | 50 | self.setDateTimeFormat("%Y-%m-%d") 51 | self.setColumnName("datetime", "Date") 52 | self.setColumnName("adj_close", "Adj. Close") 53 | -------------------------------------------------------------------------------- /pyalgotrade/bitcoincharts/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/bitstamp/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/bitstamp/barfeed.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | 22 | from pyalgotrade.bitstamp import livefeed 23 | 24 | 25 | LiveTradeFeed = livefeed.LiveTradeFeed 26 | -------------------------------------------------------------------------------- /pyalgotrade/bitstamp/common.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import pyalgotrade.logger 22 | from pyalgotrade import broker 23 | 24 | 25 | logger = pyalgotrade.logger.getLogger("bitstamp") 26 | btc_symbol = "BTC" 27 | 28 | 29 | class BTCTraits(broker.InstrumentTraits): 30 | def roundQuantity(self, quantity): 31 | return round(quantity, 8) 32 | -------------------------------------------------------------------------------- /pyalgotrade/broker/slippage.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import abc 22 | 23 | 24 | class SlippageModel(object, metaclass=abc.ABCMeta): 25 | """Base class for slippage models. 26 | 27 | .. note:: 28 | This is a base class and should not be used directly. 29 | """ 30 | 31 | @abc.abstractmethod 32 | def calculatePrice(self, order, price, quantity, bar, volumeUsed): 33 | """ 34 | Returns the slipped price per share for an order. 35 | 36 | :param order: The order being filled. 37 | :type order: :class:`pyalgotrade.broker.Order`. 38 | :param price: The price for each share before slippage. 39 | :type price: float. 40 | :param quantity: The amount of shares that will get filled at this time for this order. 41 | :type quantity: float. 42 | :param bar: The current bar. 43 | :type bar: :class:`pyalgotrade.bar.Bar`. 44 | :param volumeUsed: The volume size that was taken so far from the current bar. 45 | :type volumeUsed: float. 46 | :rtype: float. 47 | """ 48 | raise NotImplementedError() 49 | 50 | 51 | class NoSlippage(SlippageModel): 52 | """A no slippage model.""" 53 | 54 | def calculatePrice(self, order, price, quantity, bar, volumeUsed): 55 | return price 56 | 57 | 58 | class VolumeShareSlippage(SlippageModel): 59 | """ 60 | A volume share slippage model as defined in Zipline's VolumeShareSlippage model. 61 | The slippage is calculated by multiplying the price impact constant by the square of the ratio of the order 62 | to the total volume. 63 | 64 | Check https://www.quantopian.com/help#ide-slippage for more details. 65 | 66 | :param priceImpact: Defines how large of an impact your order will have on the backtester's price calculation. 67 | :type priceImpact: float. 68 | """ 69 | 70 | def __init__(self, priceImpact=0.1): 71 | super(VolumeShareSlippage, self).__init__() 72 | self.__priceImpact = priceImpact 73 | 74 | def calculatePrice(self, order, price, quantity, bar, volumeUsed): 75 | assert bar.getVolume(), "Can't use 0 volume bars with VolumeShareSlippage" 76 | 77 | totalVolume = volumeUsed + quantity 78 | volumeShare = totalVolume / float(bar.getVolume()) 79 | impactPct = volumeShare ** 2 * self.__priceImpact 80 | if order.isBuy(): 81 | ret = price * (1 + impactPct) 82 | else: 83 | ret = price * (1 - impactPct) 84 | return ret 85 | -------------------------------------------------------------------------------- /pyalgotrade/dispatchprio.py: -------------------------------------------------------------------------------- 1 | FIRST = 0 2 | 3 | # We want to process order events before bar feed events. 4 | BROKER = 1000 5 | BAR_FEED = 2000 6 | 7 | LAST = None 8 | -------------------------------------------------------------------------------- /pyalgotrade/feed/memfeed.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import feed 22 | from pyalgotrade import dataseries 23 | 24 | 25 | class MemFeed(feed.BaseFeed): 26 | def __init__(self, maxLen=None): 27 | super(MemFeed, self).__init__(maxLen) 28 | 29 | self.__values = [] 30 | self.__nextIdx = 0 31 | 32 | def reset(self): 33 | self.__nextIdx = 0 34 | feed.BaseFeed.reset(self) 35 | 36 | def start(self): 37 | super(MemFeed, self).start() 38 | # Now that all the data is in place, sort it to dispatch it in order. 39 | cmpFun = lambda x, y: cmp(x[0], y[0]) 40 | self.__values.sort(cmpFun) 41 | 42 | def stop(self): 43 | pass 44 | 45 | def join(self): 46 | pass 47 | 48 | def eof(self): 49 | if self.__nextIdx < len(self.__values): 50 | return False 51 | else: 52 | return True 53 | 54 | def peekDateTime(self): 55 | ret = None 56 | if self.__nextIdx < len(self.__values): 57 | ret = self.__values[self.__nextIdx][0] 58 | return ret 59 | 60 | def createDataSeries(self, key, maxLen): 61 | return dataseries.SequenceDataSeries(maxLen) 62 | 63 | def getNextValues(self): 64 | ret = (None, None) 65 | if self.__nextIdx < len(self.__values): 66 | ret = self.__values[self.__nextIdx] 67 | self.__nextIdx += 1 68 | return ret 69 | 70 | # Add values to the feed. values should be a sequence of tuples. The tuples should have two elements: 71 | # 1: datetime.datetime. 72 | # 2: dictionary or dict-like object. 73 | def addValues(self, values): 74 | # Register a dataseries for each item. 75 | for key in list(values[0][1].keys()): 76 | self.registerDataSeries(key) 77 | 78 | self.__values.extend(values) 79 | -------------------------------------------------------------------------------- /pyalgotrade/logger.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import logging 22 | import threading 23 | 24 | initLock = threading.Lock() 25 | rootLoggerInitialized = False 26 | 27 | log_format = "%(asctime)s %(name)s [%(levelname)s] %(message)s" 28 | level = logging.INFO 29 | file_log = None # File name 30 | console_log = True 31 | 32 | 33 | def init_handler(handler): 34 | handler.setFormatter(Formatter(log_format)) 35 | 36 | 37 | def init_logger(logger): 38 | logger.setLevel(level) 39 | 40 | if file_log is not None: 41 | fileHandler = logging.FileHandler(file_log) 42 | init_handler(fileHandler) 43 | logger.addHandler(fileHandler) 44 | 45 | if console_log: 46 | consoleHandler = logging.StreamHandler() 47 | init_handler(consoleHandler) 48 | logger.addHandler(consoleHandler) 49 | 50 | 51 | def initialize(): 52 | global rootLoggerInitialized 53 | with initLock: 54 | if not rootLoggerInitialized: 55 | init_logger(logging.getLogger()) 56 | rootLoggerInitialized = True 57 | 58 | 59 | def getLogger(name=None): 60 | initialize() 61 | return logging.getLogger(name) 62 | 63 | 64 | # This formatter provides a way to hook in formatTime. 65 | class Formatter(logging.Formatter): 66 | DATETIME_HOOK = None 67 | 68 | def formatTime(self, record, datefmt=None): 69 | newDateTime = None 70 | 71 | if Formatter.DATETIME_HOOK is not None: 72 | newDateTime = Formatter.DATETIME_HOOK() 73 | 74 | if newDateTime is None: 75 | ret = super(Formatter, self).formatTime(record, datefmt) 76 | else: 77 | ret = str(newDateTime) 78 | return ret 79 | -------------------------------------------------------------------------------- /pyalgotrade/marketsession.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import pytz 22 | 23 | 24 | # http://en.wikipedia.org/wiki/List_of_market_opening_times 25 | class MarketSession(object): 26 | """Base class for market sessions. 27 | 28 | .. note:: 29 | This is a base class and should not be used directly. 30 | """ 31 | 32 | @classmethod 33 | def getTimezone(cls): 34 | """Returns the pytz timezone for the market session.""" 35 | return cls.timezone 36 | 37 | 38 | ###################################################################### 39 | # US 40 | 41 | class NASDAQ(MarketSession): 42 | """NASDAQ market session.""" 43 | timezone = pytz.timezone("US/Eastern") 44 | 45 | 46 | class NYSE(MarketSession): 47 | """New York Stock Exchange market session.""" 48 | timezone = pytz.timezone("US/Eastern") 49 | 50 | 51 | class USEquities(MarketSession): 52 | """US Equities market session.""" 53 | timezone = pytz.timezone("US/Eastern") 54 | 55 | 56 | ###################################################################### 57 | # South America 58 | 59 | class MERVAL(MarketSession): 60 | """Buenos Aires (Argentina) market session.""" 61 | timezone = pytz.timezone("America/Argentina/Buenos_Aires") 62 | 63 | 64 | class BOVESPA(MarketSession): 65 | """BOVESPA (Brazil) market session.""" 66 | timezone = pytz.timezone("America/Sao_Paulo") 67 | 68 | 69 | ###################################################################### 70 | # Europe 71 | 72 | class FTSE(MarketSession): 73 | """ London Stock Exchange market session.""" 74 | timezone = pytz.timezone("Europe/London") 75 | 76 | 77 | ###################################################################### 78 | # Asia 79 | 80 | class TSE(MarketSession): 81 | """Tokyo Stock Exchange market session.""" 82 | timezone = pytz.timezone("Asia/Tokyo") 83 | -------------------------------------------------------------------------------- /pyalgotrade/optimizer/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/optimizer/server.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import pyalgotrade.logger 22 | from pyalgotrade.optimizer import base 23 | from pyalgotrade.optimizer import xmlrpcserver 24 | 25 | logger = pyalgotrade.logger.getLogger(__name__) 26 | 27 | 28 | class Results(object): 29 | """The results of the strategy executions.""" 30 | def __init__(self, parameters, result): 31 | self.__parameters = parameters 32 | self.__result = result 33 | 34 | def getParameters(self): 35 | """Returns a sequence of parameter values.""" 36 | return self.__parameters 37 | 38 | def getResult(self): 39 | """Returns the result for a given set of parameters.""" 40 | return self.__result 41 | 42 | 43 | def serve(barFeed, strategyParameters, address, port): 44 | """Executes a server that will provide bars and strategy parameters for workers to use. 45 | 46 | :param barFeed: The bar feed that each worker will use to backtest the strategy. 47 | :type barFeed: :class:`pyalgotrade.barfeed.BarFeed`. 48 | :param strategyParameters: The set of parameters to use for backtesting. An iterable object where **each element is a tuple that holds parameter values**. 49 | :param address: The address to listen for incoming worker connections. 50 | :type address: string. 51 | :param port: The port to listen for incoming worker connections. 52 | :type port: int. 53 | :rtype: A :class:`Results` instance with the best results found or None if no results were obtained. 54 | """ 55 | 56 | paramSource = base.ParameterSource(strategyParameters) 57 | resultSinc = base.ResultSinc() 58 | s = xmlrpcserver.Server(paramSource, resultSinc, barFeed, address, port) 59 | logger.info("Starting server") 60 | s.serve() 61 | logger.info("Server finished") 62 | 63 | ret = None 64 | bestResult, bestParameters = resultSinc.getBest() 65 | if bestResult is not None: 66 | logger.info("Best final result %s with parameters %s" % (bestResult, bestParameters.args)) 67 | ret = Results(bestParameters.args, bestResult) 68 | else: 69 | logger.error("No results. All jobs failed or no jobs were processed.") 70 | return ret 71 | -------------------------------------------------------------------------------- /pyalgotrade/stratanalyzer/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | 22 | class StrategyAnalyzer(object): 23 | """Base class for strategy analyzers. 24 | 25 | .. note:: 26 | 27 | This is a base class and should not be used directly. 28 | """ 29 | 30 | def beforeAttach(self, strat): 31 | pass 32 | 33 | def attached(self, strat): 34 | pass 35 | 36 | def beforeOnBars(self, strat, bars): 37 | pass 38 | -------------------------------------------------------------------------------- /pyalgotrade/talibext/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/technical/cumret.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import technical 22 | 23 | 24 | class CumRetEventWindow(technical.EventWindow): 25 | def __init__(self): 26 | super(CumRetEventWindow, self).__init__(2) 27 | self.__prevCumRet = 0 28 | 29 | def getValue(self): 30 | ret = None 31 | if self.windowFull(): 32 | values = self.getValues() 33 | prev = values[0] 34 | actual = values[1] 35 | netReturn = (actual - prev) / float(prev) 36 | ret = (1 + self.__prevCumRet) * (1 + netReturn) - 1 37 | self.__prevCumRet = ret 38 | return ret 39 | 40 | 41 | class CumulativeReturn(technical.EventBasedFilter): 42 | """This filter calculates cumulative returns over another dataseries. 43 | 44 | :param dataSeries: The DataSeries instance being filtered. 45 | :type dataSeries: :class:`pyalgotrade.dataseries.DataSeries`. 46 | :param maxLen: The maximum number of values to hold. 47 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 48 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 49 | :type maxLen: int. 50 | """ 51 | 52 | def __init__(self, dataSeries, maxLen=None): 53 | super(CumulativeReturn, self).__init__(dataSeries, CumRetEventWindow(), maxLen) 54 | -------------------------------------------------------------------------------- /pyalgotrade/technical/highlow.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import technical 22 | 23 | 24 | class HighLowEventWindow(technical.EventWindow): 25 | def __init__(self, windowSize, useMin): 26 | super(HighLowEventWindow, self).__init__(windowSize) 27 | self.__useMin = useMin 28 | 29 | def getValue(self): 30 | ret = None 31 | if self.windowFull(): 32 | values = self.getValues() 33 | if self.__useMin: 34 | ret = values.min() 35 | else: 36 | ret = values.max() 37 | return ret 38 | 39 | 40 | class High(technical.EventBasedFilter): 41 | """This filter calculates the highest value. 42 | 43 | :param dataSeries: The DataSeries instance being filtered. 44 | :type dataSeries: :class:`pyalgotrade.dataseries.DataSeries`. 45 | :param period: The number of values to use to calculate the highest value. 46 | :type period: int. 47 | :param maxLen: The maximum number of values to hold. 48 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 49 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 50 | :type maxLen: int. 51 | """ 52 | 53 | def __init__(self, dataSeries, period, maxLen=None): 54 | super(High, self).__init__(dataSeries, HighLowEventWindow(period, False), maxLen) 55 | 56 | 57 | class Low(technical.EventBasedFilter): 58 | """This filter calculates the lowest value. 59 | 60 | :param dataSeries: The DataSeries instance being filtered. 61 | :type dataSeries: :class:`pyalgotrade.dataseries.DataSeries`. 62 | :param period: The number of values to use to calculate the lowest value. 63 | :type period: int. 64 | :param maxLen: The maximum number of values to hold. 65 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 66 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 67 | :type maxLen: int. 68 | """ 69 | 70 | def __init__(self, dataSeries, period, maxLen=None): 71 | super(Low, self).__init__(dataSeries, HighLowEventWindow(period, True), maxLen) 72 | -------------------------------------------------------------------------------- /pyalgotrade/technical/ratio.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import technical 22 | from pyalgotrade import utils 23 | 24 | 25 | class RatioEventWindow(technical.EventWindow): 26 | def __init__(self): 27 | super(RatioEventWindow, self).__init__(2) 28 | 29 | def getValue(self): 30 | ret = None 31 | if self.windowFull(): 32 | prev = self.getValues()[0] 33 | actual = self.getValues()[-1] 34 | ret = utils.get_change_percentage(actual, prev) 35 | return ret 36 | 37 | 38 | # Calculates the ratio between a value and the previous one. 39 | # The ratio can't be calculated if a previous value is 0. 40 | class Ratio(technical.EventBasedFilter): 41 | def __init__(self, dataSeries, maxLen=None): 42 | super(Ratio, self).__init__(dataSeries, RatioEventWindow(), maxLen) 43 | -------------------------------------------------------------------------------- /pyalgotrade/technical/roc.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import technical 22 | 23 | 24 | class ROCEventWindow(technical.EventWindow): 25 | def __init__(self, windowSize): 26 | super(ROCEventWindow, self).__init__(windowSize) 27 | 28 | def getValue(self): 29 | ret = None 30 | if self.windowFull(): 31 | prev = self.getValues()[0] 32 | actual = self.getValues()[-1] 33 | if actual is not None and prev is not None: 34 | diff = float(actual - prev) 35 | if diff == 0: 36 | ret = float(0) 37 | elif prev != 0: 38 | ret = diff / prev 39 | return ret 40 | 41 | 42 | class RateOfChange(technical.EventBasedFilter): 43 | """Rate of change filter as described in http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:rate_of_change_roc_and_momentum. 44 | 45 | :param dataSeries: The DataSeries instance being filtered. 46 | :type dataSeries: :class:`pyalgotrade.dataseries.DataSeries`. 47 | :param valuesAgo: The number of values back that a given value will compare to. Must be > 0. 48 | :type valuesAgo: int. 49 | :param maxLen: The maximum number of values to hold. 50 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 51 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 52 | :type maxLen: int. 53 | """ 54 | 55 | def __init__(self, dataSeries, valuesAgo, maxLen=None): 56 | assert(valuesAgo > 0) 57 | super(RateOfChange, self).__init__(dataSeries, ROCEventWindow(valuesAgo + 1), maxLen) 58 | -------------------------------------------------------------------------------- /pyalgotrade/technical/vwap.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import technical 22 | from pyalgotrade.dataseries import bards 23 | 24 | 25 | class VWAPEventWindow(technical.EventWindow): 26 | def __init__(self, windowSize, useTypicalPrice): 27 | super(VWAPEventWindow, self).__init__(windowSize, dtype=object) 28 | self.__useTypicalPrice = useTypicalPrice 29 | 30 | def getValue(self): 31 | ret = None 32 | if self.windowFull(): 33 | cumTotal = 0 34 | cumVolume = 0 35 | 36 | for bar in self.getValues(): 37 | if self.__useTypicalPrice: 38 | cumTotal += bar.getTypicalPrice() * bar.getVolume() 39 | else: 40 | cumTotal += bar.getPrice() * bar.getVolume() 41 | cumVolume += bar.getVolume() 42 | 43 | ret = cumTotal / float(cumVolume) 44 | return ret 45 | 46 | 47 | class VWAP(technical.EventBasedFilter): 48 | """Volume Weighted Average Price filter. 49 | 50 | :param dataSeries: The DataSeries instance being filtered. 51 | :type dataSeries: :class:`pyalgotrade.dataseries.bards.BarDataSeries`. 52 | :param period: The number of values to use to calculate the VWAP. 53 | :type period: int. 54 | :param useTypicalPrice: True if the typical price should be used instead of the closing price. 55 | :type useTypicalPrice: boolean. 56 | :param maxLen: The maximum number of values to hold. 57 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 58 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 59 | :type maxLen: int. 60 | """ 61 | 62 | def __init__(self, dataSeries, period, useTypicalPrice=False, maxLen=None): 63 | assert isinstance(dataSeries, bards.BarDataSeries), \ 64 | "dataSeries must be a dataseries.bards.BarDataSeries instance" 65 | 66 | super(VWAP, self).__init__(dataSeries, VWAPEventWindow(period, useTypicalPrice), maxLen) 67 | 68 | def getPeriod(self): 69 | return self.getWindowSize() 70 | -------------------------------------------------------------------------------- /pyalgotrade/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/twitter/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /pyalgotrade/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | 22 | def get_change_percentage(actual, prev): 23 | if actual is None or prev is None or prev == 0: 24 | raise Exception("Invalid values") 25 | 26 | diff = actual-prev 27 | ret = diff / float(abs(prev)) 28 | return ret 29 | 30 | 31 | def safe_min(left, right): 32 | if left is None: 33 | return right 34 | elif right is None: 35 | return left 36 | else: 37 | return min(left, right) 38 | 39 | 40 | def safe_max(left, right): 41 | if left is None: 42 | return right 43 | elif right is None: 44 | return left 45 | else: 46 | return max(left, right) 47 | -------------------------------------------------------------------------------- /pyalgotrade/utils/csvutils.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import csv 22 | import requests 23 | 24 | import logging 25 | logging.getLogger("requests").setLevel(logging.ERROR) 26 | 27 | 28 | # A faster (but limited) version of csv.DictReader 29 | class FastDictReader(object): 30 | def __init__(self, f, fieldnames=None, dialect="excel", *args, **kwargs): 31 | self.__fieldNames = fieldnames 32 | self.reader = csv.reader(f, dialect, *args, **kwargs) 33 | if self.__fieldNames is None: 34 | self.__fieldNames = next(self.reader) 35 | self.__dict = {} 36 | 37 | def __iter__(self): 38 | return self 39 | 40 | def __next__(self): 41 | # Skip empty rows. 42 | row = next(self.reader) 43 | while row == []: 44 | row = next(self.reader) 45 | 46 | # Check that the row has the right number of columns. 47 | assert(len(self.__fieldNames) == len(row)) 48 | 49 | # Copy the row values into the dict. 50 | for i in range(len(self.__fieldNames)): 51 | self.__dict[self.__fieldNames[i]] = row[i] 52 | 53 | return self.__dict 54 | 55 | 56 | def download_csv(url, url_params=None, content_type="text/csv"): 57 | response = requests.get(url, params=url_params) 58 | 59 | response.raise_for_status() 60 | response_content_type = response.headers['content-type'] 61 | if response_content_type != content_type: 62 | raise Exception("Invalid content-type: %s" % response_content_type) 63 | 64 | ret = response.text 65 | 66 | # Remove the BOM 67 | while not ret[0].isalnum(): 68 | ret = ret[1:] 69 | 70 | return ret 71 | 72 | 73 | def float_or_string(value): 74 | try: 75 | ret = float(value) 76 | except Exception: 77 | ret = value 78 | return ret 79 | -------------------------------------------------------------------------------- /pyalgotrade/utils/dt.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | import pytz 23 | 24 | 25 | def datetime_is_naive(dateTime): 26 | """ Returns True if dateTime is naive.""" 27 | return dateTime.tzinfo is None or dateTime.tzinfo.utcoffset(dateTime) is None 28 | 29 | 30 | # Remove timezone information. 31 | def unlocalize(dateTime): 32 | return dateTime.replace(tzinfo=None) 33 | 34 | 35 | def localize(dateTime, timeZone): 36 | """Returns a datetime adjusted to a timezone: 37 | 38 | * If dateTime is a naive datetime (datetime with no timezone information), timezone information is added but date 39 | and time remains the same. 40 | * If dateTime is not a naive datetime, a datetime object with new tzinfo attribute is returned, adjusting the date 41 | and time data so the result is the same UTC time. 42 | """ 43 | 44 | if datetime_is_naive(dateTime): 45 | ret = timeZone.localize(dateTime) 46 | else: 47 | ret = dateTime.astimezone(timeZone) 48 | return ret 49 | 50 | 51 | def as_utc(dateTime): 52 | return localize(dateTime, pytz.utc) 53 | 54 | 55 | def datetime_to_timestamp(dateTime): 56 | """ Converts a datetime.datetime to a UTC timestamp.""" 57 | diff = as_utc(dateTime) - epoch_utc 58 | return diff.total_seconds() 59 | 60 | 61 | def timestamp_to_datetime(timeStamp, localized=True): 62 | """ Converts a UTC timestamp to a datetime.datetime.""" 63 | ret = datetime.datetime.utcfromtimestamp(timeStamp) 64 | if localized: 65 | ret = localize(ret, pytz.utc) 66 | return ret 67 | 68 | 69 | def get_first_monday(year): 70 | ret = datetime.date(year, 1, 1) 71 | if ret.weekday() != 0: 72 | diff = 7 - ret.weekday() 73 | ret = ret + datetime.timedelta(days=diff) 74 | return ret 75 | 76 | 77 | def get_last_monday(year): 78 | ret = datetime.date(year, 12, 31) 79 | if ret.weekday() != 0: 80 | diff = ret.weekday() * -1 81 | ret = ret + datetime.timedelta(days=diff) 82 | return ret 83 | 84 | 85 | epoch_utc = as_utc(datetime.datetime(1970, 1, 1)) 86 | -------------------------------------------------------------------------------- /pyalgotrade/utils/stats.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import numpy 22 | 23 | 24 | def mean(values): 25 | ret = None 26 | if len(values): 27 | ret = numpy.array(values).mean() 28 | return ret 29 | 30 | 31 | def stddev(values, ddof=1): 32 | ret = None 33 | if len(values): 34 | ret = numpy.array(values).std(ddof=ddof) 35 | return ret 36 | -------------------------------------------------------------------------------- /pyalgotrade/warninghelpers.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import warnings 22 | 23 | 24 | class PyAlgoTradeDeprecationWarning(DeprecationWarning): 25 | pass 26 | 27 | warnings.simplefilter("default", PyAlgoTradeDeprecationWarning) 28 | 29 | 30 | # Deprecation warnings are disabled by default in Python 2.7, so this helper function enables them back. 31 | def deprecation_warning(msg, stacklevel=0): 32 | warnings.warn(msg, category=PyAlgoTradeDeprecationWarning, stacklevel=stacklevel+1) 33 | -------------------------------------------------------------------------------- /pyalgotrade/websocket/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /release-checklist.txt: -------------------------------------------------------------------------------- 1 | Release checklist 2 | 3 | [ ] Run flake8 (flake8 --exclude=doc . | grep -v "line too long") 4 | [ ] Run testcases on Mac and Windows. 5 | [ ] Check that code coverage is >= 89 %. 6 | [ ] Update CHANGELOG. 7 | [ ] Update version in __init__.py. 8 | [ ] Update version in setup.py. 9 | [ ] Update version in docker/build.sh. 10 | [ ] Update version in docker/Dockerfile 11 | [ ] Update version in travis/Dockerfile 12 | [ ] Update version in .travis.yml. 13 | [ ] Update directories in setup.py. 14 | [ ] Update version doc/conf.py. 15 | [ ] Rebuild and review documentation. 16 | [ ] Run testcases using the installed lib (use docker testcases image as in Travis). 17 | [ ] Run tutorial examples from the Optimizing section using the installed lib (use 'docker run -ti testcases /bin/bash'). 18 | [ ] Run samples/tutorial-5.py using the installed lib (use 'docker run -ti testcases /bin/bash'). 19 | [ ] Run samples/tutorial_bitstamp_1.py using the installed lib. Check disconnection detection (use 'docker run -ti testcases /bin/bash'). 20 | [ ] Run samples/tutorial_twitter_bitstamp.py using the installed lib. 21 | [ ] Run samples/eventstudy.py using the installed lib. 22 | 23 | [ ] Update the website (doc + package). 24 | [ ] Update README.md. 25 | [ ] Build package and upload to sourceforge. 26 | [ ] Update PyPi (both PKG-INFO and upload file using https://pypi.python.org/pypi?%3Aaction=submit_form). 27 | [ ] Install using 'sudo pip install pyalgotrade' and check that the right version was installed. 28 | [ ] Build and push Docker image. 29 | 30 | [ ] Commit and tag (git tag v0.x-rtm -m "Version 0.x"). 31 | [ ] Push (git push; git push --tags). 32 | [ ] Push the website (git push). 33 | [ ] Announce on Google Groups and Twitter. 34 | -------------------------------------------------------------------------------- /samples/bbands.output: -------------------------------------------------------------------------------- 1 | 2013-09-21 00:06:07,740 yahoofinance [INFO] Creating data directory 2 | 2013-09-21 00:06:07,741 yahoofinance [INFO] Downloading yhoo 2011 to data/yhoo-2011-yahoofinance.csv 3 | 2013-09-21 00:06:09,621 yahoofinance [INFO] Downloading yhoo 2012 to data/yhoo-2012-yahoofinance.csv 4 | Sharpe ratio: 0.71 5 | -------------------------------------------------------------------------------- /samples/bbands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/bbands.png -------------------------------------------------------------------------------- /samples/bbands.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade import plotter 3 | from pyalgotrade.tools import yahoofinance 4 | from pyalgotrade.technical import bollinger 5 | from pyalgotrade.stratanalyzer import sharpe 6 | 7 | 8 | class BBands(strategy.BacktestingStrategy): 9 | def __init__(self, feed, instrument, bBandsPeriod): 10 | super(BBands, self).__init__(feed) 11 | self.__instrument = instrument 12 | self.__bbands = bollinger.BollingerBands(feed[instrument].getCloseDataSeries(), bBandsPeriod, 2) 13 | 14 | def getBollingerBands(self): 15 | return self.__bbands 16 | 17 | def onBars(self, bars): 18 | lower = self.__bbands.getLowerBand()[-1] 19 | upper = self.__bbands.getUpperBand()[-1] 20 | if lower is None: 21 | return 22 | 23 | shares = self.getBroker().getShares(self.__instrument) 24 | bar = bars[self.__instrument] 25 | if shares == 0 and bar.getClose() < lower: 26 | sharesToBuy = int(self.getBroker().getCash(False) / bar.getClose()) 27 | self.marketOrder(self.__instrument, sharesToBuy) 28 | elif shares > 0 and bar.getClose() > upper: 29 | self.marketOrder(self.__instrument, -1*shares) 30 | 31 | 32 | def main(plot): 33 | instrument = "yhoo" 34 | bBandsPeriod = 40 35 | 36 | # Download the bars. 37 | feed = yahoofinance.build_feed([instrument], 2011, 2012, ".") 38 | 39 | strat = BBands(feed, instrument, bBandsPeriod) 40 | sharpeRatioAnalyzer = sharpe.SharpeRatio() 41 | strat.attachAnalyzer(sharpeRatioAnalyzer) 42 | 43 | if plot: 44 | plt = plotter.StrategyPlotter(strat, True, True, True) 45 | plt.getInstrumentSubplot(instrument).addDataSeries("upper", strat.getBollingerBands().getUpperBand()) 46 | plt.getInstrumentSubplot(instrument).addDataSeries("middle", strat.getBollingerBands().getMiddleBand()) 47 | plt.getInstrumentSubplot(instrument).addDataSeries("lower", strat.getBollingerBands().getLowerBand()) 48 | 49 | strat.run() 50 | print("Sharpe ratio: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0.05)) 51 | 52 | if plot: 53 | plt.plot() 54 | 55 | 56 | if __name__ == "__main__": 57 | main(True) 58 | -------------------------------------------------------------------------------- /samples/bccharts_example_1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade.bitcoincharts import barfeed 2 | from pyalgotrade.tools import resample 3 | from pyalgotrade import bar 4 | 5 | import datetime 6 | 7 | 8 | def main(): 9 | barFeed = barfeed.CSVTradeFeed() 10 | barFeed.addBarsFromCSV("bitstampUSD.csv", fromDateTime=datetime.datetime(2014, 1, 1)) 11 | resample.resample_to_csv(barFeed, bar.Frequency.MINUTE*30, "30min-bitstampUSD.csv") 12 | 13 | 14 | if __name__ == "__main__": 15 | main() 16 | -------------------------------------------------------------------------------- /samples/bccharts_example_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/bccharts_example_2.png -------------------------------------------------------------------------------- /samples/compinv-1.output: -------------------------------------------------------------------------------- 1 | Final portfolio value: $1604979.11 2 | Anual return: 60.50 % 3 | Average daily return: 0.20 % 4 | Std. dev. daily return: 0.0136 5 | Sharpe ratio: 2.30 6 | -------------------------------------------------------------------------------- /samples/compinv-1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.stratanalyzer import returns 4 | from pyalgotrade.stratanalyzer import sharpe 5 | from pyalgotrade.utils import stats 6 | 7 | 8 | class MyStrategy(strategy.BacktestingStrategy): 9 | def __init__(self, feed): 10 | super(MyStrategy, self).__init__(feed, 1000000) 11 | 12 | # We wan't to use adjusted close prices instead of close. 13 | self.setUseAdjustedValues(True) 14 | 15 | # Place the orders to get them processed on the first bar. 16 | orders = { 17 | "aeti": 297810, 18 | "egan": 81266, 19 | "glng": 11095, 20 | "simo": 17293, 21 | } 22 | for instrument, quantity in list(orders.items()): 23 | self.marketOrder(instrument, quantity, onClose=True, allOrNone=True) 24 | 25 | def onBars(self, bars): 26 | pass 27 | 28 | # Load the yahoo feed from CSV files. 29 | feed = yahoofeed.Feed() 30 | feed.addBarsFromCSV("aeti", "aeti-2011-yahoofinance.csv") 31 | feed.addBarsFromCSV("egan", "egan-2011-yahoofinance.csv") 32 | feed.addBarsFromCSV("glng", "glng-2011-yahoofinance.csv") 33 | feed.addBarsFromCSV("simo", "simo-2011-yahoofinance.csv") 34 | 35 | # Evaluate the strategy with the feed's bars. 36 | myStrategy = MyStrategy(feed) 37 | 38 | # Attach returns and sharpe ratio analyzers. 39 | retAnalyzer = returns.Returns() 40 | myStrategy.attachAnalyzer(retAnalyzer) 41 | sharpeRatioAnalyzer = sharpe.SharpeRatio() 42 | myStrategy.attachAnalyzer(sharpeRatioAnalyzer) 43 | 44 | # Run the strategy 45 | myStrategy.run() 46 | 47 | # Print the results. 48 | print("Final portfolio value: $%.2f" % myStrategy.getResult()) 49 | print("Anual return: %.2f %%" % (retAnalyzer.getCumulativeReturns()[-1] * 100)) 50 | print("Average daily return: %.2f %%" % (stats.mean(retAnalyzer.getReturns()) * 100)) 51 | print("Std. dev. daily return: %.4f" % (stats.stddev(retAnalyzer.getReturns()))) 52 | print("Sharpe ratio: %.2f" % (sharpeRatioAnalyzer.getSharpeRatio(0))) 53 | -------------------------------------------------------------------------------- /samples/csvfeed_1.output: -------------------------------------------------------------------------------- 1 | 1968-04-07 00:00:00 {'USD': 37.0, 'GBP': 15.3875, 'EUR': ''} 2 | 1968-04-14 00:00:00 {'USD': 38.0, 'GBP': 15.8208, 'EUR': ''} 3 | 1968-04-21 00:00:00 {'USD': 37.65, 'GBP': 15.6833, 'EUR': ''} 4 | 1968-04-28 00:00:00 {'USD': 38.65, 'GBP': 16.1271, 'EUR': ''} 5 | 1968-05-05 00:00:00 {'USD': 39.1, 'GBP': 16.3188, 'EUR': ''} 6 | 1968-05-12 00:00:00 {'USD': 39.6, 'GBP': 16.5625, 'EUR': ''} 7 | 1968-05-19 00:00:00 {'USD': 41.5, 'GBP': 17.3958, 'EUR': ''} 8 | 1968-05-26 00:00:00 {'USD': 41.75, 'GBP': 17.5104, 'EUR': ''} 9 | 1968-06-02 00:00:00 {'USD': 41.95, 'GBP': 17.6, 'EUR': ''} 10 | 1968-06-09 00:00:00 {'USD': 41.25, 'GBP': 17.3042, 'EUR': ''} 11 | . 12 | . 13 | . 14 | 2013-07-28 00:00:00 {'USD': 1331.0, 'GBP': 864.23, 'EUR': 1001.505} 15 | 2013-08-04 00:00:00 {'USD': 1309.25, 'GBP': 858.637, 'EUR': 986.921} 16 | 2013-08-11 00:00:00 {'USD': 1309.0, 'GBP': 843.156, 'EUR': 979.79} 17 | 2013-08-18 00:00:00 {'USD': 1369.25, 'GBP': 875.424, 'EUR': 1024.964} 18 | 2013-08-25 00:00:00 {'USD': 1377.5, 'GBP': 885.738, 'EUR': 1030.6} 19 | 2013-09-01 00:00:00 {'USD': 1394.75, 'GBP': 901.292, 'EUR': 1055.749} 20 | 2013-09-08 00:00:00 {'USD': 1387.0, 'GBP': 886.885, 'EUR': 1052.911} 21 | 2013-09-15 00:00:00 {'USD': 1318.5, 'GBP': 831.546, 'EUR': 993.969} 22 | 2013-09-22 00:00:00 {'USD': 1349.25, 'GBP': 842.755, 'EUR': 997.671} 23 | 2013-09-29 00:00:00 {'USD': 1333.0, 'GBP': 831.203, 'EUR': 986.75} 24 | -------------------------------------------------------------------------------- /samples/csvfeed_1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade.feed import csvfeed 2 | 3 | feed = csvfeed.Feed("Date", "%Y-%m-%d") 4 | feed.addValuesFromCSV("quandl_gold_2.csv") 5 | for dateTime, value in feed: 6 | print(dateTime, value) 7 | -------------------------------------------------------------------------------- /samples/eventstudy.output: -------------------------------------------------------------------------------- 1 | 2013-09-20 23:30:45,340 yahoofinance [INFO] Creating data directory 2 | 2013-09-20 23:30:45,341 yahoofinance [INFO] Downloading AA 2008 to data/AA-2008-yahoofinance.csv 3 | 2013-09-20 23:30:46,092 yahoofinance [INFO] Downloading AES 2008 to data/AES-2008-yahoofinance.csv 4 | 2013-09-20 23:30:46,683 yahoofinance [INFO] Downloading AIG 2008 to data/AIG-2008-yahoofinance.csv 5 | 2013-09-20 23:30:47,260 yahoofinance [INFO] Downloading AA 2009 to data/AA-2009-yahoofinance.csv 6 | 2013-09-20 23:30:48,019 yahoofinance [INFO] Downloading AES 2009 to data/AES-2009-yahoofinance.csv 7 | 2013-09-20 23:30:48,761 yahoofinance [INFO] Downloading AIG 2009 to data/AIG-2009-yahoofinance.csv 8 | 14 events found 9 | -------------------------------------------------------------------------------- /samples/eventstudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/eventstudy.png -------------------------------------------------------------------------------- /samples/eventstudy.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import eventprofiler 2 | from pyalgotrade.technical import stats 3 | from pyalgotrade.technical import roc 4 | from pyalgotrade.technical import ma 5 | from pyalgotrade.tools import yahoofinance 6 | 7 | # Event inspired on an example from Ernie Chan's book: 8 | # 'Algorithmic Trading: Winning Strategies and Their Rationale' 9 | 10 | 11 | class BuyOnGap(eventprofiler.Predicate): 12 | def __init__(self, feed): 13 | super(BuyOnGap, self).__init__() 14 | 15 | stdDevPeriod = 90 16 | smaPeriod = 20 17 | self.__returns = {} 18 | self.__stdDev = {} 19 | self.__ma = {} 20 | for instrument in feed.getRegisteredInstruments(): 21 | priceDS = feed[instrument].getAdjCloseDataSeries() 22 | # Returns over the adjusted close values. 23 | self.__returns[instrument] = roc.RateOfChange(priceDS, 1) 24 | # StdDev over those returns. 25 | self.__stdDev[instrument] = stats.StdDev(self.__returns[instrument], stdDevPeriod) 26 | # MA over the adjusted close values. 27 | self.__ma[instrument] = ma.SMA(priceDS, smaPeriod) 28 | 29 | def __gappedDown(self, instrument, bards): 30 | ret = False 31 | if self.__stdDev[instrument][-1] is not None: 32 | prevBar = bards[-2] 33 | currBar = bards[-1] 34 | low2OpenRet = (currBar.getOpen(True) - prevBar.getLow(True)) / float(prevBar.getLow(True)) 35 | if low2OpenRet < (self.__returns[instrument][-1] - self.__stdDev[instrument][-1]): 36 | ret = True 37 | return ret 38 | 39 | def __aboveSMA(self, instrument, bards): 40 | ret = False 41 | if self.__ma[instrument][-1] is not None and bards[-1].getOpen(True) > self.__ma[instrument][-1]: 42 | ret = True 43 | return ret 44 | 45 | def eventOccurred(self, instrument, bards): 46 | ret = False 47 | if self.__gappedDown(instrument, bards) and self.__aboveSMA(instrument, bards): 48 | ret = True 49 | return ret 50 | 51 | 52 | def main(plot): 53 | instruments = ["AA", "AES", "AIG"] 54 | feed = yahoofinance.build_feed(instruments, 2008, 2009, ".") 55 | 56 | predicate = BuyOnGap(feed) 57 | eventProfiler = eventprofiler.Profiler(predicate, 5, 5) 58 | eventProfiler.run(feed, True) 59 | 60 | results = eventProfiler.getResults() 61 | print("%d events found" % (results.getEventCount())) 62 | if plot: 63 | eventprofiler.plot(results) 64 | 65 | if __name__ == "__main__": 66 | main(True) 67 | -------------------------------------------------------------------------------- /samples/market_timing.output: -------------------------------------------------------------------------------- 1 | 2014-05-25 22:36:59,740 yahoofinance [INFO] Creating data directory 2 | 2014-05-25 22:36:59,740 yahoofinance [INFO] Downloading SPY 2007 to data/SPY-2007-yahoofinance.csv 3 | 2014-05-25 22:37:06,332 yahoofinance [INFO] Downloading VTI 2007 to data/VTI-2007-yahoofinance.csv 4 | 2014-05-25 22:37:10,666 yahoofinance [INFO] Downloading DBC 2007 to data/DBC-2007-yahoofinance.csv 5 | 2014-05-25 22:37:13,102 yahoofinance [INFO] Downloading IEF 2007 to data/IEF-2007-yahoofinance.csv 6 | 2014-05-25 22:37:19,394 yahoofinance [INFO] Downloading VEU 2007 to data/VEU-2007-yahoofinance.csv 7 | 2014-05-25 22:37:27,378 yahoofinance [INFO] Downloading VNQ 2007 to data/VNQ-2007-yahoofinance.csv 8 | 2014-05-25 22:37:32,771 yahoofinance [INFO] Downloading SPY 2008 to data/SPY-2008-yahoofinance.csv 9 | 2014-05-25 22:37:38,326 yahoofinance [INFO] Downloading VTI 2008 to data/VTI-2008-yahoofinance.csv 10 | 2014-05-25 22:37:42,262 yahoofinance [INFO] Downloading DBC 2008 to data/DBC-2008-yahoofinance.csv 11 | . 12 | . 13 | . 14 | 2013-12-02 00:00:00 strategy [INFO] Rebalancing 15 | 2013-12-02 00:00:00 strategy [INFO] Best for class US Stocks: VTI 16 | 2013-12-02 00:00:00 strategy [INFO] Best for class Commodities: None 17 | 2013-12-02 00:00:00 strategy [INFO] Best for class US 10 Year Government Bonds: None 18 | 2013-12-02 00:00:00 strategy [INFO] Best for class Foreign Stocks: VEU 19 | 2013-12-02 00:00:00 strategy [INFO] Best for class Real Estate: None 20 | 2013-12-02 00:00:00 strategy [INFO] Placing market order for -1 VTI shares 21 | 2013-12-02 00:00:00 strategy [INFO] Placing market order for -39 VNQ shares 22 | Sharpe ratio: -0.06 23 | Returns: 32.97 % 24 | -------------------------------------------------------------------------------- /samples/market_timing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/market_timing.png -------------------------------------------------------------------------------- /samples/quandl_sample.output: -------------------------------------------------------------------------------- 1 | 2006-01-01 00:00:00 strategy [INFO] {'USD': 513.0, 'GBP': 298.204, 'EUR': 433.533} 2 | 2006-01-08 00:00:00 strategy [INFO] {'USD': 535.25, 'GBP': 302.572, 'EUR': 440.173} 3 | 2006-01-15 00:00:00 strategy [INFO] {'USD': 548.25, 'GBP': 309.781, 'EUR': 454.489} 4 | 2006-01-22 00:00:00 strategy [INFO] {'USD': 567.25, 'GBP': 321.152, 'EUR': 468.802} 5 | 2006-01-29 00:00:00 strategy [INFO] {'USD': 561.75, 'GBP': 315.147, 'EUR': 460.526} 6 | 2006-02-05 00:00:00 strategy [INFO] {'USD': 569.0, 'GBP': 322.562, 'EUR': 474.167} 7 | 2006-02-12 00:00:00 strategy [INFO] {'USD': 557.0, 'GBP': 317.198, 'EUR': 463.78} 8 | 2006-02-19 00:00:00 strategy [INFO] {'USD': 551.7, 'GBP': 317.251, 'EUR': 463.224} 9 | 2006-02-26 00:00:00 strategy [INFO] {'USD': 554.15, 'GBP': 316.838, 'EUR': 465.555} 10 | 2006-03-05 00:00:00 strategy [INFO] {'USD': 565.0, 'GBP': 322.029, 'EUR': 469.854} 11 | . 12 | . 13 | . 14 | 2012-12-19 00:00:00 strategy [INFO] 15.43 15 | 2012-12-20 00:00:00 strategy [INFO] 15.39 16 | 2012-12-21 00:00:00 strategy [INFO] 15.35 17 | 2012-12-23 00:00:00 strategy [INFO] {'USD': 1651.5, 'GBP': 1019.256, 'EUR': 1253.701} 18 | 2012-12-24 00:00:00 strategy [INFO] 15.2 19 | 2012-12-26 00:00:00 strategy [INFO] 15.56 20 | 2012-12-27 00:00:00 strategy [INFO] 15.24 21 | 2012-12-28 00:00:00 strategy [INFO] 15.09 22 | 2012-12-30 00:00:00 strategy [INFO] {'USD': 1657.5, 'GBP': 1027.206, 'EUR': 1253.024} 23 | 2012-12-31 00:00:00 strategy [INFO] 15.41 24 | -------------------------------------------------------------------------------- /samples/quandl_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/quandl_sample.png -------------------------------------------------------------------------------- /samples/quandl_sample.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade import plotter 3 | from pyalgotrade.tools import quandl 4 | from pyalgotrade.feed import csvfeed 5 | import datetime 6 | 7 | 8 | class MyStrategy(strategy.BacktestingStrategy): 9 | def __init__(self, feed, quandlFeed, instrument): 10 | super(MyStrategy, self).__init__(feed) 11 | self.setUseAdjustedValues(True) 12 | self.__instrument = instrument 13 | 14 | # It is VERY important to add the the extra feed to the event dispatch loop before 15 | # running the strategy. 16 | self.getDispatcher().addSubject(quandlFeed) 17 | 18 | # Subscribe to events from the Quandl feed. 19 | quandlFeed.getNewValuesEvent().subscribe(self.onQuandlData) 20 | 21 | def onQuandlData(self, dateTime, values): 22 | self.info(values) 23 | 24 | def onBars(self, bars): 25 | self.info(bars[self.__instrument].getAdjClose()) 26 | 27 | 28 | def main(plot): 29 | instruments = ["GORO"] 30 | 31 | # Download GORO bars using WIKI source code. 32 | feed = quandl.build_feed("WIKI", instruments, 2006, 2012, ".") 33 | 34 | # Load Quandl CSV downloaded from http://www.quandl.com/OFDP-Open-Financial-Data-Project/GOLD_2-LBMA-Gold-Price-London-Fixings-P-M 35 | quandlFeed = csvfeed.Feed("Date", "%Y-%m-%d") 36 | quandlFeed.setDateRange(datetime.datetime(2006, 1, 1), datetime.datetime(2012, 12, 31)) 37 | quandlFeed.addValuesFromCSV("quandl_gold_2.csv") 38 | 39 | myStrategy = MyStrategy(feed, quandlFeed, instruments[0]) 40 | 41 | if plot: 42 | plt = plotter.StrategyPlotter(myStrategy, True, False, False) 43 | plt.getOrCreateSubplot("quandl").addDataSeries("USD", quandlFeed["USD"]) 44 | plt.getOrCreateSubplot("quandl").addDataSeries("EUR", quandlFeed["EUR"]) 45 | plt.getOrCreateSubplot("quandl").addDataSeries("GBP", quandlFeed["GBP"]) 46 | 47 | myStrategy.run() 48 | 49 | if plot: 50 | plt.plot() 51 | 52 | 53 | if __name__ == "__main__": 54 | main(True) 55 | -------------------------------------------------------------------------------- /samples/rsi2_sample.output: -------------------------------------------------------------------------------- 1 | 2014-05-03 13:49:35,354 yahoofinance [INFO] Downloading DIA 2009 to ./DIA-2009-yahoofinance.csv 2 | 2014-05-03 13:49:36,388 yahoofinance [INFO] Downloading DIA 2010 to ./DIA-2010-yahoofinance.csv 3 | 2014-05-03 13:49:36,900 yahoofinance [INFO] Downloading DIA 2011 to ./DIA-2011-yahoofinance.csv 4 | 2014-05-03 13:49:37,457 yahoofinance [INFO] Downloading DIA 2012 to ./DIA-2012-yahoofinance.csv 5 | Sharpe ratio: -0.11 6 | -------------------------------------------------------------------------------- /samples/rsi2_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/rsi2_sample.png -------------------------------------------------------------------------------- /samples/rsi2_sample.py: -------------------------------------------------------------------------------- 1 | import rsi2 2 | from pyalgotrade import plotter 3 | from pyalgotrade.tools import yahoofinance 4 | from pyalgotrade.stratanalyzer import sharpe 5 | 6 | 7 | def main(plot): 8 | instrument = "DIA" 9 | entrySMA = 200 10 | exitSMA = 5 11 | rsiPeriod = 2 12 | overBoughtThreshold = 90 13 | overSoldThreshold = 10 14 | 15 | # Download the bars. 16 | feed = yahoofinance.build_feed([instrument], 2009, 2012, ".") 17 | 18 | strat = rsi2.RSI2(feed, instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold) 19 | sharpeRatioAnalyzer = sharpe.SharpeRatio() 20 | strat.attachAnalyzer(sharpeRatioAnalyzer) 21 | 22 | if plot: 23 | plt = plotter.StrategyPlotter(strat, True, False, True) 24 | plt.getInstrumentSubplot(instrument).addDataSeries("Entry SMA", strat.getEntrySMA()) 25 | plt.getInstrumentSubplot(instrument).addDataSeries("Exit SMA", strat.getExitSMA()) 26 | plt.getOrCreateSubplot("rsi").addDataSeries("RSI", strat.getRSI()) 27 | plt.getOrCreateSubplot("rsi").addLine("Overbought", overBoughtThreshold) 28 | plt.getOrCreateSubplot("rsi").addLine("Oversold", overSoldThreshold) 29 | 30 | strat.run() 31 | print("Sharpe ratio: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0.05)) 32 | 33 | if plot: 34 | plt.plot() 35 | 36 | 37 | if __name__ == "__main__": 38 | main(True) 39 | -------------------------------------------------------------------------------- /samples/sample-strategy-analyzer.output: -------------------------------------------------------------------------------- 1 | Final portfolio value: $1295887.22 2 | Cumulative returns: 29.59 % 3 | Sharpe ratio: 0.70 4 | Max. drawdown: 24.53 % 5 | Longest drawdown duration: 277 days, 0:00:00 6 | 7 | Total trades: 13 8 | Avg. profit: $14437 9 | Profits std. dev.: $127539 10 | Max. profit: $420866 11 | Min. profit: $-89320 12 | Avg. return: 2 % 13 | Returns std. dev.: 13 % 14 | Max. return: 46 % 15 | Min. return: -7 % 16 | 17 | Profitable trades: 3 18 | Avg. profit: $197053 19 | Profits std. dev.: $158987 20 | Max. profit: $420866 21 | Min. profit: $66537 22 | Avg. return: 21 % 23 | Returns std. dev.: 18 % 24 | Max. return: 46 % 25 | Min. return: 6 % 26 | 27 | Unprofitable trades: 10 28 | Avg. loss: $-40348 29 | Losses std. dev.: $23601 30 | Max. loss: $-89320 31 | Min. loss: $-4516 32 | Avg. return: -3 % 33 | Returns std. dev.: 2 % 34 | Max. return: -0 % 35 | Min. return: -7 % 36 | -------------------------------------------------------------------------------- /samples/sma_crossover.output: -------------------------------------------------------------------------------- 1 | 2013-09-21 00:01:23,813 yahoofinance [INFO] Creating data directory 2 | 2013-09-21 00:01:23,814 yahoofinance [INFO] Downloading aapl 2011 to data/aapl-2011-yahoofinance.csv 3 | 2013-09-21 00:01:25,275 yahoofinance [INFO] Downloading aapl 2012 to data/aapl-2012-yahoofinance.csv 4 | Sharpe ratio: 1.12 5 | -------------------------------------------------------------------------------- /samples/sma_crossover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/sma_crossover.png -------------------------------------------------------------------------------- /samples/sma_crossover.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.technical import ma 3 | from pyalgotrade.technical import cross 4 | 5 | 6 | class SMACrossOver(strategy.BacktestingStrategy): 7 | def __init__(self, feed, instrument, smaPeriod): 8 | super(SMACrossOver, self).__init__(feed) 9 | self.__instrument = instrument 10 | self.__position = None 11 | # We'll use adjusted close values instead of regular close values. 12 | self.setUseAdjustedValues(True) 13 | self.__prices = feed[instrument].getPriceDataSeries() 14 | self.__sma = ma.SMA(self.__prices, smaPeriod) 15 | 16 | def getSMA(self): 17 | return self.__sma 18 | 19 | def onEnterCanceled(self, position): 20 | self.__position = None 21 | 22 | def onExitOk(self, position): 23 | self.__position = None 24 | 25 | def onExitCanceled(self, position): 26 | # If the exit was canceled, re-submit it. 27 | self.__position.exitMarket() 28 | 29 | def onBars(self, bars): 30 | # If a position was not opened, check if we should enter a long position. 31 | if self.__position is None: 32 | if cross.cross_above(self.__prices, self.__sma) > 0: 33 | shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice()) 34 | # Enter a buy market order. The order is good till canceled. 35 | self.__position = self.enterLong(self.__instrument, shares, True) 36 | # Check if we have to exit the position. 37 | elif not self.__position.exitActive() and cross.cross_below(self.__prices, self.__sma) > 0: 38 | self.__position.exitMarket() 39 | -------------------------------------------------------------------------------- /samples/sma_crossover_sample.py: -------------------------------------------------------------------------------- 1 | import sma_crossover 2 | from pyalgotrade import plotter 3 | from pyalgotrade.tools import yahoofinance 4 | from pyalgotrade.stratanalyzer import sharpe 5 | 6 | 7 | def main(plot): 8 | instrument = "aapl" 9 | smaPeriod = 163 10 | 11 | # Download the bars. 12 | feed = yahoofinance.build_feed([instrument], 2011, 2012, ".") 13 | 14 | strat = sma_crossover.SMACrossOver(feed, instrument, smaPeriod) 15 | sharpeRatioAnalyzer = sharpe.SharpeRatio() 16 | strat.attachAnalyzer(sharpeRatioAnalyzer) 17 | 18 | if plot: 19 | plt = plotter.StrategyPlotter(strat, True, False, True) 20 | plt.getInstrumentSubplot(instrument).addDataSeries("sma", strat.getSMA()) 21 | 22 | strat.run() 23 | print("Sharpe ratio: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0.05)) 24 | 25 | if plot: 26 | plt.plot() 27 | 28 | 29 | if __name__ == "__main__": 30 | main(True) 31 | -------------------------------------------------------------------------------- /samples/statarb_erniechan.output: -------------------------------------------------------------------------------- 1 | 2013-09-21 00:02:35,136 yahoofinance [INFO] Creating data directory 2 | 2013-09-21 00:02:35,136 yahoofinance [INFO] Downloading gld 2006 to data/gld-2006-yahoofinance.csv 3 | 2013-09-21 00:02:35,899 yahoofinance [INFO] Downloading gdx 2006 to data/gdx-2006-yahoofinance.csv 4 | 2013-09-21 00:02:36,637 yahoofinance [INFO] Downloading gld 2007 to data/gld-2007-yahoofinance.csv 5 | 2013-09-21 00:02:37,265 yahoofinance [INFO] Downloading gdx 2007 to data/gdx-2007-yahoofinance.csv 6 | 2013-09-21 00:02:37,881 yahoofinance [INFO] Downloading gld 2008 to data/gld-2008-yahoofinance.csv 7 | 2013-09-21 00:02:38,462 yahoofinance [INFO] Downloading gdx 2008 to data/gdx-2008-yahoofinance.csv 8 | 2013-09-21 00:02:39,243 yahoofinance [INFO] Downloading gld 2009 to data/gld-2009-yahoofinance.csv 9 | 2013-09-21 00:02:39,996 yahoofinance [INFO] Downloading gdx 2009 to data/gdx-2009-yahoofinance.csv 10 | 2013-09-21 00:02:40,577 yahoofinance [INFO] Downloading gld 2010 to data/gld-2010-yahoofinance.csv 11 | 2013-09-21 00:02:42,630 yahoofinance [INFO] Downloading gdx 2010 to data/gdx-2010-yahoofinance.csv 12 | 2013-09-21 00:02:43,397 yahoofinance [INFO] Downloading gld 2011 to data/gld-2011-yahoofinance.csv 13 | 2013-09-21 00:02:44,153 yahoofinance [INFO] Downloading gdx 2011 to data/gdx-2011-yahoofinance.csv 14 | 2013-09-21 00:02:44,901 yahoofinance [INFO] Downloading gld 2012 to data/gld-2012-yahoofinance.csv 15 | 2013-09-21 00:02:45,965 yahoofinance [INFO] Downloading gdx 2012 to data/gdx-2012-yahoofinance.csv 16 | Sharpe ratio: -0.20 17 | -------------------------------------------------------------------------------- /samples/statarb_erniechan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/statarb_erniechan.png -------------------------------------------------------------------------------- /samples/technical-1.output: -------------------------------------------------------------------------------- 1 | None 2 | None 3 | 3.0 4 | 6.0 5 | 144.0 6 | -------------------------------------------------------------------------------- /samples/technical-1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import dataseries 2 | from pyalgotrade import technical 3 | 4 | 5 | # An EventWindow is responsible for making calculations using a window of values. 6 | class Accumulator(technical.EventWindow): 7 | def getValue(self): 8 | ret = None 9 | if self.windowFull(): 10 | ret = self.getValues().sum() 11 | return ret 12 | 13 | # Build a sequence based DataSeries. 14 | seqDS = dataseries.SequenceDataSeries() 15 | # Wrap it with a filter that will get fed as new values get added to the underlying DataSeries. 16 | accum = technical.EventBasedFilter(seqDS, Accumulator(3)) 17 | 18 | # Put in some values. 19 | for i in range(0, 50): 20 | seqDS.append(i) 21 | 22 | # Get some values. 23 | print(accum[0]) # Not enough values yet. 24 | print(accum[1]) # Not enough values yet. 25 | print(accum[2]) # Ok, now we should have at least 3 values. 26 | print(accum[3]) 27 | 28 | # Get the last value, which should be equal to 49 + 48 + 47. 29 | print(accum[-1]) 30 | -------------------------------------------------------------------------------- /samples/tutorial-1.output: -------------------------------------------------------------------------------- 1 | 2000-01-03 00:00:00 strategy [INFO] 118.12 2 | 2000-01-04 00:00:00 strategy [INFO] 107.69 3 | 2000-01-05 00:00:00 strategy [INFO] 102.0 4 | . 5 | . 6 | . 7 | 2000-12-27 00:00:00 strategy [INFO] 30.69 8 | 2000-12-28 00:00:00 strategy [INFO] 31.06 9 | 2000-12-29 00:00:00 strategy [INFO] 29.06 10 | -------------------------------------------------------------------------------- /samples/tutorial-1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | 4 | 5 | class MyStrategy(strategy.BacktestingStrategy): 6 | def __init__(self, feed, instrument): 7 | super(MyStrategy, self).__init__(feed) 8 | self.__instrument = instrument 9 | 10 | def onBars(self, bars): 11 | bar = bars[self.__instrument] 12 | self.info(bar.getClose()) 13 | 14 | # Load the yahoo feed from the CSV file 15 | feed = yahoofeed.Feed() 16 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 17 | 18 | # Evaluate the strategy with the feed's bars. 19 | myStrategy = MyStrategy(feed, "orcl") 20 | myStrategy.run() 21 | -------------------------------------------------------------------------------- /samples/tutorial-2.output: -------------------------------------------------------------------------------- 1 | 2000-01-03 00:00:00 strategy [INFO] 118.12 None 2 | 2000-01-04 00:00:00 strategy [INFO] 107.69 None 3 | 2000-01-05 00:00:00 strategy [INFO] 102.0 None 4 | 2000-01-06 00:00:00 strategy [INFO] 96.0 None 5 | 2000-01-07 00:00:00 strategy [INFO] 103.37 None 6 | 2000-01-10 00:00:00 strategy [INFO] 115.75 None 7 | 2000-01-11 00:00:00 strategy [INFO] 112.37 None 8 | 2000-01-12 00:00:00 strategy [INFO] 105.62 None 9 | 2000-01-13 00:00:00 strategy [INFO] 105.06 None 10 | 2000-01-14 00:00:00 strategy [INFO] 106.81 None 11 | 2000-01-18 00:00:00 strategy [INFO] 111.25 None 12 | 2000-01-19 00:00:00 strategy [INFO] 57.13 None 13 | 2000-01-20 00:00:00 strategy [INFO] 59.25 None 14 | 2000-01-21 00:00:00 strategy [INFO] 59.69 None 15 | 2000-01-24 00:00:00 strategy [INFO] 54.19 94.2866666667 16 | 2000-01-25 00:00:00 strategy [INFO] 56.44 90.1746666667 17 | . 18 | . 19 | . 20 | 2000-12-27 00:00:00 strategy [INFO] 30.69 29.9866666667 21 | 2000-12-28 00:00:00 strategy [INFO] 31.06 30.0446666667 22 | 2000-12-29 00:00:00 strategy [INFO] 29.06 30.0946666667 23 | -------------------------------------------------------------------------------- /samples/tutorial-2.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.technical import ma 4 | 5 | 6 | class MyStrategy(strategy.BacktestingStrategy): 7 | def __init__(self, feed, instrument): 8 | super(MyStrategy, self).__init__(feed) 9 | # We want a 15 period SMA over the closing prices. 10 | self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 15) 11 | self.__instrument = instrument 12 | 13 | def onBars(self, bars): 14 | bar = bars[self.__instrument] 15 | self.info("%s %s" % (bar.getClose(), self.__sma[-1])) 16 | 17 | # Load the yahoo feed from the CSV file 18 | feed = yahoofeed.Feed() 19 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 20 | 21 | # Evaluate the strategy with the feed's bars. 22 | myStrategy = MyStrategy(feed, "orcl") 23 | myStrategy.run() 24 | -------------------------------------------------------------------------------- /samples/tutorial-3.output: -------------------------------------------------------------------------------- 1 | 2000-01-03 00:00:00 strategy [INFO] 118.12 None None 2 | 2000-01-04 00:00:00 strategy [INFO] 107.69 None None 3 | 2000-01-05 00:00:00 strategy [INFO] 102.0 None None 4 | 2000-01-06 00:00:00 strategy [INFO] 96.0 None None 5 | 2000-01-07 00:00:00 strategy [INFO] 103.37 None None 6 | 2000-01-10 00:00:00 strategy [INFO] 115.75 None None 7 | 2000-01-11 00:00:00 strategy [INFO] 112.37 None None 8 | 2000-01-12 00:00:00 strategy [INFO] 105.62 None None 9 | 2000-01-13 00:00:00 strategy [INFO] 105.06 None None 10 | 2000-01-14 00:00:00 strategy [INFO] 106.81 None None 11 | 2000-01-18 00:00:00 strategy [INFO] 111.25 None None 12 | 2000-01-19 00:00:00 strategy [INFO] 57.13 None None 13 | 2000-01-20 00:00:00 strategy [INFO] 59.25 None None 14 | 2000-01-21 00:00:00 strategy [INFO] 59.69 None None 15 | 2000-01-24 00:00:00 strategy [INFO] 54.19 23.5673530141 None 16 | 2000-01-25 00:00:00 strategy [INFO] 56.44 25.0687519877 None 17 | 2000-01-26 00:00:00 strategy [INFO] 55.06 24.7476577095 None 18 | 2000-01-27 00:00:00 strategy [INFO] 51.81 23.9690136517 None 19 | 2000-01-28 00:00:00 strategy [INFO] 47.38 22.9108539956 None 20 | 2000-01-31 00:00:00 strategy [INFO] 49.95 24.980004823 None 21 | 2000-02-01 00:00:00 strategy [INFO] 54.0 28.2484181864 None 22 | 2000-02-02 00:00:00 strategy [INFO] 54.31 28.505177315 None 23 | 2000-02-03 00:00:00 strategy [INFO] 56.69 30.5596770599 None 24 | 2000-02-04 00:00:00 strategy [INFO] 57.81 31.5564353751 None 25 | 2000-02-07 00:00:00 strategy [INFO] 59.94 33.5111056589 None 26 | 2000-02-08 00:00:00 strategy [INFO] 59.56 33.3282358994 None 27 | 2000-02-09 00:00:00 strategy [INFO] 59.94 33.7177605915 None 28 | 2000-02-10 00:00:00 strategy [INFO] 62.31 36.2205441255 None 29 | 2000-02-11 00:00:00 strategy [INFO] 59.69 34.6623493641 29.0368892505 30 | 2000-02-14 00:00:00 strategy [INFO] 62.19 37.4284445543 29.9609620198 31 | . 32 | . 33 | . 34 | 2000-12-27 00:00:00 strategy [INFO] 30.69 51.3196802735 49.8506368511 35 | 2000-12-28 00:00:00 strategy [INFO] 31.06 52.1646203455 49.997518354 36 | 2000-12-29 00:00:00 strategy [INFO] 29.06 47.3776678335 50.0790646925 37 | -------------------------------------------------------------------------------- /samples/tutorial-3.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.technical import ma 4 | from pyalgotrade.technical import rsi 5 | 6 | 7 | class MyStrategy(strategy.BacktestingStrategy): 8 | def __init__(self, feed, instrument): 9 | super(MyStrategy, self).__init__(feed) 10 | self.__rsi = rsi.RSI(feed[instrument].getCloseDataSeries(), 14) 11 | self.__sma = ma.SMA(self.__rsi, 15) 12 | self.__instrument = instrument 13 | 14 | def onBars(self, bars): 15 | bar = bars[self.__instrument] 16 | self.info("%s %s %s" % (bar.getClose(), self.__rsi[-1], self.__sma[-1])) 17 | 18 | # Load the yahoo feed from the CSV file 19 | feed = yahoofeed.Feed() 20 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 21 | 22 | # Evaluate the strategy with the feed's bars. 23 | myStrategy = MyStrategy(feed, "orcl") 24 | myStrategy.run() 25 | -------------------------------------------------------------------------------- /samples/tutorial-4.output: -------------------------------------------------------------------------------- 1 | 2000-01-26 00:00:00 strategy [INFO] BUY at $27.26 2 | 2000-01-28 00:00:00 strategy [INFO] SELL at $24.74 3 | 2000-02-03 00:00:00 strategy [INFO] BUY at $26.60 4 | 2000-02-22 00:00:00 strategy [INFO] SELL at $28.40 5 | 2000-02-23 00:00:00 strategy [INFO] BUY at $28.91 6 | 2000-03-31 00:00:00 strategy [INFO] SELL at $38.51 7 | 2000-04-07 00:00:00 strategy [INFO] BUY at $40.19 8 | 2000-04-12 00:00:00 strategy [INFO] SELL at $37.44 9 | 2000-04-19 00:00:00 strategy [INFO] BUY at $37.76 10 | 2000-04-20 00:00:00 strategy [INFO] SELL at $35.45 11 | 2000-04-28 00:00:00 strategy [INFO] BUY at $37.70 12 | 2000-05-05 00:00:00 strategy [INFO] SELL at $35.54 13 | 2000-05-08 00:00:00 strategy [INFO] BUY at $36.17 14 | 2000-05-09 00:00:00 strategy [INFO] SELL at $35.39 15 | 2000-05-16 00:00:00 strategy [INFO] BUY at $37.28 16 | 2000-05-19 00:00:00 strategy [INFO] SELL at $34.58 17 | 2000-05-31 00:00:00 strategy [INFO] BUY at $35.18 18 | 2000-06-23 00:00:00 strategy [INFO] SELL at $38.81 19 | 2000-06-27 00:00:00 strategy [INFO] BUY at $39.56 20 | 2000-06-28 00:00:00 strategy [INFO] SELL at $39.42 21 | 2000-06-29 00:00:00 strategy [INFO] BUY at $39.41 22 | 2000-06-30 00:00:00 strategy [INFO] SELL at $38.60 23 | 2000-07-03 00:00:00 strategy [INFO] BUY at $38.96 24 | 2000-07-05 00:00:00 strategy [INFO] SELL at $36.89 25 | 2000-07-21 00:00:00 strategy [INFO] BUY at $37.19 26 | 2000-07-24 00:00:00 strategy [INFO] SELL at $37.04 27 | 2000-07-26 00:00:00 strategy [INFO] BUY at $35.93 28 | 2000-07-28 00:00:00 strategy [INFO] SELL at $36.08 29 | 2000-08-01 00:00:00 strategy [INFO] BUY at $36.11 30 | 2000-08-02 00:00:00 strategy [INFO] SELL at $35.06 31 | 2000-08-04 00:00:00 strategy [INFO] BUY at $37.61 32 | 2000-09-11 00:00:00 strategy [INFO] SELL at $41.34 33 | 2000-09-29 00:00:00 strategy [INFO] BUY at $39.07 34 | 2000-10-02 00:00:00 strategy [INFO] SELL at $38.30 35 | 2000-10-20 00:00:00 strategy [INFO] BUY at $34.71 36 | 2000-10-31 00:00:00 strategy [INFO] SELL at $31.34 37 | 2000-11-20 00:00:00 strategy [INFO] BUY at $23.35 38 | 2000-11-21 00:00:00 strategy [INFO] SELL at $23.83 39 | 2000-12-01 00:00:00 strategy [INFO] BUY at $25.33 40 | 2000-12-21 00:00:00 strategy [INFO] SELL at $26.72 41 | 2000-12-22 00:00:00 strategy [INFO] BUY at $29.17 42 | Final portfolio value: $979.44 43 | -------------------------------------------------------------------------------- /samples/tutorial-4.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.technical import ma 4 | 5 | 6 | class MyStrategy(strategy.BacktestingStrategy): 7 | def __init__(self, feed, instrument, smaPeriod): 8 | super(MyStrategy, self).__init__(feed, 1000) 9 | self.__position = None 10 | self.__instrument = instrument 11 | # We'll use adjusted close values instead of regular close values. 12 | self.setUseAdjustedValues(True) 13 | self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod) 14 | 15 | def onEnterOk(self, position): 16 | execInfo = position.getEntryOrder().getExecutionInfo() 17 | self.info("BUY at $%.2f" % (execInfo.getPrice())) 18 | 19 | def onEnterCanceled(self, position): 20 | self.__position = None 21 | 22 | def onExitOk(self, position): 23 | execInfo = position.getExitOrder().getExecutionInfo() 24 | self.info("SELL at $%.2f" % (execInfo.getPrice())) 25 | self.__position = None 26 | 27 | def onExitCanceled(self, position): 28 | # If the exit was canceled, re-submit it. 29 | self.__position.exitMarket() 30 | 31 | def onBars(self, bars): 32 | # Wait for enough bars to be available to calculate a SMA. 33 | if self.__sma[-1] is None: 34 | return 35 | 36 | bar = bars[self.__instrument] 37 | # If a position was not opened, check if we should enter a long position. 38 | if self.__position is None: 39 | if bar.getPrice() > self.__sma[-1]: 40 | # Enter a buy market order for 10 shares. The order is good till canceled. 41 | self.__position = self.enterLong(self.__instrument, 10, True) 42 | # Check if we have to exit the position. 43 | elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): 44 | self.__position.exitMarket() 45 | 46 | 47 | def run_strategy(smaPeriod): 48 | # Load the yahoo feed from the CSV file 49 | feed = yahoofeed.Feed() 50 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 51 | 52 | # Evaluate the strategy with the feed. 53 | myStrategy = MyStrategy(feed, "orcl", smaPeriod) 54 | myStrategy.run() 55 | print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity()) 56 | 57 | run_strategy(15) 58 | -------------------------------------------------------------------------------- /samples/tutorial-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/tutorial-5.png -------------------------------------------------------------------------------- /samples/tutorial-5.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import plotter 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.stratanalyzer import returns 4 | import sma_crossover 5 | 6 | # Load the yahoo feed from the CSV file 7 | feed = yahoofeed.Feed() 8 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 9 | 10 | # Evaluate the strategy with the feed's bars. 11 | myStrategy = sma_crossover.SMACrossOver(feed, "orcl", 20) 12 | 13 | # Attach a returns analyzers to the strategy. 14 | returnsAnalyzer = returns.Returns() 15 | myStrategy.attachAnalyzer(returnsAnalyzer) 16 | 17 | # Attach the plotter to the strategy. 18 | plt = plotter.StrategyPlotter(myStrategy) 19 | # Include the SMA in the instrument's subplot to get it displayed along with the closing prices. 20 | plt.getInstrumentSubplot("orcl").addDataSeries("SMA", myStrategy.getSMA()) 21 | # Plot the simple returns on each bar. 22 | plt.getOrCreateSubplot("returns").addDataSeries("Simple returns", returnsAnalyzer.getReturns()) 23 | 24 | # Run the strategy. 25 | myStrategy.run() 26 | myStrategy.info("Final portfolio value: $%.2f" % myStrategy.getResult()) 27 | 28 | # Plot the strategy. 29 | plt.plot() 30 | -------------------------------------------------------------------------------- /samples/tutorial-optimizer-local.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from pyalgotrade.optimizer import local 3 | from pyalgotrade.barfeed import yahoofeed 4 | import rsi2 5 | 6 | 7 | def parameters_generator(): 8 | instrument = ["dia"] 9 | entrySMA = list(range(150, 251)) 10 | exitSMA = list(range(5, 16)) 11 | rsiPeriod = list(range(2, 11)) 12 | overBoughtThreshold = list(range(75, 96)) 13 | overSoldThreshold = list(range(5, 26)) 14 | return itertools.product(instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold) 15 | 16 | 17 | # The if __name__ == '__main__' part is necessary if running on Windows. 18 | if __name__ == '__main__': 19 | # Load the feed from the CSV files. 20 | feed = yahoofeed.Feed() 21 | feed.addBarsFromCSV("dia", "dia-2009.csv") 22 | feed.addBarsFromCSV("dia", "dia-2010.csv") 23 | feed.addBarsFromCSV("dia", "dia-2011.csv") 24 | 25 | local.run(rsi2.RSI2, feed, parameters_generator()) 26 | -------------------------------------------------------------------------------- /samples/tutorial-optimizer-server.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.optimizer import server 4 | 5 | 6 | def parameters_generator(): 7 | instrument = ["dia"] 8 | entrySMA = list(range(150, 251)) 9 | exitSMA = list(range(5, 16)) 10 | rsiPeriod = list(range(2, 11)) 11 | overBoughtThreshold = list(range(75, 96)) 12 | overSoldThreshold = list(range(5, 26)) 13 | return itertools.product(instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold) 14 | 15 | # The if __name__ == '__main__' part is necessary if running on Windows. 16 | if __name__ == '__main__': 17 | # Load the feed from the CSV files. 18 | feed = yahoofeed.Feed() 19 | feed.addBarsFromCSV("dia", "dia-2009.csv") 20 | feed.addBarsFromCSV("dia", "dia-2010.csv") 21 | feed.addBarsFromCSV("dia", "dia-2011.csv") 22 | 23 | # Run the server. 24 | server.serve(feed, parameters_generator(), "localhost", 5000) 25 | -------------------------------------------------------------------------------- /samples/tutorial-optimizer-worker.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade.optimizer import worker 2 | import rsi2 3 | 4 | # The if __name__ == '__main__' part is necessary if running on Windows. 5 | if __name__ == '__main__': 6 | worker.run(rsi2.RSI2, "localhost", 5000, workerName="localworker") 7 | -------------------------------------------------------------------------------- /samples/tutorial_bitstamp_1.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade.bitstamp import barfeed 2 | from pyalgotrade.bitstamp import broker 3 | from pyalgotrade import strategy 4 | from pyalgotrade.technical import ma 5 | from pyalgotrade.technical import cross 6 | 7 | 8 | class Strategy(strategy.BaseStrategy): 9 | def __init__(self, feed, brk): 10 | super(Strategy, self).__init__(feed, brk) 11 | smaPeriod = 20 12 | self.__instrument = "BTC" 13 | self.__prices = feed[self.__instrument].getCloseDataSeries() 14 | self.__sma = ma.SMA(self.__prices, smaPeriod) 15 | self.__bid = None 16 | self.__ask = None 17 | self.__position = None 18 | self.__posSize = 0.05 19 | 20 | # Subscribe to order book update events to get bid/ask prices to trade. 21 | feed.getOrderBookUpdateEvent().subscribe(self.__onOrderBookUpdate) 22 | 23 | def __onOrderBookUpdate(self, orderBookUpdate): 24 | bid = orderBookUpdate.getBidPrices()[0] 25 | ask = orderBookUpdate.getAskPrices()[0] 26 | 27 | if bid != self.__bid or ask != self.__ask: 28 | self.__bid = bid 29 | self.__ask = ask 30 | self.info("Order book updated. Best bid: %s. Best ask: %s" % (self.__bid, self.__ask)) 31 | 32 | def onEnterOk(self, position): 33 | self.info("Position opened at %s" % (position.getEntryOrder().getExecutionInfo().getPrice())) 34 | 35 | def onEnterCanceled(self, position): 36 | self.info("Position entry canceled") 37 | self.__position = None 38 | 39 | def onExitOk(self, position): 40 | self.__position = None 41 | self.info("Position closed at %s" % (position.getExitOrder().getExecutionInfo().getPrice())) 42 | 43 | def onExitCanceled(self, position): 44 | # If the exit was canceled, re-submit it. 45 | self.__position.exitLimit(self.__bid) 46 | 47 | def onBars(self, bars): 48 | bar = bars[self.__instrument] 49 | self.info("Price: %s. Volume: %s." % (bar.getClose(), bar.getVolume())) 50 | 51 | # Wait until we get the current bid/ask prices. 52 | if self.__ask is None: 53 | return 54 | 55 | # If a position was not opened, check if we should enter a long position. 56 | if self.__position is None: 57 | if cross.cross_above(self.__prices, self.__sma) > 0: 58 | self.info("Entry signal. Buy at %s" % (self.__ask)) 59 | self.__position = self.enterLongLimit(self.__instrument, self.__ask, self.__posSize, True) 60 | # Check if we have to close the position. 61 | elif not self.__position.exitActive() and cross.cross_below(self.__prices, self.__sma) > 0: 62 | self.info("Exit signal. Sell at %s" % (self.__bid)) 63 | self.__position.exitLimit(self.__bid) 64 | 65 | 66 | def main(): 67 | barFeed = barfeed.LiveTradeFeed() 68 | brk = broker.PaperTradingBroker(1000, barFeed) 69 | strat = Strategy(barFeed, brk) 70 | 71 | strat.run() 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /samples/tutorial_twitter_bitstamp.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.bitstamp import barfeed 3 | from pyalgotrade.bitstamp import broker 4 | from pyalgotrade.twitter import feed as twitterfeed 5 | 6 | 7 | class Strategy(strategy.BaseStrategy): 8 | def __init__(self, feed, brk, twitterFeed): 9 | super(Strategy, self).__init__(feed, brk) 10 | self.__instrument = "BTC" 11 | 12 | # Subscribe to Twitter events. 13 | twitterFeed.subscribe(self.__onTweet) 14 | 15 | def __onTweet(self, data): 16 | # Refer to https://dev.twitter.com/docs/streaming-apis/messages#Public_stream_messages for 17 | # the information available in data. 18 | try: 19 | self.info("Twitter: %s" % (data["text"])) 20 | except KeyError: 21 | pass 22 | 23 | def onBars(self, bars): 24 | bar = bars[self.__instrument] 25 | self.info("Price: %s. Volume: %s." % (bar.getClose(), bar.getVolume())) 26 | 27 | 28 | def main(): 29 | # Go to http://dev.twitter.com and create an app. 30 | # The consumer key and secret will be generated for you after that. 31 | consumer_key = "" 32 | consumer_secret = "" 33 | 34 | # After the step above, you will be redirected to your app's page. 35 | # Create an access token under the the "Your access token" section 36 | access_token = "" 37 | access_token_secret = "" 38 | 39 | # Create a twitter feed to track BitCoin related events. 40 | track = ["bitcoin", "btc", "mtgox", "bitstamp", "xapo"] 41 | follow = [] 42 | languages = ["en"] 43 | twitterFeed = twitterfeed.TwitterFeed(consumer_key, consumer_secret, access_token, access_token_secret, track, follow, languages) 44 | 45 | barFeed = barfeed.LiveTradeFeed() 46 | brk = broker.PaperTradingBroker(1000, barFeed) 47 | strat = Strategy(barFeed, brk, twitterFeed) 48 | 49 | # It is VERY important to add twitterFeed to the event dispatch loop before running the strategy. 50 | strat.getDispatcher().addSubject(twitterFeed) 51 | 52 | strat.run() 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /samples/vwap_momentum.output: -------------------------------------------------------------------------------- 1 | 2013-09-21 00:01:23,813 yahoofinance [INFO] Creating data directory 2 | 2013-09-21 00:01:23,814 yahoofinance [INFO] Downloading aapl 2011 to data/aapl-2011-yahoofinance.csv 3 | 2013-09-21 00:01:25,275 yahoofinance [INFO] Downloading aapl 2012 to data/aapl-2012-yahoofinance.csv 4 | Sharpe ratio: 0.89 5 | -------------------------------------------------------------------------------- /samples/vwap_momentum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdaJass/pyalgotrade3/5481c214891b7bd8ca76fee4e3f5cfcbb9f9de4e/samples/vwap_momentum.png -------------------------------------------------------------------------------- /samples/vwap_momentum.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade import plotter 3 | from pyalgotrade.tools import yahoofinance 4 | from pyalgotrade.technical import vwap 5 | from pyalgotrade.stratanalyzer import sharpe 6 | 7 | 8 | class VWAPMomentum(strategy.BacktestingStrategy): 9 | def __init__(self, feed, instrument, vwapWindowSize, threshold): 10 | super(VWAPMomentum, self).__init__(feed) 11 | self.__instrument = instrument 12 | self.__vwap = vwap.VWAP(feed[instrument], vwapWindowSize) 13 | self.__threshold = threshold 14 | 15 | def getVWAP(self): 16 | return self.__vwap 17 | 18 | def onBars(self, bars): 19 | vwap = self.__vwap[-1] 20 | if vwap is None: 21 | return 22 | 23 | shares = self.getBroker().getShares(self.__instrument) 24 | price = bars[self.__instrument].getClose() 25 | notional = shares * price 26 | 27 | if price > vwap * (1 + self.__threshold) and notional < 1000000: 28 | self.marketOrder(self.__instrument, 100) 29 | elif price < vwap * (1 - self.__threshold) and notional > 0: 30 | self.marketOrder(self.__instrument, -100) 31 | 32 | 33 | def main(plot): 34 | instrument = "aapl" 35 | vwapWindowSize = 5 36 | threshold = 0.01 37 | 38 | # Download the bars. 39 | feed = yahoofinance.build_feed([instrument], 2011, 2012, ".") 40 | 41 | strat = VWAPMomentum(feed, instrument, vwapWindowSize, threshold) 42 | sharpeRatioAnalyzer = sharpe.SharpeRatio() 43 | strat.attachAnalyzer(sharpeRatioAnalyzer) 44 | 45 | if plot: 46 | plt = plotter.StrategyPlotter(strat, True, False, True) 47 | plt.getInstrumentSubplot(instrument).addDataSeries("vwap", strat.getVWAP()) 48 | 49 | strat.run() 50 | print("Sharpe ratio: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0.05)) 51 | 52 | if plot: 53 | plt.plot() 54 | 55 | if __name__ == "__main__": 56 | main(True) 57 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity=2 3 | detailed-errors=1 4 | nocapture=1 5 | 6 | # Code coverage settings using nose-cov instead of the builtin plugin because: 7 | # 1: It tracks down subprocesses 8 | # 2: It supports coverage config files 9 | 10 | # cov=pyalgotrade 11 | # with-cov=1 12 | # cov-report=html 13 | # cov-config=coverage.cfg 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # PyAlgoTrade 4 | # 5 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | 20 | try: 21 | from setuptools import setup 22 | except ImportError: 23 | from distutils.core import setup 24 | 25 | setup( 26 | name='PyAlgoTrade', 27 | version='0.18', 28 | description='Python Algorithmic Trading', 29 | long_description='Python library for backtesting stock trading strategies.', 30 | author='Gabriel Martin Becedillas Ruiz', 31 | author_email='pyalgotrade@gmail.com', 32 | url='http://gbeced.github.io/pyalgotrade/', 33 | download_url='http://sourceforge.net/projects/pyalgotrade/files/0.18/PyAlgoTrade-0.18.tar.gz/download', 34 | packages=[ 35 | 'pyalgotrade', 36 | 'pyalgotrade.barfeed', 37 | 'pyalgotrade.bitcoincharts', 38 | 'pyalgotrade.bitstamp', 39 | 'pyalgotrade.broker', 40 | 'pyalgotrade.dataseries', 41 | 'pyalgotrade.feed', 42 | 'pyalgotrade.optimizer', 43 | 'pyalgotrade.stratanalyzer', 44 | 'pyalgotrade.strategy', 45 | 'pyalgotrade.talibext', 46 | 'pyalgotrade.technical', 47 | 'pyalgotrade.tools', 48 | 'pyalgotrade.twitter', 49 | 'pyalgotrade.utils', 50 | 'pyalgotrade.websocket', 51 | ], 52 | install_requires=[ 53 | "numpy", 54 | "pytz", 55 | "python-dateutil", 56 | "requests", 57 | ], 58 | extras_require={ 59 | 'Scipy': ["scipy"], 60 | 'TALib': ["Cython", "TA-Lib"], 61 | 'Plotting': ["matplotlib"], 62 | 'Bitstamp': ["ws4py>=0.3.4", "tornado"], 63 | 'Twitter': ["tweepy"], 64 | }, 65 | ) 66 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import strategy 2 | from pyalgotrade.barfeed import yahoofeed 3 | from pyalgotrade.technical import ma 4 | 5 | 6 | class MyStrategy(strategy.BacktestingStrategy): 7 | def __init__(self, feed, instrument, smaPeriod): 8 | super(MyStrategy, self).__init__(feed, 1000) 9 | self.__position = None 10 | self.__instrument = instrument 11 | # We'll use adjusted close values instead of regular close values. 12 | self.setUseAdjustedValues(True) 13 | self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod) 14 | 15 | def onEnterOk(self, position): 16 | execInfo = position.getEntryOrder().getExecutionInfo() 17 | self.info("BUY at $%.2f" % (execInfo.getPrice())) 18 | 19 | def onEnterCanceled(self, position): 20 | self.__position = None 21 | 22 | def onExitOk(self, position): 23 | execInfo = position.getExitOrder().getExecutionInfo() 24 | self.info("SELL at $%.2f" % (execInfo.getPrice())) 25 | self.__position = None 26 | 27 | def onExitCanceled(self, position): 28 | # If the exit was canceled, re-submit it. 29 | self.__position.exitMarket() 30 | 31 | def onBars(self, bars): 32 | # Wait for enough bars to be available to calculate a SMA. 33 | if self.__sma[-1] is None: 34 | return 35 | 36 | bar = bars[self.__instrument] 37 | # If a position was not opened, check if we should enter a long position. 38 | if self.__position is None: 39 | if bar.getPrice() > self.__sma[-1]: 40 | # Enter a buy market order for 10 shares. The order is good till canceled. 41 | self.__position = self.enterLong(self.__instrument, 10, True) 42 | # Check if we have to exit the position. 43 | elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): 44 | self.__position.exitMarket() 45 | 46 | 47 | def run_strategy(smaPeriod): 48 | # Load the yahoo feed from the CSV file 49 | feed = yahoofeed.Feed() 50 | feed.addBarsFromCSV("orcl", "orcl-2000.csv") 51 | 52 | # Evaluate the strategy with the feed. 53 | myStrategy = MyStrategy(feed, "orcl", smaPeriod) 54 | myStrategy.run() 55 | print(("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity())) 56 | 57 | # run_strategy(15) 58 | import datetime 59 | begin=datetime.date(2008, 1, 1) 60 | end=datetime.date(2015,12,31) 61 | url = "http://ichart.finance.yahoo.com/table.csv?s=%s&a=%d&b=%d&c=%d&d=%d&e=%d&f=%d&g=%s&ignore=.csv" % ('orcl', begin.month, begin.day, begin.year, end.month, end.day, end.year, 'd') 62 | print(url) -------------------------------------------------------------------------------- /testcases/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | -------------------------------------------------------------------------------- /testcases/feed_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from pyalgotrade import dispatcher 22 | 23 | 24 | # This will test both the feed and subject interface. 25 | def tstBaseFeedInterface(testCase, feed): 26 | # This tests the observer.Subject interface. 27 | disp = dispatcher.Dispatcher() 28 | disp.addSubject(feed) 29 | disp.run() 30 | 31 | # This tests the feed.BaseFeed interface. 32 | feed.createDataSeries("any", 10) 33 | feed.getNextValues() 34 | -------------------------------------------------------------------------------- /testcases/google_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import os 22 | import datetime 23 | 24 | from . import common 25 | 26 | from pyalgotrade import bar 27 | from pyalgotrade.barfeed import googlefeed 28 | from pyalgotrade.tools import googlefinance 29 | 30 | 31 | class ToolsTestCase(common.TestCase): 32 | def testDownloadAndParseDaily(self): 33 | instrument = "orcl" 34 | 35 | with common.TmpDir() as tmp_path: 36 | path = os.path.join(tmp_path, "orcl-2010.csv") 37 | googlefinance.download_daily_bars(instrument, 2010, path) 38 | bf = googlefeed.Feed() 39 | bf.addBarsFromCSV(instrument, path) 40 | bf.loadAll() 41 | self.assertEqual(bf[instrument][-1].getOpen(), 31.22) 42 | self.assertEqual(bf[instrument][-1].getClose(), 31.30) 43 | 44 | def testBuildDailyFeed(self): 45 | with common.TmpDir() as tmpPath: 46 | instrument = "orcl" 47 | bf = googlefinance.build_feed([instrument], 2010, 2010, storage=tmpPath) 48 | bf.loadAll() 49 | self.assertEqual(bf[instrument][-1].getDateTime(), datetime.datetime(2010, 12, 31)) 50 | self.assertEqual(bf[instrument][-1].getOpen(), 31.22) 51 | self.assertEqual(bf[instrument][-1].getClose(), 31.30) 52 | 53 | def testInvalidInstrument(self): 54 | instrument = "inexistent" 55 | 56 | # Don't skip errors. 57 | with self.assertRaisesRegex(Exception, "400 Client Error: Bad Request"): 58 | with common.TmpDir() as tmpPath: 59 | bf = googlefinance.build_feed([instrument], 2100, 2101, storage=tmpPath, frequency=bar.Frequency.DAY) 60 | 61 | # Skip errors. 62 | with common.TmpDir() as tmpPath: 63 | bf = googlefinance.build_feed( 64 | [instrument], 2100, 2101, storage=tmpPath, frequency=bar.Frequency.DAY, skipErrors=True 65 | ) 66 | bf.loadAll() 67 | self.assertNotIn(instrument, bf) 68 | -------------------------------------------------------------------------------- /testcases/http_server.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import http.server 3 | 4 | 5 | class WebServerThread(threading.Thread): 6 | def __init__(self, host, port, handlerClass): 7 | super(WebServerThread, self).__init__() 8 | self.__host = host 9 | self.__port = port 10 | self.__handlerClass = handlerClass 11 | self.__server = None 12 | 13 | def run(self): 14 | def handler_cls_builder(*args, **kwargs): 15 | return self.__handlerClass(*args, **kwargs) 16 | 17 | self.__server = http.server.HTTPServer((self.__host, self.__port), handler_cls_builder) 18 | self.__server.serve_forever() 19 | 20 | def stop(self): 21 | self.__server.shutdown() 22 | 23 | 24 | # handlerClass should be a subclass of (BaseHTTPServer.BaseHTTPRequestHandler. 25 | # The handler instance will get a thread and server attribute injected. 26 | def run_webserver_thread(host, port, handlerClass): 27 | wss_thread = WebServerThread(host, port, handlerClass) 28 | wss_thread.start() 29 | return wss_thread 30 | -------------------------------------------------------------------------------- /testcases/logger_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from testcases import common 24 | 25 | 26 | class TestCase(common.TestCase): 27 | # Check that strategy and custom logs have the proper datetime, this is, the bars date time. 28 | def testBacktestingLog1(self): 29 | code = """from testcases import logger_test_1 30 | logger_test_1.main() 31 | """ 32 | res = common.run_python_code(code) 33 | expectedLines = [ 34 | "2000-01-01 00:00:00 strategy [INFO] bla", 35 | "2000-01-01 00:00:00 custom [INFO] ble", 36 | ] 37 | self.assertEqual(res.get_output_lines(), expectedLines) 38 | self.assertTrue(res.exit_ok()) 39 | 40 | # Check that strategy and custom logs have the proper datetime, this is, the bars date time. 41 | def testBacktestingLog2(self): 42 | code = """from testcases import logger_test_2 43 | logger_test_2.main() 44 | """ 45 | res = common.run_python_code(code) 46 | self.assertEqual(len(res.get_output_lines()), 3) 47 | self.assertEqual(res.get_output_lines()[0], "2000-01-01 00:00:00 strategy [INFO] bla") 48 | self.assertEqual( 49 | res.get_output_lines()[1], 50 | "2000-01-02 00:00:00 broker.backtesting [DEBUG] Not enough cash to fill orcl order [1] for 1 share/s" 51 | ) 52 | self.assertEqual(res.get_output_lines()[2], "2000-01-02 00:00:00 strategy [INFO] bla") 53 | self.assertTrue(res.exit_ok()) 54 | 55 | # Check that strategy and custom logs have the proper datetime, this is, the current date. 56 | def testNonBacktestingLog3(self): 57 | code = """from testcases import logger_test_3 58 | logger_test_3.main() 59 | """ 60 | res = common.run_python_code(code) 61 | 62 | now = datetime.datetime.now() 63 | self.assertEqual(len(res.get_output_lines()), 2) 64 | for line in res.get_output_lines(True): 65 | self.assertEqual(line.find(str(now.date())), 0) 66 | self.assertNotEqual(res.get_output_lines()[0].find("strategy [INFO] bla"), -1) 67 | self.assertNotEqual(res.get_output_lines()[1].find("custom [INFO] ble"), -1) 68 | self.assertTrue(res.exit_ok()) 69 | -------------------------------------------------------------------------------- /testcases/logger_test_1.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from pyalgotrade import strategy 24 | from pyalgotrade import bar 25 | from pyalgotrade import logger 26 | from pyalgotrade.barfeed import membf 27 | 28 | 29 | class TestBarFeed(membf.BarFeed): 30 | def barsHaveAdjClose(self): 31 | raise NotImplementedError() 32 | 33 | 34 | class BacktestingStrategy(strategy.BacktestingStrategy): 35 | def __init__(self, barFeed, cash): 36 | strategy.BacktestingStrategy.__init__(self, barFeed, cash) 37 | 38 | def onBars(self, bars): 39 | self.info("bla") 40 | logger.getLogger("custom").info("ble") 41 | 42 | 43 | def main(): 44 | bf = TestBarFeed(bar.Frequency.DAY) 45 | bars = [ 46 | bar.BasicBar(datetime.datetime(2000, 1, 1), 10, 10, 10, 10, 10, 10, bar.Frequency.DAY), 47 | ] 48 | bf.addBarsFromSequence("orcl", bars) 49 | 50 | strat = BacktestingStrategy(bf, 1000) 51 | strat.run() 52 | -------------------------------------------------------------------------------- /testcases/logger_test_2.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | import logging 23 | 24 | from pyalgotrade import strategy 25 | from pyalgotrade import bar 26 | from pyalgotrade import logger 27 | from pyalgotrade.barfeed import membf 28 | 29 | 30 | class TestBarFeed(membf.BarFeed): 31 | def barsHaveAdjClose(self): 32 | raise NotImplementedError() 33 | 34 | 35 | class BacktestingStrategy(strategy.BacktestingStrategy): 36 | def __init__(self, barFeed, cash): 37 | strategy.BacktestingStrategy.__init__(self, barFeed, cash) 38 | 39 | def onBars(self, bars): 40 | self.info("bla") 41 | self.marketOrder("orcl", 1) 42 | 43 | 44 | def main(): 45 | bf = TestBarFeed(bar.Frequency.DAY) 46 | bars = [ 47 | bar.BasicBar(datetime.datetime(2000, 1, 1), 10, 10, 10, 10, 10, 10, bar.Frequency.DAY), 48 | bar.BasicBar(datetime.datetime(2000, 1, 2), 10, 10, 10, 10, 10, 10, bar.Frequency.DAY), 49 | ] 50 | bf.addBarsFromSequence("orcl", bars) 51 | 52 | logger.getLogger().setLevel(logging.DEBUG) 53 | strat = BacktestingStrategy(bf, 1) 54 | strat.run() 55 | -------------------------------------------------------------------------------- /testcases/logger_test_3.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from pyalgotrade import strategy 24 | from pyalgotrade.broker import backtesting 25 | from pyalgotrade import bar 26 | from pyalgotrade import logger 27 | from pyalgotrade.barfeed import membf 28 | 29 | 30 | class TestBarFeed(membf.BarFeed): 31 | def barsHaveAdjClose(self): 32 | raise NotImplementedError() 33 | 34 | 35 | class Strategy(strategy.BaseStrategy): 36 | def __init__(self, barFeed, cash): 37 | strategy.BaseStrategy.__init__(self, barFeed, backtesting.Broker(cash, barFeed)) 38 | 39 | def onBars(self, bars): 40 | self.info("bla") 41 | logger.getLogger("custom").info("ble") 42 | 43 | 44 | def main(): 45 | bf = TestBarFeed(bar.Frequency.DAY) 46 | bars = [ 47 | bar.BasicBar(datetime.datetime(2000, 1, 1), 10, 10, 10, 10, 10, 10, bar.Frequency.DAY), 48 | ] 49 | bf.addBarsFromSequence("orcl", bars) 50 | 51 | strat = Strategy(bf, 1000) 52 | strat.run() 53 | -------------------------------------------------------------------------------- /testcases/memfeed_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from . import common 24 | 25 | from pyalgotrade.feed import memfeed 26 | from pyalgotrade import dispatcher 27 | from . import feed_test 28 | 29 | 30 | class MemFeedTestCase(common.TestCase): 31 | def testBaseFeedInterface(self): 32 | values = [(datetime.datetime.now() + datetime.timedelta(seconds=i), {"i": i}) for i in range(100)] 33 | feed = memfeed.MemFeed() 34 | feed.addValues(values) 35 | feed_test.tstBaseFeedInterface(self, feed) 36 | 37 | def testFeed(self): 38 | values = [(datetime.datetime.now() + datetime.timedelta(seconds=i), {"i": i}) for i in range(100)] 39 | 40 | feed = memfeed.MemFeed() 41 | feed.addValues(values) 42 | 43 | # Check that the dataseries are available after adding values. 44 | self.assertTrue("i" in feed) 45 | self.assertEqual(len(feed["i"]), 0) 46 | self.assertFalse("dt" in feed) 47 | 48 | disp = dispatcher.Dispatcher() 49 | disp.addSubject(feed) 50 | disp.run() 51 | 52 | self.assertTrue("i" in feed) 53 | self.assertFalse("dt" in feed) 54 | self.assertEqual(feed["i"][0], 0) 55 | self.assertEqual(feed["i"][-1], 99) 56 | 57 | def testReset(self): 58 | key = "i" 59 | values = [(datetime.datetime.now() + datetime.timedelta(seconds=i), {key: i}) for i in range(100)] 60 | 61 | feed = memfeed.MemFeed() 62 | feed.addValues(values) 63 | 64 | disp = dispatcher.Dispatcher() 65 | disp.addSubject(feed) 66 | disp.run() 67 | 68 | keys = feed.getKeys() 69 | values = feed[key] 70 | 71 | feed.reset() 72 | disp = dispatcher.Dispatcher() 73 | disp.addSubject(feed) 74 | disp.run() 75 | reloadedKeys = feed.getKeys() 76 | reloadedValues = feed[key] 77 | 78 | self.assertEqual(keys, reloadedKeys) 79 | self.assertNotEqual(values, reloadedValues) 80 | self.assertEqual(len(values), len(reloadedValues)) 81 | for i in range(len(values)): 82 | self.assertEqual(values[i], reloadedValues[i]) 83 | -------------------------------------------------------------------------------- /testcases/optimizer_testcase.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | import logging 23 | 24 | from . import common 25 | 26 | from pyalgotrade.optimizer import local 27 | from pyalgotrade import strategy 28 | from pyalgotrade.barfeed import yahoofeed 29 | 30 | sys.path.append("samples") 31 | import sma_crossover 32 | 33 | 34 | def parameters_generator(instrument, smaFirst, smaLast): 35 | for sma in range(smaFirst, smaLast+1): 36 | yield(instrument, sma) 37 | 38 | 39 | class FailingStrategy(strategy.BacktestingStrategy): 40 | def __init__(self, barFeed, instrument, smaPeriod): 41 | super(FailingStrategy, self).__init__(barFeed) 42 | 43 | def onBars(self, bars): 44 | raise Exception("oh no!") 45 | 46 | 47 | class OptimizerTestCase(common.TestCase): 48 | def testLocal(self): 49 | barFeed = yahoofeed.Feed() 50 | instrument = "orcl" 51 | barFeed.addBarsFromCSV(instrument, common.get_data_file_path("orcl-2000-yahoofinance.csv")) 52 | res = local.run( 53 | sma_crossover.SMACrossOver, barFeed, parameters_generator(instrument, 5, 100), logLevel=logging.DEBUG 54 | ) 55 | self.assertEqual(round(res.getResult(), 2), 1295462.6) 56 | self.assertEqual(res.getParameters()[1], 20) 57 | 58 | def testFailingStrategy(self): 59 | barFeed = yahoofeed.Feed() 60 | instrument = "orcl" 61 | barFeed.addBarsFromCSV(instrument, common.get_data_file_path("orcl-2000-yahoofinance.csv")) 62 | res = local.run(FailingStrategy, barFeed, parameters_generator(instrument, 5, 100), logLevel=logging.DEBUG) 63 | self.assertIsNone(res) 64 | -------------------------------------------------------------------------------- /testcases/plotter_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | import os 23 | 24 | from . import common 25 | 26 | from pyalgotrade.barfeed import yahoofeed 27 | from pyalgotrade import plotter 28 | 29 | sys.path.append("samples") 30 | import sma_crossover 31 | 32 | 33 | class PlotterTestCase(common.TestCase): 34 | def testDownloadAndParseDaily(self): 35 | instrument = "orcl" 36 | barFeed = yahoofeed.Feed() 37 | barFeed.addBarsFromCSV(instrument, common.get_data_file_path("orcl-2000-yahoofinance.csv")) 38 | strat = sma_crossover.SMACrossOver(barFeed, instrument, 20) 39 | plt = plotter.StrategyPlotter(strat, True, True, True) 40 | plt.getInstrumentSubplot(instrument).addDataSeries("sma", strat.getSMA()) 41 | strat.run() 42 | 43 | with common.TmpDir() as tmpPath: 44 | fig, subplots = plt.buildFigureAndSubplots() 45 | self.assertIsNotNone(fig) 46 | self.assertIsNotNone(subplots) 47 | fig = plt.buildFigure() 48 | fig.set_size_inches(10, 8) 49 | png = os.path.join(tmpPath, "plotter_test.png") 50 | fig.savefig(png) 51 | # Check that file size looks ok. 52 | # 118458 on Mac 53 | # 116210 on Linux 54 | self.assertGreater( 55 | os.stat(png).st_size, 56 | 110000 57 | ) 58 | -------------------------------------------------------------------------------- /testcases/pusher_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import unittest 22 | import threading 23 | import datetime 24 | 25 | from pyalgotrade.websocket import pusher 26 | 27 | 28 | class WebSocketClient(pusher.WebSocketClient): 29 | def __init__(self): 30 | pusher.WebSocketClient .__init__(self, "de504dc5763aeef9ff52", maxInactivity=1) 31 | self.__errors = 0 32 | self.__unknown_events = 0 33 | self.__connected = None 34 | 35 | def __checkStop(self): 36 | if self.__errors == 0: 37 | return 38 | if self.__unknown_events == 0: 39 | return 40 | # Give it some time to send ping messages. 41 | if self.__connected is None or (datetime.datetime.now() - self.__connected).total_seconds() < 3: 42 | return 43 | 44 | self.close() 45 | 46 | def onConnectionEstablished(self, event): 47 | pusher.WebSocketClient.onConnectionEstablished(self, event) 48 | self.__connected = datetime.datetime.now() 49 | self.sendPong() 50 | self.subscribeChannel("invalid channel") 51 | self.subscribeChannel("order_book") 52 | self.subscribeChannel("live_trades") 53 | 54 | def onError(self, event): 55 | self.__errors += 1 56 | self.__checkStop() 57 | 58 | def onUnknownEvent(self, event): 59 | self.__unknown_events += 1 60 | self.__checkStop() 61 | 62 | def stop(self): 63 | self.close() 64 | 65 | 66 | class WebSocketClientThread(threading.Thread): 67 | def __init__(self): 68 | threading.Thread.__init__(self) 69 | self.__wsclient = WebSocketClient() 70 | 71 | def run(self): 72 | self.__wsclient.connect() 73 | self.__wsclient.startClient() 74 | 75 | def stop(self): 76 | self.__wsclient.stop() 77 | 78 | 79 | class TestCase(unittest.TestCase): 80 | def test_pusher(self): 81 | thread = WebSocketClientThread() 82 | thread.start() 83 | thread.join(30) 84 | # After 30 seconds the thread should have finished. 85 | if thread.isAlive(): 86 | thread.stop() 87 | self.assertTrue(False) 88 | -------------------------------------------------------------------------------- /testcases/technical_cumret_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade import dataseries 24 | from pyalgotrade.technical import cumret 25 | 26 | 27 | class CumRetTestCase(common.TestCase): 28 | def testCumRet(self): 29 | values = dataseries.SequenceDataSeries() 30 | rets = cumret.CumulativeReturn(values) 31 | for value in [1, 2, 3, 4, 4, 3, 1, 1.2]: 32 | values.append(value) 33 | self.assertEqual(rets[0], None) 34 | self.assertEqual(rets[1], 1) 35 | self.assertEqual(rets[2], 2) 36 | self.assertEqual(rets[3], 3) 37 | self.assertEqual(rets[4], 3) 38 | self.assertEqual(rets[5], 2) 39 | self.assertEqual(rets[6], 0) 40 | self.assertEqual(round(rets[7], 1), 0.2) 41 | -------------------------------------------------------------------------------- /testcases/technical_highlow_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade import dataseries 24 | from pyalgotrade.technical import highlow 25 | 26 | 27 | class HighLowTestCase(common.TestCase): 28 | def testHighLow(self): 29 | values = dataseries.SequenceDataSeries() 30 | high = highlow.High(values, 5) 31 | low = highlow.Low(values, 3) 32 | for value in [1, 2, 3, 4, 5]: 33 | values.append(value) 34 | self.assertEqual(high[-1], 5) 35 | self.assertEqual(low[-1], 3) 36 | -------------------------------------------------------------------------------- /testcases/technical_hurst_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import numpy as np 22 | 23 | from . import common 24 | 25 | from pyalgotrade.technical import hurst 26 | from pyalgotrade import dataseries 27 | 28 | 29 | def build_hurst(values, period, minLags, maxLags): 30 | ds = dataseries.SequenceDataSeries() 31 | ret = hurst.HurstExponent(ds, period, minLags, maxLags) 32 | for value in values: 33 | ds.append(value) 34 | return ret 35 | 36 | 37 | class TestCase(common.TestCase): 38 | def testHurstExpFunRandomWalk(self): 39 | values = np.cumsum(np.random.randn(50000)) + 1000 40 | h = hurst.hurst_exp(np.log10(values), 2, 20) 41 | self.assertEqual(round(h, 1), 0.5) 42 | 43 | def testHurstExpFunTrending(self): 44 | values = np.cumsum(np.random.randn(50000)+1) + 1000 45 | h = hurst.hurst_exp(np.log10(values), 2, 20) 46 | self.assertEqual(round(h), 1) 47 | 48 | def testHurstExpFunMeanRev(self): 49 | values = (np.random.randn(50000)) + 1000 50 | h = hurst.hurst_exp(np.log10(values), 2, 20) 51 | self.assertEqual(round(h), 0) 52 | 53 | def testRandomWalk(self): 54 | num_values = 10000 55 | values = np.cumsum(np.random.randn(num_values)) + 1000 56 | hds = build_hurst(values, num_values - 10, 2, 20) 57 | self.assertEqual(round(hds[-1], 1), 0.5) 58 | self.assertEqual(round(hds[-2], 1), 0.5) 59 | 60 | def testTrending(self): 61 | num_values = 10000 62 | values = np.cumsum(np.random.randn(num_values) + 10) + 1000 63 | hds = build_hurst(values, num_values - 10, 2, 20) 64 | self.assertEqual(round(hds[-1], 1), 1) 65 | self.assertEqual(round(hds[-2], 1), 1) 66 | 67 | def testMeanRev(self): 68 | num_values = 10000 69 | values = np.random.randn(num_values) + 100 70 | hds = build_hurst(values, num_values - 10, 2, 20) 71 | self.assertEqual(round(hds[-1], 1), 0) 72 | self.assertEqual(round(hds[-2], 1), 0) 73 | -------------------------------------------------------------------------------- /testcases/technical_linreg_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from . import common 24 | 25 | from pyalgotrade.technical import linreg 26 | from pyalgotrade import dataseries 27 | 28 | 29 | class LeastSquaresRegressionTestCase(common.TestCase): 30 | def testLsreg1(self): 31 | x = [0, 1, 2] 32 | y = [1, 2, 3] 33 | a, b = linreg.lsreg(x, y) 34 | self.assertEqual(round(a, 2), 1.0) 35 | self.assertEqual(round(b, 2), 1.0) 36 | 37 | def testLsreg2(self): 38 | x = [0, 1, 2] 39 | y = [4, 5, 6] 40 | a, b = linreg.lsreg(x, y) 41 | self.assertEqual(round(a, 2), 1.0) 42 | self.assertEqual(round(b, 2), 4.0) 43 | 44 | def testLsreg3(self): 45 | x = [1, 2, 3] 46 | y = [1, 2, 3] 47 | a, b = linreg.lsreg(x, y) 48 | self.assertEqual(round(a, 2), 1.0) 49 | self.assertEqual(round(b, 2), 0) 50 | 51 | def testStraightLine(self): 52 | seqDS = dataseries.SequenceDataSeries() 53 | lsReg = linreg.LeastSquaresRegression(seqDS, 3) 54 | 55 | nextDateTime = datetime.datetime(2012, 1, 1) 56 | seqDS.appendWithDateTime(nextDateTime, 1) 57 | self.assertEqual(lsReg[-1], None) 58 | 59 | nextDateTime = nextDateTime + datetime.timedelta(hours=1) 60 | seqDS.appendWithDateTime(nextDateTime, 2) 61 | self.assertEqual(lsReg[-1], None) 62 | 63 | # Check current value. 64 | nextDateTime = nextDateTime + datetime.timedelta(hours=1) 65 | seqDS.appendWithDateTime(nextDateTime, 3) 66 | self.assertEqual(round(lsReg[-1], 2), 3) 67 | 68 | # Check future values. 69 | futureDateTime = nextDateTime + datetime.timedelta(hours=1) 70 | self.assertEqual(round(lsReg.getValueAt(futureDateTime), 2), 4) 71 | futureDateTime = futureDateTime + datetime.timedelta(minutes=30) 72 | self.assertEqual(round(lsReg.getValueAt(futureDateTime), 2), 4.5) 73 | futureDateTime = futureDateTime + datetime.timedelta(minutes=30) 74 | self.assertEqual(round(lsReg.getValueAt(futureDateTime), 2), 5) 75 | 76 | # Move forward in sub-second increments. 77 | nextDateTime = nextDateTime + datetime.timedelta(milliseconds=50) 78 | seqDS.appendWithDateTime(nextDateTime, 4) 79 | nextDateTime = nextDateTime + datetime.timedelta(milliseconds=50) 80 | seqDS.appendWithDateTime(nextDateTime, 5) 81 | self.assertEqual(round(lsReg[-1], 2), 5) 82 | -------------------------------------------------------------------------------- /testcases/technical_macd_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade.technical import macd 24 | from pyalgotrade import dataseries 25 | 26 | 27 | class MACDTestCase(common.TestCase): 28 | def testMACD(self): 29 | values = [16.39, 16.4999, 16.45, 16.43, 16.52, 16.51, 16.423, 16.41, 16.47, 16.45, 16.32, 16.36, 16.34, 16.59, 16.54, 16.52, 16.44, 16.47, 16.5, 16.45, 16.28, 16.07, 16.08, 16.1, 16.1, 16.09, 16.43, 16.4899, 16.59, 16.65, 16.78, 16.86, 16.86, 16.76] 30 | # These expected values were generated using TA-Lib 31 | macdValues = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0.0067, 0.0106, 0.0028, -0.0342, -0.0937, -0.1214, -0.1276, -0.125, -0.1195, -0.0459, 0.0097, 0.0601, 0.0975, 0.139, 0.1713, 0.1816, 0.1598] 32 | signalValues = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0.0036, 0.0056, 0.0048, -0.0064, -0.0313, -0.057, -0.0772, -0.0909, -0.0991, -0.0839, -0.0571, -0.0236, 0.011, 0.0475, 0.0829, 0.1111, 0.125] 33 | histogramValues = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0.0031, 0.005, -0.002, -0.0279, -0.0624, -0.0643, -0.0504, -0.0342, -0.0205, 0.0379, 0.0668, 0.0838, 0.0865, 0.0914, 0.0884, 0.0705, 0.0348] 34 | ds = dataseries.SequenceDataSeries() 35 | 36 | macdDs = macd.MACD(ds, 5, 13, 6) 37 | for i, value in enumerate(values): 38 | ds.append(value) 39 | self.assertEqual(common.safe_round(macdDs[i], 4), macdValues[i]) 40 | self.assertEqual(common.safe_round(macdDs.getSignal()[i], 4), signalValues[i]) 41 | self.assertEqual(common.safe_round(macdDs.getHistogram()[i], 4), histogramValues[i]) 42 | -------------------------------------------------------------------------------- /testcases/technical_ratio_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade.technical import ratio 24 | from pyalgotrade import dataseries 25 | 26 | 27 | class TestCase(common.TestCase): 28 | def __buildRatio(self, values, ratioMaxLen=None): 29 | seqDS = dataseries.SequenceDataSeries() 30 | ret = ratio.Ratio(seqDS, ratioMaxLen) 31 | for value in values: 32 | seqDS.append(value) 33 | return ret 34 | 35 | def testSimple(self): 36 | ratio = self.__buildRatio([1, 2, 1]) 37 | self.assertEqual(ratio[0], None) 38 | self.assertEqual(ratio[1], 1) 39 | self.assertEqual(ratio[2], -0.5) 40 | self.assertEqual(ratio[-1], -0.5) 41 | with self.assertRaises(IndexError): 42 | ratio[3] 43 | 44 | self.assertEqual(ratio[-2], ratio[1]) 45 | self.assertEqual(ratio[-1], ratio[2]) 46 | 47 | self.assertEqual(len(ratio.getDateTimes()), 3) 48 | for i in range(len(ratio)): 49 | self.assertEqual(ratio.getDateTimes()[i], None) 50 | 51 | def testNegativeValues(self): 52 | ratio = self.__buildRatio([-1, -2, -1]) 53 | self.assertEqual(ratio[0], None) 54 | self.assertEqual(ratio[1], -1) 55 | self.assertEqual(ratio[2], 0.5) 56 | self.assertEqual(ratio[-1], 0.5) 57 | with self.assertRaises(IndexError): 58 | ratio[3] 59 | 60 | self.assertEqual(ratio[-2], ratio[1]) 61 | self.assertEqual(ratio[-1], ratio[2]) 62 | 63 | self.assertEqual(len(ratio.getDateTimes()), 3) 64 | for i in range(len(ratio)): 65 | self.assertEqual(ratio.getDateTimes()[i], None) 66 | 67 | def testBounded(self): 68 | ratio = self.__buildRatio([-1, -2, -1], 2) 69 | self.assertEqual(ratio[0], -1) 70 | self.assertEqual(ratio[1], 0.5) 71 | self.assertEqual(len(ratio), 2) 72 | -------------------------------------------------------------------------------- /testcases/technical_roc_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade.technical import roc 24 | from pyalgotrade import dataseries 25 | 26 | 27 | class ROCTestCase(common.TestCase): 28 | def __buildROC(self, values, period, rocMaxLen=None): 29 | seqDS = dataseries.SequenceDataSeries() 30 | ret = roc.RateOfChange(seqDS, period, rocMaxLen) 31 | for value in values: 32 | seqDS.append(value) 33 | return ret 34 | 35 | def testPeriod12(self): 36 | # http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:rate_of_change 37 | inputValues = [11045.27, 11167.32, 11008.61, 11151.83, 10926.77, 10868.12, 10520.32, 10380.43, 10785.14, 10748.26, 10896.91, 10782.95, 10620.16, 10625.83, 10510.95, 10444.37, 10068.01, 10193.39, 10066.57, 10043.75] 38 | roc_ = self.__buildROC(inputValues, 12) 39 | outputValues = [-3.85, -4.85, -4.52, -6.34, -7.86, -6.21, -4.31, -3.24] 40 | for i in range(len(outputValues)): 41 | outputValue = roc_[12 + i] * 100 42 | self.assertTrue(round(outputValue, 2) == outputValues[i]) 43 | 44 | self.assertEqual(len(roc_.getDateTimes()), len(inputValues)) 45 | for i in range(len(roc_)): 46 | self.assertEqual(roc_.getDateTimes()[i], None) 47 | 48 | def testPeriod1(self): 49 | def simple_roc(value1, value2): 50 | return self.__buildROC([value1, value2], 1)[1] 51 | 52 | self.assertTrue(simple_roc(1, 2) == 1) 53 | self.assertTrue(simple_roc(1, 2) == simple_roc(50, 100)) 54 | self.assertTrue(simple_roc(2, 1) == -0.5) 55 | self.assertTrue(simple_roc(2, 1) == simple_roc(100, 50)) 56 | 57 | def testBounded(self): 58 | # http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:rate_of_change 59 | inputValues = [11045.27, 11167.32, 11008.61, 11151.83, 10926.77, 10868.12, 10520.32, 10380.43, 10785.14, 10748.26, 10896.91, 10782.95, 10620.16, 10625.83, 10510.95, 10444.37, 10068.01, 10193.39, 10066.57, 10043.75] 60 | outputValues = [-4.31, -3.24] 61 | roc_ = self.__buildROC(inputValues, 12, 2) 62 | for i in range(2): 63 | self.assertEqual(round(roc_[i], 4), round(outputValues[i] / 100, 4)) 64 | 65 | def testZeroes(self): 66 | inputValues = [0, 0, 0] 67 | outputValues = [None, 0, 0] 68 | roc_ = self.__buildROC(inputValues, 1) 69 | for i in range(len(inputValues)): 70 | self.assertEqual(roc_[i], outputValues[i]) 71 | -------------------------------------------------------------------------------- /testcases/technical_stats_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import numpy 22 | 23 | from . import common 24 | 25 | from pyalgotrade.technical import stats 26 | from pyalgotrade import dataseries 27 | 28 | 29 | class TestCase(common.TestCase): 30 | def testStdDev_1(self): 31 | values = [1, 1, 2, 3, 5] 32 | seqDS = dataseries.SequenceDataSeries() 33 | stdDev = stats.StdDev(seqDS, 1) 34 | for value in values: 35 | seqDS.append(value) 36 | for i in stdDev: 37 | self.assertEqual(i, 0) 38 | 39 | def testStdDev(self): 40 | values = [1, 1, 2, 3, 5] 41 | seqDS = dataseries.SequenceDataSeries() 42 | stdDev = stats.StdDev(seqDS, 2) 43 | for value in values: 44 | seqDS.append(value) 45 | 46 | self.assertEqual(stdDev[0], None) 47 | self.assertEqual(stdDev[1], numpy.array([1, 1]).std()) 48 | self.assertEqual(stdDev[2], numpy.array([1, 2]).std()) 49 | self.assertEqual(stdDev[3], numpy.array([2, 3]).std()) 50 | self.assertEqual(stdDev[4], numpy.array([3, 5]).std()) 51 | 52 | def testStdDev_Bounded(self): 53 | values = [1, 1, 2, 3, 5] 54 | seqDS = dataseries.SequenceDataSeries() 55 | stdDev = stats.StdDev(seqDS, 2, maxLen=2) 56 | for value in values: 57 | seqDS.append(value) 58 | 59 | self.assertEqual(stdDev[0], numpy.array([2, 3]).std()) 60 | self.assertEqual(stdDev[1], numpy.array([3, 5]).std()) 61 | 62 | def testZScore(self): 63 | values = [1.10, 2.20, 4.00, 5.10, 6.00, 7.10, 8.20, 9.00, 10.10, 3.00, 4.10, 5.20, 7.00, 8.10, 9.20, 16.00, 17.10, 18.20, 19.30, 20.40] 64 | expected = [None, None, None, None, 1.283041407, 1.317884611, 1.440611043, 1.355748299, 1.4123457, -1.831763202, -0.990484842, -0.388358578, 0.449889908, 1.408195169, 1.332948099, 1.867732104, 1.334258333, 1.063608066, 0.939656572, 1.414213562] 65 | seqDS = dataseries.SequenceDataSeries() 66 | zscore = stats.ZScore(seqDS, 5) 67 | i = 0 68 | for value in values: 69 | seqDS.append(value) 70 | if i >= 4: 71 | self.assertEqual(round(zscore[-1], 4), round(expected[i], 4)) 72 | i += 1 73 | -------------------------------------------------------------------------------- /testcases/technical_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from . import common 22 | 23 | from pyalgotrade import technical 24 | from pyalgotrade import dataseries 25 | 26 | 27 | class TestEventWindow(technical.EventWindow): 28 | def __init__(self): 29 | technical.EventWindow.__init__(self, 1, skipNone=False, dtype=object) 30 | 31 | def getValue(self): 32 | return self.getValues()[-1] 33 | 34 | 35 | class TestFilter(technical.EventBasedFilter): 36 | def __init__(self, dataSeries): 37 | technical.EventBasedFilter.__init__(self, dataSeries, TestEventWindow()) 38 | 39 | 40 | class DataSeriesFilterTest(common.TestCase): 41 | def testInvalidPosNotCached(self): 42 | ds = dataseries.SequenceDataSeries() 43 | testFilter = TestFilter(ds) 44 | for i in range(10): 45 | ds.append(i) 46 | ds.append(None) # Interleave Nones. 47 | 48 | self.assertEqual(testFilter[-1], None) 49 | self.assertEqual(testFilter[-2], 9) 50 | self.assertEqual(testFilter[-4], 8) # We go 3 instead of 2 because we need to skip the interleaved None values. 51 | 52 | self.assertEqual(testFilter[18], 9) 53 | self.assertEqual(testFilter[19], None) 54 | # Absolut pos 20 should have the next value once we insert it, but right now it should be invalid. 55 | with self.assertRaises(IndexError): 56 | testFilter[20] 57 | ds.append(10) 58 | self.assertEqual(testFilter[20], 10) 59 | -------------------------------------------------------------------------------- /testcases/test_strategy.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import datetime 22 | 23 | from pyalgotrade import strategy 24 | 25 | 26 | class BaseTestStrategy(strategy.BaseStrategy): 27 | def __init__(self, barFeed, broker, maxMinutes=5): 28 | super(BaseTestStrategy, self).__init__(barFeed, broker) 29 | self.posExecutionInfo = [] 30 | self.ordersUpdated = [] 31 | self.orderExecutionInfo = [] 32 | self.begin = datetime.datetime.now() 33 | self.deadline = self.begin + datetime.timedelta(minutes=maxMinutes) 34 | 35 | def onOrderUpdated(self, order): 36 | self.ordersUpdated.append(order) 37 | self.orderExecutionInfo.append(order.getExecutionInfo()) 38 | 39 | def onEnterOk(self, position): 40 | self.posExecutionInfo.append(position.getEntryOrder().getExecutionInfo()) 41 | 42 | def onEnterCanceled(self, position): 43 | self.posExecutionInfo.append(position.getEntryOrder().getExecutionInfo()) 44 | 45 | def onExitOk(self, position): 46 | self.posExecutionInfo.append(position.getExitOrder().getExecutionInfo()) 47 | 48 | def onExitCanceled(self, position): 49 | self.posExecutionInfo.append(position.getExitOrder().getExecutionInfo()) 50 | 51 | def onIdle(self): 52 | if datetime.datetime.now() >= self.deadline: 53 | self.stop() 54 | -------------------------------------------------------------------------------- /testcases/twitter_test.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import os 22 | import datetime 23 | 24 | from . import common 25 | try: 26 | # This will get environment variables set. 27 | import credentials 28 | except: 29 | pass 30 | 31 | from pyalgotrade import dispatcher 32 | from pyalgotrade.twitter import feed as twitterfeed 33 | 34 | 35 | class TwitterFeedTestCase(common.TestCase): 36 | def testTwitterFeed(self): 37 | events = { 38 | "on_tweet": False, 39 | "start": datetime.datetime.now() 40 | } 41 | disp = dispatcher.Dispatcher() 42 | 43 | def on_tweet(data): 44 | events["on_tweet"] = True 45 | disp.stop() 46 | 47 | def on_idle(): 48 | # Stop after 5 minutes. 49 | if (datetime.datetime.now() - events["start"]).seconds > 60*5: 50 | disp.stop() 51 | 52 | # Create a twitter feed to track BitCoin related events. 53 | consumer_key = os.getenv("TWITTER_CONSUMER_KEY") 54 | consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET") 55 | access_token = os.getenv("TWITTER_ACCESS_TOKEN") 56 | access_token_secret = os.getenv("TWITTER_ACCESS_TOKEN_SECRET") 57 | track = ["bitcoin", "btc"] 58 | follow = [] 59 | languages = ["en"] 60 | twitterFeed = twitterfeed.TwitterFeed( 61 | consumer_key, 62 | consumer_secret, 63 | access_token, 64 | access_token_secret, 65 | track, 66 | follow, 67 | languages 68 | ) 69 | 70 | disp.addSubject(twitterFeed) 71 | twitterFeed.subscribe(on_tweet) 72 | disp.getIdleEvent().subscribe(on_idle) 73 | disp.run() 74 | 75 | # Check that we received both events. 76 | self.assertTrue(events["on_tweet"]) 77 | -------------------------------------------------------------------------------- /testcases/websocket_server.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from wsgiref import simple_server 3 | 4 | from ws4py.server import wsgirefserver 5 | from ws4py.server import wsgiutils 6 | 7 | 8 | class WebSocketServerThread(threading.Thread): 9 | def __init__(self, host, port, webSocketServerClass): 10 | super(WebSocketServerThread, self).__init__() 11 | self.__host = host 12 | self.__port = port 13 | self.__webSocketServerClass = webSocketServerClass 14 | self.__server = None 15 | 16 | def run(self): 17 | def handler_cls_builder(*args, **kwargs): 18 | return self.__webSocketServerClass(*args, **kwargs) 19 | 20 | self.__server = simple_server.make_server( 21 | self.__host, 22 | self.__port, 23 | server_class=wsgirefserver.WSGIServer, 24 | handler_class=wsgirefserver.WebSocketWSGIRequestHandler, 25 | app=wsgiutils.WebSocketWSGIApplication(handler_cls=handler_cls_builder) 26 | ) 27 | self.__server.initialize_websockets_manager() 28 | self.__server.serve_forever() 29 | 30 | def stop(self): 31 | self.__server.shutdown() 32 | 33 | 34 | # webSocketServerClass should be a subclass of ws4py.websocket.WebSocket 35 | def run_websocket_server_thread(host, port, webSocketServerClass): 36 | wss_thread = WebSocketServerThread(host, port, webSocketServerClass) 37 | wss_thread.start() 38 | return wss_thread 39 | -------------------------------------------------------------------------------- /tools/symbols/get_merval_symbols.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | sys.path.append("../..") 23 | 24 | import pyalgotrade.logger 25 | import lxml.html 26 | import symbolsxml 27 | 28 | logger = pyalgotrade.logger.getLogger("get_merval_symbols") 29 | 30 | 31 | def find_company(htmlTree, ticker): 32 | ret = None 33 | anchor = htmlTree.xpath("//td[1]/a[@href='/q/pr?s=%s']/text()" % (ticker)) 34 | if anchor: 35 | ret = anchor[0] 36 | return ret 37 | 38 | 39 | def find_sector(htmlTree): 40 | ret = None 41 | anchor = htmlTree.xpath("//th[1][text() = 'Sector:']/../td/a[1]/text()") 42 | if anchor: 43 | ret = anchor[0] 44 | return ret 45 | 46 | 47 | def find_industry(htmlTree): 48 | ret = None 49 | anchor = htmlTree.xpath("//th[1][text() = 'Industry:']/../td/a[1]/text()") 50 | if anchor: 51 | ret = anchor[0] 52 | return ret 53 | 54 | 55 | def process_symbol(writer, symbol): 56 | try: 57 | logger.info("Getting info for %s" % (symbol)) 58 | url = "http://finance.yahoo.com/q/in?s=%s+Industry" % (symbol) 59 | htmlTree = lxml.html.parse(url) 60 | 61 | company = find_company(htmlTree, symbol) 62 | if not company: 63 | raise Exception("Company name not found") 64 | 65 | sector = find_sector(htmlTree) 66 | if not sector: 67 | sector = "" 68 | logger.warning("Sector not found") 69 | 70 | industry = find_industry(htmlTree) 71 | if not industry: 72 | industry = "" 73 | logger.warning("Industry not found") 74 | 75 | writer.addStock(symbol, company, sector, industry) 76 | except Exception as e: 77 | logger.error(str(e)) 78 | 79 | 80 | def main(): 81 | try: 82 | writer = symbolsxml.Writer() 83 | for symbol in open("merval-symbols.txt", "r"): 84 | symbol = symbol.strip() 85 | process_symbol(writer, symbol) 86 | 87 | # Index 88 | writer.addIndex("^MERV", "Merval") 89 | 90 | logger.info("Writing merval.xml") 91 | writer.write("merval.xml") 92 | except Exception as e: 93 | logger.error(str(e)) 94 | 95 | if __name__ == "__main__": 96 | main() 97 | -------------------------------------------------------------------------------- /tools/symbols/get_nasdaq_symbols.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | sys.path.append("../..") 23 | 24 | import pyalgotrade.logger 25 | import tempfile 26 | import urllib.request, urllib.error, urllib.parse 27 | import csv 28 | import symbolsxml 29 | 30 | 31 | logger = pyalgotrade.logger.getLogger("get_nasdaq_symbols") 32 | 33 | 34 | def main(): 35 | try: 36 | logger.info("Getting NASDAQ symbols from http://www.nasdaq.com/") 37 | url = "http://www.nasdaq.com/screening/companies-by-name.aspx?exchange=NASDAQ&render=download" 38 | buff = urllib.request.urlopen(url).read() 39 | 40 | tmpFile = tempfile.NamedTemporaryFile() 41 | tmpFile.write(buff) 42 | tmpFile.flush() 43 | with open(tmpFile.name, 'rb') as csvfile: 44 | symbolsXML = symbolsxml.Writer() 45 | for row in csv.DictReader(csvfile): 46 | symbolsXML.addStock(row["Symbol"], row["Name"], row["Sector"], row["industry"]) 47 | 48 | logger.info("Writing nasdaq.xml") 49 | symbolsXML.write("nasdaq.xml") 50 | except Exception as e: 51 | logger.error(str(e)) 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /tools/symbols/get_nyse_symbols.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | sys.path.append("../..") 23 | 24 | import pyalgotrade.logger 25 | import tempfile 26 | import urllib.request, urllib.error, urllib.parse 27 | import csv 28 | import symbolsxml 29 | 30 | 31 | logger = pyalgotrade.logger.getLogger("get_nyse_symbols") 32 | 33 | 34 | def main(): 35 | try: 36 | logger.info("Getting NYSE symbols from http://www.nasdaq.com/") 37 | url = "http://www.nasdaq.com/screening/companies-by-name.aspx?exchange=NYSE&render=download" 38 | buff = urllib.request.urlopen(url).read() 39 | 40 | tmpFile = tempfile.NamedTemporaryFile() 41 | tmpFile.write(buff) 42 | tmpFile.flush() 43 | with open(tmpFile.name, 'rb') as csvfile: 44 | symbolsXML = symbolsxml.Writer() 45 | for row in csv.DictReader(csvfile): 46 | symbolsXML.addStock(row["Symbol"], row["Name"], row["Sector"], row["industry"]) 47 | 48 | logger.info("Writing nyse.xml") 49 | symbolsXML.write("nyse.xml") 50 | except Exception as e: 51 | logger.error(str(e)) 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /tools/symbols/get_sp500_symbols.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import sys 22 | sys.path.append("../..") 23 | 24 | import pyalgotrade.logger 25 | import lxml.html 26 | import symbolsxml 27 | 28 | # pyalgotrade.logger.file_log = "get_sp500_symbols.log" 29 | logger = pyalgotrade.logger.getLogger("get_sp500_symbols") 30 | 31 | TICKER_SYMBOL_COL = 0 32 | COMPANY_COL = 1 33 | GICS_COL = 3 34 | GICS_SUB_INDUSTRY_COL = 4 35 | 36 | 37 | def get_html(): 38 | logger.info("Getting S&P 500 Component Stocks from Wikipedia") 39 | ret = lxml.html.parse("http://en.wikipedia.org/wiki/List_of_S%26P_500_companies") 40 | return ret 41 | 42 | 43 | def find_table(htmlTree): 44 | logger.info("Finding the right table") 45 | ret = None 46 | tables = htmlTree.xpath("//table[@class='wikitable sortable']") 47 | for table in tables: 48 | headers = table.xpath("tr[1]/th") 49 | if len(headers) > 5: 50 | if headers[TICKER_SYMBOL_COL].xpath("a[1]")[0].text != "Ticker symbol": 51 | continue 52 | if headers[COMPANY_COL].text != "Company": 53 | continue 54 | if headers[GICS_COL].xpath("a[1]")[0].text != "GICS": 55 | continue 56 | if headers[GICS_SUB_INDUSTRY_COL].text != "GICS Sub Industry": 57 | continue 58 | ret = table 59 | break 60 | return ret 61 | 62 | 63 | def parse_results(table): 64 | ret = symbolsxml.Writer() 65 | logger.info("Parsing table") 66 | rows = table.xpath("tr") 67 | for row in rows[1:]: 68 | cols = row.xpath("td") 69 | tickerSymbol = cols[TICKER_SYMBOL_COL].xpath("a[1]")[0].text 70 | company = cols[COMPANY_COL].xpath("a[1]")[0].text 71 | gics = cols[GICS_COL].text 72 | gicsSubIndustry = cols[GICS_SUB_INDUSTRY_COL].text 73 | if gicsSubIndustry is None: 74 | gicsSubIndustry = "" 75 | 76 | ret.addStock(tickerSymbol, company, gics, gicsSubIndustry) 77 | return ret 78 | 79 | 80 | def main(): 81 | try: 82 | htmlTree = get_html() 83 | table = find_table(htmlTree) 84 | if table is None: 85 | raise Exception("S&P 500 Component Stocks table not found") 86 | symbolsXML = parse_results(table) 87 | 88 | logger.info("Writing sp500.xml") 89 | symbolsXML.write("sp500.xml") 90 | except Exception as e: 91 | logger.error(str(e)) 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /tools/symbols/merval-symbols.txt: -------------------------------------------------------------------------------- 1 | AGRO.BA 2 | ALUA.BA 3 | APBR.BA 4 | APSA.BA 5 | AUSO.BA 6 | BHIP.BA 7 | BMA.BA 8 | BOLT.BA 9 | BPAT.BA 10 | BRIO.BA 11 | BRIO6.BA 12 | CADO.BA 13 | CAPU.BA 14 | CAPX.BA 15 | CARC.BA 16 | CECO2.BA 17 | CELU.BA 18 | CEPU2.BA 19 | CGPA2.BA 20 | COME.BA 21 | CRES.BA 22 | CTIO.BA 23 | DGCU2.BA 24 | DYCA.BA 25 | EDN.BA 26 | ERAR.BA 27 | ESTR.BA 28 | FERR.BA 29 | FIPL.BA 30 | FRAN.BA 31 | GALI.BA 32 | GARO.BA 33 | GBAN.BA 34 | GCLA.BA 35 | GGAL.BA 36 | GRIM.BA 37 | INDU.BA 38 | INTR.BA 39 | IRSA.BA 40 | JMIN.BA 41 | LEDE.BA 42 | LONG.BA 43 | METR.BA 44 | MIRG.BA 45 | MOLI.BA 46 | MORI.BA 47 | OEST.BA 48 | PAMP.BA 49 | PATA.BA 50 | PATY.BA 51 | PESA.BA 52 | POLL.BA 53 | PSUR.BA 54 | REP.BA 55 | RIGO.BA 56 | ROSE.BA 57 | SAMI.BA 58 | SEMI.BA 59 | STD.BA 60 | TECO2.BA 61 | TEF.BA 62 | TGNO4.BA 63 | TGSU2.BA 64 | TRAN.BA 65 | TS.BA 66 | YPFD.BA 67 | -------------------------------------------------------------------------------- /tools/symbols/symbolsxml.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | from lxml import etree 22 | 23 | 24 | class Stock: 25 | def __init__(self, symbolElement): 26 | self.__symbolElement = symbolElement 27 | 28 | def getTicker(self): 29 | return self.__symbolElement.attrib["ticker"] 30 | 31 | def getCompany(self): 32 | return self.__symbolElement.attrib["name"] 33 | 34 | def getSector(self): 35 | return self.__symbolElement.attrib["sector"] 36 | 37 | def getIndustry(self): 38 | return self.__symbolElement.attrib["industry"] 39 | 40 | 41 | class Index: 42 | def __init__(self, symbolElement): 43 | self.__symbolElement = symbolElement 44 | 45 | def getTicker(self): 46 | return self.__symbolElement.attrib["ticker"] 47 | 48 | def getName(self): 49 | return self.__symbolElement.attrib["name"] 50 | 51 | 52 | class Writer: 53 | def __init__(self): 54 | self.__root = etree.Element('symbols') 55 | 56 | def addStock(self, ticker, company, sector, industry): 57 | symbolElement = etree.Element("symbol") 58 | symbolElement.set("ticker", ticker) 59 | symbolElement.set("name", company) 60 | symbolElement.set("type", "stock") 61 | if sector is None: 62 | sector = "" 63 | symbolElement.set("sector", sector) 64 | if industry is None: 65 | industry = "" 66 | symbolElement.set("industry", industry) 67 | self.__root.append(symbolElement) 68 | 69 | def addIndex(self, ticker, name): 70 | symbolElement = etree.Element("symbol") 71 | symbolElement.set("ticker", ticker) 72 | symbolElement.set("name", name) 73 | symbolElement.set("type", "index") 74 | self.__root.append(symbolElement) 75 | 76 | def write(self, fileName): 77 | etree.ElementTree(self.__root).write(fileName, xml_declaration=True, encoding="utf-8", pretty_print=True) 78 | 79 | 80 | def parse(fileName, stockCallback, indexCallback): 81 | root = etree.parse(open(fileName, "r")) 82 | if stockCallback is not None: 83 | for symbol in root.xpath("//symbols/symbol[@type='stock']"): 84 | stockCallback(Stock(symbol)) 85 | if indexCallback is not None: 86 | for symbol in root.xpath("//symbols/symbol[@type='index']"): 87 | indexCallback(Index(symbol)) 88 | -------------------------------------------------------------------------------- /tools/yahoodbfeed/download_data.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade 2 | # 3 | # Copyright 2011-2015 Gabriel Martin Becedillas Ruiz 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | .. moduleauthor:: Gabriel Martin Becedillas Ruiz 19 | """ 20 | 21 | import os 22 | import sys 23 | 24 | sys.path.append(os.path.join("..", "symbols")) 25 | sys.path.append(os.path.join("..", "..")) # For pyalgotrade 26 | 27 | import pyalgotrade.logger 28 | 29 | pyalgotrade.logger.file_log = "download_data.log" 30 | logger = pyalgotrade.logger.getLogger("download_data") 31 | 32 | from pyalgotrade.tools import yahoofinance 33 | import symbolsxml 34 | 35 | 36 | storage = "data" 37 | 38 | 39 | def get_csv_filename(symbol, year): 40 | return os.path.join(storage, "%s-%d-yahoofinance.csv" % (symbol, year)) 41 | 42 | 43 | def download_files_for_symbol(symbol, fromYear, toYear): 44 | if not os.path.exists(storage): 45 | logger.info("Creating %s directory" % (storage)) 46 | os.mkdir(storage) 47 | 48 | status = "" 49 | for year in range(fromYear, toYear+1): 50 | fileName = get_csv_filename(symbol, year) 51 | if not os.path.exists(fileName): 52 | logger.info("Downloading %s %d to %s" % (symbol, year, fileName)) 53 | try: 54 | yahoofinance.download_daily_bars(symbol, year, fileName) 55 | status += "1" 56 | except Exception as e: 57 | logger.error(str(e)) 58 | status += "0" 59 | else: 60 | status += "1" 61 | 62 | if status.find("1") == -1: 63 | logger.fatal("No data found for %s" % (symbol)) 64 | elif status.lstrip("0").find("0") != -1: 65 | logger.fatal("Some bars are missing for %s" % (symbol)) 66 | 67 | 68 | def main(): 69 | fromYear = 2000 70 | toYear = 2013 71 | 72 | try: 73 | symbolsFile = os.path.join("..", "symbols", "merval.xml") 74 | callback = lambda stock: download_files_for_symbol(stock.getTicker(), fromYear, toYear) 75 | symbolsxml.parse(symbolsFile, callback, callback) 76 | except Exception as e: 77 | logger.error(str(e)) 78 | 79 | main() 80 | -------------------------------------------------------------------------------- /travis/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gbecedillas/pyalgotrade:0.18 2 | MAINTAINER Gabriel Martin Becedillas Ruiz 3 | 4 | RUN pip install nose 5 | RUN pip install nose-cov 6 | 7 | RUN mkdir /tmp/pyalgotrade 8 | 9 | # Files needed to execute testcases. 10 | COPY travis/run_tests.sh /tmp/pyalgotrade/ 11 | COPY coverage.cfg /tmp/pyalgotrade/ 12 | COPY setup.cfg /tmp/pyalgotrade/ 13 | COPY testcases /tmp/pyalgotrade/testcases 14 | COPY samples /tmp/pyalgotrade/samples 15 | 16 | # We need to upgrade the installed version with the one checked out from GIT. 17 | COPY setup.py /tmp/pyalgotrade/ 18 | COPY pyalgotrade /tmp/pyalgotrade/pyalgotrade 19 | RUN pip install -U /tmp/pyalgotrade 20 | -------------------------------------------------------------------------------- /travis/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH 4 | # This is needed to avoid "Coverage.py warning: No data was collected" from cov plugin. 5 | export PYTHONPATH=. 6 | 7 | nosetests --with-cov --cov=pyalgotrade --cov-config=coverage.cfg 8 | --------------------------------------------------------------------------------