├── 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 | [](https://travis-ci.org/gbeced/pyalgotrade)
5 | [](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 |
--------------------------------------------------------------------------------