├── .gitignore ├── Makefile ├── README.md ├── pyalgotrade_mootdx ├── __init__.py ├── barfeed.py ├── broker.py ├── common.py ├── livefeed.py └── tools.py ├── sample.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *pyc 2 | MANIFEST 3 | /docs/_build 4 | /site 5 | .tox 6 | .idea 7 | .cache 8 | .coverage 9 | .ipynb_checkpoints 10 | .ropeproject 11 | /__pycache__ 12 | /dist 13 | /build 14 | *.egg-info 15 | ======= 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | # Distribution / packaging 25 | .Python 26 | build/ 27 | develop-eggs/ 28 | dist/ 29 | downloads/ 30 | eggs/ 31 | .eggs/ 32 | lib/ 33 | lib64/ 34 | parts/ 35 | sdist/ 36 | var/ 37 | wheels/ 38 | *.egg-info/ 39 | .installed.cfg 40 | *.egg 41 | MANIFEST 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | .hypothesis/ 63 | .pytest_cache/ 64 | 65 | # Translations 66 | *.mo 67 | *.pot 68 | 69 | # Django stuff: 70 | *.log 71 | local_settings.py 72 | db.sqlite3 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | target/ 86 | 87 | # Jupyter Notebook 88 | .ipynb_checkpoints 89 | 90 | # pyenv 91 | .python-version 92 | 93 | # celery beat schedule file 94 | celerybeat-schedule 95 | 96 | # SageMath parsed files 97 | *.sage.py 98 | 99 | # Environments 100 | .env 101 | .venv 102 | env/ 103 | venv/ 104 | ENV/ 105 | env.bak/ 106 | venv.bak/ 107 | 108 | # Spyder project settings 109 | .spyderproject 110 | .spyproject 111 | 112 | # Rope project settings 113 | .ropeproject 114 | 115 | # mkdocs documentation 116 | /site 117 | 118 | # mypy 119 | .mypy_cache/ 120 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean clean-test clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | define BROWSER_PYSCRIPT 4 | import os, webbrowser, sys 5 | try: 6 | from urllib import pathname2url 7 | except: 8 | from urllib.request import pathname2url 9 | 10 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 11 | endef 12 | export BROWSER_PYSCRIPT 13 | 14 | define PRINT_HELP_PYSCRIPT 15 | import re, sys 16 | 17 | for line in sys.stdin: 18 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 19 | if match: 20 | target, help = match.groups() 21 | print("%-20s %s" % (target, help)) 22 | endef 23 | export PRINT_HELP_PYSCRIPT 24 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 25 | 26 | help: 27 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 28 | 29 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 30 | 31 | 32 | clean-build: ## remove build artifacts 33 | rm -fr build/ 34 | rm -fr dist/ 35 | rm -fr .eggs/ 36 | find . -name '*.egg-info' -exec rm -fr {} + 37 | find . -name '*.egg' -exec rm -f {} + 38 | 39 | clean-pyc: ## remove Python file artifacts 40 | find . -name '*.pyc' -exec rm -f {} + 41 | find . -name '*.pyo' -exec rm -f {} + 42 | find . -name '*~' -exec rm -f {} + 43 | find . -name '__pycache__' -exec rm -fr {} + 44 | 45 | clean-test: ## remove test and coverage artifacts 46 | rm -fr .tox/ 47 | rm -f .coverage 48 | rm -fr htmlcov/ 49 | 50 | lint: ## check style with flake8 51 | flake8 mooquant tests 52 | 53 | test: ## run tests quickly with the default Python 54 | py.test 55 | 56 | 57 | test-all: ## run tests on every Python version with tox 58 | tox 59 | 60 | coverage: ## check code coverage quickly with the default Python 61 | coverage run --source mooquant -m pytest 62 | coverage report -m 63 | coverage html 64 | $(BROWSER) htmlcov/index.html 65 | 66 | docs: ## generate Sphinx HTML documentation, including API docs 67 | rm -f docs/mooquant.rst 68 | rm -f docs/modules.rst 69 | sphinx-apidoc -o docs/ mooquant 70 | $(MAKE) -C docs clean 71 | $(MAKE) -C docs html 72 | $(BROWSER) docs/_build/html/index.html 73 | 74 | servedocs: docs ## compile the docs watching for changes 75 | watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . 76 | 77 | release: clean ## package and upload a release 78 | python setup.py sdist upload 79 | python setup.py bdist_wheel upload 80 | 81 | dist: clean ## builds source and wheel package 82 | python setup.py sdist 83 | python setup.py bdist_wheel 84 | ls -l dist 85 | 86 | install: clean ## install the package to the active Python's site-packages 87 | python setup.py install 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyAlgoTrade mootdx module 2 | ========================= 3 | 此项目是 PyAlgoTrade [mootdx](https://github.com/bopo/mootdx) (基于 pytdx 的二次封装版本) 的一个数据源 4 | 5 | 一个简单的用法: 6 | 7 | ``` 8 | from pyalgotrade import plotter, strategy 9 | from pyalgotrade.bar import Frequency 10 | from pyalgotrade.barfeed.csvfeed import GenericBarFeed 11 | from pyalgotrade.stratanalyzer import sharpe 12 | from pyalgotrade.technical import ma 13 | 14 | from pyalgotrade_mootdx import tools 15 | 16 | 17 | class Strategy(strategy.BacktestingStrategy): 18 | def __init__(self, feed, instrument): 19 | super(Strategy, self).__init__(feed) 20 | 21 | self.__position = None 22 | self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 150) 23 | self.__instrument = instrument 24 | self.getBroker() 25 | 26 | def onEnterOk(self, position): 27 | execInfo = position.getEntryOrder().getExecutionInfo() 28 | self.info("买入 %.2f" % (execInfo.getPrice())) 29 | 30 | def onEnterCanceled(self, position): 31 | self.__position = None 32 | 33 | def onExitOk(self, position): 34 | execInfo = position.getExitOrder().getExecutionInfo() 35 | self.info("卖出 %.2f" % (execInfo.getPrice())) 36 | self.__position = None 37 | 38 | def onExitCanceled(self, position): 39 | # If the exit was canceled, re-submit it. 40 | self.__position.exitMarket() 41 | 42 | def getSMA(self): 43 | return self.__sma 44 | 45 | def onBars(self, bars): 46 | # 每一个数据都会抵达这里,就像becktest中的next 47 | # Wait for enough bars to be available to calculate a SMA. 48 | if self.__sma[-1] is None: 49 | return 50 | 51 | # bar.getTyoicalPrice = (bar.getHigh() + bar.getLow() + bar.getClose())/ 3.0 52 | bar = bars[self.__instrument] 53 | 54 | # If a position was not opened, check if we should enter a long position. 55 | if self.__position is None: 56 | if bar.getPrice() > self.__sma[-1]: 57 | # 开多头. 58 | self.__position = self.enterLong(self.__instrument, 100, True) 59 | 60 | # 平掉多头头寸. 61 | elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): 62 | self.__position.exitMarket() 63 | 64 | 65 | def main(): 66 | instruments = ["600036"] 67 | feeds = tools.build_feed(instruments, 2017, 2018, "histdata") 68 | 69 | # 3.实例化策略 70 | strat = Strategy(feeds, instruments[0]) 71 | 72 | # 4.设置指标和绘图 73 | ratio = sharpe.SharpeRatio() 74 | strat.attachAnalyzer(ratio) 75 | plter = plotter.StrategyPlotter(strat) 76 | 77 | # 5.运行策略 78 | strat.run() 79 | strat.info("最终收益: %.2f" % strat.getResult()) 80 | 81 | # 6.输出夏普率、绘图 82 | strat.info("夏普比率: " + str(ratio.getSharpeRatio(0))) 83 | # plter.plot() 84 | 85 | 86 | if __name__ == '__main__': 87 | main() 88 | ``` 89 | 90 | ``` 91 | 2018-04-03 00:00:00 strategy [INFO] 卖出 28.86 92 | 2018-04-11 00:00:00 strategy [INFO] 买入 30.34 93 | 2018-04-17 00:00:00 strategy [INFO] 卖出 28.25 94 | 2018-05-02 00:00:00 strategy [INFO] 买入 29.98 95 | 2018-05-04 00:00:00 strategy [INFO] 卖出 29.28 96 | 2018-05-09 00:00:00 strategy [INFO] 买入 29.70 97 | 2018-05-24 00:00:00 strategy [INFO] 卖出 29.60 98 | 2018-09-25 00:00:00 strategy [INFO] 买入 29.83 99 | 2018-10-12 00:00:00 strategy [INFO] 卖出 28.70 100 | 2018-10-15 00:00:00 strategy [INFO] 买入 29.16 101 | 2018-10-19 00:00:00 strategy [INFO] 卖出 27.75 102 | 2018-10-22 00:00:00 strategy [INFO] 买入 29.53 103 | 2018-10-30 00:00:00 strategy [INFO] 卖出 28.12 104 | 2018-11-01 00:00:00 strategy [INFO] 买入 29.45 105 | 2018-11-15 00:00:00 strategy [INFO] 卖出 28.24 106 | 2018-11-19 00:00:00 strategy [INFO] 买入 28.70 107 | 2018-11-23 00:00:00 strategy [INFO] 卖出 28.29 108 | 2018-12-03 00:00:00 strategy [INFO] 买入 29.49 109 | 2018-12-11 00:00:00 strategy [INFO] 卖出 28.35 110 | 2018-12-12 00:00:00 strategy [INFO] 买入 29.00 111 | 2018-12-18 00:00:00 strategy [INFO] 卖出 28.05 112 | 2018-12-21 13:48:01,740 strategy [INFO] 最终收益: 999254.00 113 | 2018-12-21 13:48:01,740 strategy [INFO] 夏普比率: -0.6957113853538597 114 | ``` 115 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/__init__.py: -------------------------------------------------------------------------------- 1 | # PyAlgoTrade MooTDX module 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 | # Modified from PyAlgoTrade Bitstamp module 18 | 19 | """ 20 | .. moduleauthor:: Mikko Gozalo 21 | """ 22 | __version__ = '0.1.1' 23 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/barfeed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # PyAlgoTrade mootdx module 3 | # 4 | # Copyright 2017 bopo.wang 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """ 19 | .. moduleauthor:: bopo.wang 20 | """ 21 | import datetime 22 | 23 | from pyalgotrade import bar 24 | from pyalgotrade.barfeed import common, csvfeed 25 | from pyalgotrade.utils import dt 26 | 27 | 28 | ###################################################################### 29 | # MooTDX CSV parser 30 | # Each bar must be on its own line and fields must be separated by comma (,). 31 | # 32 | # Bars Format: 33 | # Date,Open,High,Low,Close,Volume 34 | # 35 | # The csv Date column must have the following format: D-B-YY 36 | def parse_date(date): 37 | date = date.split("-") 38 | ret = datetime.datetime(int(date[0]), int(date[1]), int(date[2])) 39 | 40 | return ret 41 | 42 | 43 | class RowParser(csvfeed.RowParser): 44 | def __init__(self, dailyBarTime, frequency, timezone=None, sanitize=False): 45 | self.__dailyBarTime = dailyBarTime 46 | self.__frequency = frequency 47 | self.__timezone = timezone 48 | self.__sanitize = sanitize 49 | 50 | def __parseDate(self, dateString): 51 | ret = parse_date(dateString) 52 | # Time on MooTDX CSV files is empty. If told to set one, do it. 53 | if self.__dailyBarTime is not None: 54 | ret = datetime.datetime.combine(ret, self.__dailyBarTime) 55 | 56 | # Localize the datetime if a timezone was given. 57 | if self.__timezone: 58 | ret = dt.localize(ret, self.__timezone) 59 | 60 | return ret 61 | 62 | def getFieldNames(self): 63 | # It is expected for the first row to have the field names. 64 | return None 65 | 66 | def getDelimiter(self): 67 | return "," 68 | 69 | def parseBar(self, csvRowDict): 70 | dateTime = self.__parseDate(csvRowDict["Date"]) 71 | close = float(csvRowDict["Close"]) 72 | open_ = float(csvRowDict["Open"]) 73 | high = float(csvRowDict["High"]) 74 | low = float(csvRowDict["Low"]) 75 | volume = float(csvRowDict["Volume"]) 76 | adjClose = None 77 | 78 | if self.__sanitize: 79 | open_, high, low, close = common.sanitize_ohlc(open_, high, low, close) 80 | 81 | return bar.BasicBar(dateTime, open_, high, low, close, volume, adjClose, self.__frequency) 82 | 83 | 84 | class Feed(csvfeed.BarFeed): 85 | """A :class:`pyalgotrade.barfeed.csvfeed.BarFeed` that loads bars from CSV files downloaded from MooTDX. 86 | 87 | :param frequency: The frequency of the bars. Only **pyalgotrade.bar.Frequency.DAY** is currently supported. 88 | :param timezone: The default timezone to use to localize bars. Check :mod:`pyalgotrade.marketsession`. 89 | :type timezone: A pytz timezone. 90 | :param maxLen: The maximum number of values that the :class:`pyalgotrade.dataseries.bards.BarDataSeries` will hold. 91 | Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the 92 | opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. 93 | :type maxLen: int. 94 | 95 | .. note:: 96 | MooTDX csv files lack timezone information. 97 | When working with multiple instruments: 98 | 99 | * If all the instruments loaded are in the same timezone, then the timezone parameter may not be specified. 100 | * If any of the instruments loaded are in different timezones, then the timezone parameter must be set. 101 | """ 102 | 103 | # frequency == 频率 104 | def __init__(self, frequency=bar.Frequency.DAY, timezone=None, maxLen=None): 105 | if frequency not in [bar.Frequency.DAY]: 106 | raise Exception("Invalid frequency.") 107 | 108 | super().__init__(frequency, maxLen) 109 | 110 | self.__timezone = timezone 111 | self.__sanitizeBars = False 112 | 113 | # 标准化函数 114 | def sanitizeBars(self, sanitize): 115 | self.__sanitizeBars = sanitize 116 | 117 | def barsHaveAdjClose(self): 118 | return False 119 | 120 | def addBarsFromCSV(self, instrument, path, timezone=None): 121 | """Loads bars for a given instrument from a CSV formatted file. 122 | The instrument gets registered in the bar feed. 123 | 124 | :param instrument: Instrument identifier. 125 | :type instrument: string. 126 | :param path: The path to the CSV file. 127 | :type path: string. 128 | :param timezone: The timezone to use to localize bars. Check :mod:`pyalgotrade.marketsession`. 129 | :type timezone: A pytz timezone. 130 | """ 131 | 132 | if timezone is None: 133 | timezone = self.__timezone 134 | 135 | rowParser = RowParser(self.getDailyBarTime(), self.getFrequency(), timezone, self.__sanitizeBars) 136 | super().addBarsFromCSV(instrument, path, rowParser) 137 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/broker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # PyAlgoTrade mootdx module 3 | # 4 | # Copyright 2017 bopo.wang 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """ 19 | .. moduleauthor:: bopo.wang 20 | """ 21 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # PyAlgoTrade mootdx module 3 | # 4 | # Copyright 2017 bopo.wang 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """ 19 | .. moduleauthor:: bopo.wang 20 | """ 21 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/livefeed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # PyAlgoTrade mootdx module 3 | # 4 | # Copyright 2017 bopo.wang 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """ 19 | .. moduleauthor:: bopo.wang 20 | """ 21 | -------------------------------------------------------------------------------- /pyalgotrade_mootdx/tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # PyAlgoTrade mootdx module 3 | # 4 | # Copyright 2017 bopo.wang 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """ 19 | .. moduleauthor:: bopo.wang 20 | """ 21 | 22 | import datetime 23 | import os 24 | from pathlib import Path 25 | 26 | import pyalgotrade.logger 27 | from mootdx import quotes 28 | from pyalgotrade import bar 29 | from pyalgotrade.utils import csvutils 30 | 31 | from pyalgotrade_mootdx import barfeed 32 | 33 | 34 | def download_bars(instrument, begin, end): 35 | quote = quotes.Quotes.factory() 36 | return quote.k(instrument, begin, end) 37 | 38 | def download_daily_bars(instrument, year, csvFile): 39 | """Download daily bars from Google Finance for a given year. 40 | 41 | :param instrument: Instrument identifier. 42 | :type instrument: string. 43 | :param year: The year. 44 | :type year: int. 45 | :param csvFile: The path to the CSV file to write. 46 | :type csvFile: string. 47 | """ 48 | 49 | begin = datetime.date(year, 1, 1).strftime('%Y-%m-%d') 50 | end = datetime.date(year, 12, 31).strftime('%Y-%m-%d') 51 | bars = download_bars(instrument, begin, end) 52 | 53 | if len(bars) > 0: 54 | bars = bars.drop(['code'], axis=1) 55 | bars.columns = ['Open', 'Close', 'High', 'Low', 'Volume', 'Amount', 'Date'] 56 | bars.to_csv(csvFile, encoding='utf-8', index=False) 57 | 58 | 59 | def build_feed(instruments, fromYear, toYear, storage, frequency=bar.Frequency.DAY, timezone=None, skipErrors=False): 60 | """Build and load a :class:`pyalgotrade.barfeed.mootdxfeed.Feed` using CSV files downloaded from Google Finance. 61 | CSV files are downloaded if they haven't been downloaded before. 62 | 63 | :param instruments: Instrument identifiers. 64 | :type instruments: list. 65 | :param fromYear: The first year. 66 | :type fromYear: int. 67 | :param toYear: The last year. 68 | :type toYear: int. 69 | :param storage: The path were the files will be loaded from, or downloaded to. 70 | :type storage: string. 71 | :param frequency: The frequency of the bars. Only **pyalgotrade.bar.Frequency.DAY** is currently supported. 72 | :param timezone: The default timezone to use to localize bars. Check :mod:`pyalgotrade.marketsession`. 73 | :type timezone: A pytz timezone. 74 | :param skipErrors: True to keep on loading/downloading files in case of errors. 75 | :type skipErrors: boolean. 76 | :rtype: :class:`pyalgotrade.barfeed.mootdxfeed.Feed`. 77 | """ 78 | 79 | logger = pyalgotrade.logger.getLogger("mootdx") 80 | ret = barfeed.Feed(frequency, timezone) 81 | 82 | if not os.path.exists(storage): 83 | logger.info("Creating {dirname} directory".format(dirname=storage)) 84 | os.mkdir(storage) 85 | 86 | for year in range(fromYear, toYear + 1): 87 | for instrument in instruments: 88 | filePath = Path(storage) 89 | fileName = filePath / "{instrument}-{year}-mootdx.csv".format( 90 | instrument=instrument, year=year) 91 | 92 | if not os.path.exists(fileName): 93 | logger.info( 94 | "Downloading {instrument} {year} to {filename}".format( 95 | instrument=instrument, year=year, filename=fileName)) 96 | try: 97 | if frequency == bar.Frequency.DAY: 98 | download_daily_bars(instrument, year, fileName) 99 | else: 100 | raise Exception("Invalid frequency") 101 | except Exception as e: 102 | if skipErrors: 103 | logger.error(str(e)) 104 | continue 105 | else: 106 | raise e 107 | 108 | ret.addBarsFromCSV(instrument, fileName) 109 | 110 | return ret 111 | -------------------------------------------------------------------------------- /sample.py: -------------------------------------------------------------------------------- 1 | from pyalgotrade import plotter, strategy 2 | from pyalgotrade.bar import Frequency 3 | from pyalgotrade.barfeed.csvfeed import GenericBarFeed 4 | from pyalgotrade.stratanalyzer import sharpe 5 | from pyalgotrade.technical import ma 6 | 7 | from pyalgotrade_mootdx import tools 8 | 9 | 10 | class Strategy(strategy.BacktestingStrategy): 11 | def __init__(self, feed, instrument): 12 | super(Strategy, self).__init__(feed) 13 | 14 | self.__position = None 15 | self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 150) 16 | self.__instrument = instrument 17 | self.getBroker() 18 | 19 | def onEnterOk(self, position): 20 | execInfo = position.getEntryOrder().getExecutionInfo() 21 | self.info("买入 %.2f" % (execInfo.getPrice())) 22 | 23 | def onEnterCanceled(self, position): 24 | self.__position = None 25 | 26 | def onExitOk(self, position): 27 | execInfo = position.getExitOrder().getExecutionInfo() 28 | self.info("卖出 %.2f" % (execInfo.getPrice())) 29 | self.__position = None 30 | 31 | def onExitCanceled(self, position): 32 | # If the exit was canceled, re-submit it. 33 | self.__position.exitMarket() 34 | 35 | def getSMA(self): 36 | return self.__sma 37 | 38 | def onBars(self, bars): 39 | # 每一个数据都会抵达这里,就像becktest中的next 40 | # Wait for enough bars to be available to calculate a SMA. 41 | if self.__sma[-1] is None: 42 | return 43 | 44 | # bar.getTyoicalPrice = (bar.getHigh() + bar.getLow() + bar.getClose())/ 3.0 45 | bar = bars[self.__instrument] 46 | 47 | # If a position was not opened, check if we should enter a long position. 48 | if self.__position is None: 49 | if bar.getPrice() > self.__sma[-1]: 50 | # 开多头. 51 | self.__position = self.enterLong(self.__instrument, 100, True) 52 | 53 | # 平掉多头头寸. 54 | elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): 55 | self.__position.exitMarket() 56 | 57 | 58 | def main(): 59 | instruments = ["600036"] 60 | feeds = tools.build_feed(instruments, 2017, 2018, "histdata") 61 | 62 | # 3.实例化策略 63 | strat = Strategy(feeds, instruments[0]) 64 | 65 | # 4.设置指标和绘图 66 | ratio = sharpe.SharpeRatio() 67 | strat.attachAnalyzer(ratio) 68 | plter = plotter.StrategyPlotter(strat) 69 | 70 | # 5.运行策略 71 | strat.run() 72 | strat.info("最终收益: %.2f" % strat.getResult()) 73 | 74 | # 6.输出夏普率、绘图 75 | strat.info("夏普比率: " + str(ratio.getSharpeRatio(0))) 76 | # plter.plot() 77 | 78 | 79 | if __name__ == '__main__': 80 | main() 81 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:mootdx/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | 20 | [aliases] 21 | test = pytest 22 | 23 | [egg_info] 24 | tag_build = 25 | tag_date = 0 26 | 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """The setup script.""" 5 | 6 | try: 7 | from setuptools import setup, find_packages 8 | except ImportError: 9 | from distutils.core import setup, find_packages 10 | 11 | from pyalgotrade_mootdx import __version__ 12 | 13 | with open('README.md') as readme_file: 14 | readme = readme_file.read() 15 | 16 | requirements = ['pyalgotrade', 'mootdx'] 17 | test_requirements = ['pyalgotrade', 'mootdx', 'pytest',] 18 | setup_requirements = ['pyalgotrade', 'mootdx'] 19 | 20 | setup( 21 | name='pyalgotrade_mootdx', 22 | version=__version__, 23 | description="pyalgotrade mootdx module", 24 | long_description=readme + '\n\n', 25 | author="bopo.wang", 26 | author_email='ibopo@126.com', 27 | url='https://github.com/bopo/pyalgotrade_mootdx', 28 | packages=find_packages(include=['pyalgotrade_mootdx','pyalgotrade_mootdx.*']), 29 | include_package_data=True, 30 | install_requires=requirements, 31 | license="MIT license", 32 | zip_safe=False, 33 | keywords='pyalgotrade_mootdx', 34 | classifiers=[ 35 | 'Development Status :: 2 - Pre-Alpha', 36 | 'Intended Audience :: Developers', 37 | 'License :: OSI Approved :: MIT License', 38 | 'Natural Language :: English', 39 | 'Programming Language :: Python :: 3', 40 | 'Programming Language :: Python :: 3.3', 41 | 'Programming Language :: Python :: 3.4', 42 | 'Programming Language :: Python :: 3.5', 43 | 'Programming Language :: Python :: 3.6', 44 | 'Programming Language :: Python :: 3.7', 45 | ], 46 | test_suite='tests', 47 | tests_require=test_requirements, 48 | setup_requires=setup_requirements, 49 | ) 50 | --------------------------------------------------------------------------------