PyAlgoTrade Documentation



Similar documents
电 信 与 互 联 网 法 律 热 点 问 题

广 东 培 正 学 院 2016 年 本 科 插 班 生 专 业 课 考 试 大 纲 基 础 英 语 课 程 考 试 大 纲

ifuzhen.com, ifortzone.com a product of Edgework Ventures Financial Management Software & Financial Wiki

A Brief Study on Cancellation of Late-Marriage and Late-Childbirth Leaves

中 国 石 化 上 海 石 油 化 工 研 究 院 欢 迎 国 内 外 高 层 次 人 才 加 入

Master Program in Project Management Yunnan University of Finance & Economics, 2016

第 二 届 中 国 中 东 欧 国 家 投 资 贸 易 博 览 会 总 体 方 案

Synergy yet to be Seen, Maintain Accumulate

China Foreign Affairs University(CFAU)

Terms and Conditions of Purchase- Bosch China [ 采 购 通 则 博 世 ( 中 国 )]

Slow Earnings Growth but Attractive Discount to A-Shares

4Q15 Results Beat Expectations, Upgrade to Accumulate

Can someone speak some Korean to tell him where the aperture button is?

中 国 ( 南 京 ) 软 件 谷 简 介

~1: 15 /;' J~~~~c...:;.--:.. I. ~ffi ~I J) ':~

美 国 律 师 协 会 知 识 产 权 法 部 和 国 际 法 律 部 关 于 中 华 人 民 共 和 国 专 利 法 修 改 草 案 ( 征 求 意 见 稿 ) 的 联 合 意 见 书

THE PEI CHUN STORY Primary 4 Level Briefing for Parents 小 四 家 长 讲 解 会. 公 立 培 群 学 校 Pei Chun Public School

PCS Educational Foundation Inc.


Romeo and Juliet 罗 密 欧 与 茱 丽 叶

FRESH PRODUCE FORUM CHINA, 1 JUNE 2016, CHENGDU, CHINA 新 鲜 果 蔬 行 业 中 国 高 峰 论 坛,2016 年 6 月 1 日, 成 都 EXHIBITOR REGISTRATION FORM 参 展 商 申 请 表 格

Bird still caged? China s courts under reform. Workshop, June 3-4, 2016, Vienna, Austria. (University of Vienna, Department of East Asian Studies)

The Maryknoll Advantage With Pacific Aviation Museum Pearl Harbor

中 国 国 家 留 学 基 金 管 理 委 员 会 CHINA SCHOLARSHIP COUNCIL 驻 外

ANSYS HFSS 3D Layout 侯 明 刚

大 学 英 语 六 级 随 堂 听 简 答 篇

2013 年 6 月 英 语 六 级 真 题 及 答 案 ( 文 都 版 )

2013 首 届 国 际 营 养 与 健 康 大 会

The transmission calculation by empirical numerical model and Monte Carlo simulation in high energy proton radiography of thick objects *

Profession or passion?: Teaching Chinese in London Chinese complementary schools

MySQL 備 份, 高 可 用 及 高 扩 展 解 决 方 案

2015 年 12 月 大 学 英 语 六 级 考 试 真 题 优 化 卷 ( 第 二 套 ) 答 题 卡 1

Advancements in Slurry Gasification

2016 年 6 月 份 大 学 英 语 四 级 考 试 听 力 样 题

新 东 方 大 学 英 语 四 级 考 试

国 际 储 备 与 外 币 流 动 性 数 据 模 板 Template on International Reserves and Foreign Currency Liquidity

MARGINAL COST OF INDUSTRIAL PRODUCTION

MySQL High Availability. MMM & MHA in DP 卢钧轶

Kingdom Tower: A New Icon for Saudi Arabia

酷 刑 和 其 他 残 忍 不 人 道 或 有 辱 人 格 的 待 遇 或 处 罚 问 题 特 别 报 告 员 曼 弗 雷 德 诺 瓦 克 的 报 告

Whole Genome Based Plant Breeding: Platforms and Technologies

Mobile TV Target Audience Measurement (Report 1)

Red Pandas in China Population and Habitat Viability Assessment Workshop June 2012, Beijing, China FINAL REPORT

A web-based vocabulary profiler for Chinese language teaching and research

1.1 Exploded view of indoor unit for KF-26GW/GX1b,KFR-26GW/GX1b,

English. Copyright DEWALT

Word Alignment of English-Chinese Bilingual Corpus Based on Chunks

Key Words: United Nations Peacebuilding, protection of human security, liberal

PROFILES OF SPEAKERS, DISCUSSANTS AND MODERATORS * 发 言 人 评 论 员 与 主 持 人 简 历 (in alphabetical order)

Procedures to file a request to the JPO for Patent Prosecution Highway Pilot Program between the JPO and the HPO

LC Paper No. PWSC269/15-16(01)

Translation of Repetitions in Text: A Systemic Functional Approach

2014 年 12 月 英 语 六 级 考 试 真 题 试 卷 ( 第 1 套 )

China M&A goes global

路 面 供 电 电 动 汽 车 感 应 电 能 传 输 系 统 综 述. A Review on Inductive Power Transfer Systems for Roadway Powered Electric Vehicles

Wuhan University of Technology MBA Thesis

Complexe Teaching Methods

南 京 农 业 大 学 农 业 资 源 与 生 态 环 境 研 究 所 土 壤 碳 氮 循 环 与 气 候 变 化 研 究 团 队 2009 年 报 2010 年 1 月 1 日, 南 京

Wi-Drive User Guide. (for use with Amazon s Kindle Fire) Document No. 480WID4KF-001.A01 Kingston Wi-Drive Page 1 of 15

SHAU KEI WAN GOVERNMENT SECONDARY SCHOOL

Transcription methods for consistency, volume and efficiency

Case Study of a New Generation Call Center

南 宁 二 中 2013 届 高 三 英 语 2 月 月 考 试 题

WORKING PAPER. Analysis of China s overseas investment policies. Huang Wenbin Andreas Wilkes

Ex. Either we must get in line early to buy the tickets, or only scalpers INDEPENDENT CLAUSE 1 INDEPENDENT tickets will be available.

Microsoft SQL Server PDW 新世代 MPP 資料倉儲解決方案

Chemistry I -- Final Exam

China s Agricultural Investment in Latin America. Margaret Myers Inter- American Dialogue November 21, 2013

JAPAN PATENT OFFICE AS DESIGNATED (OR ELECTED) OFFICE CONTENTS

Application Guidelines for International Graduate Programs in Engineering

Kingston MobileLite Wireless. (ßeta Release) Document No. 480WD+MLW.ß01 Kingston MobileLite Wireless (ßeta) Page 1 of 12

Intel RAID Premium Feature Key AXXRPFKSSD, AXXRPFKDE, and AXXRPFKSNSH Installation Guide

Mini-review Diagnosis and Treatment of Chinese Medicine and Western Medicine on Refractory Chronic Pelvic Pain 难 治 性 盆 腔 痛 的 中 西 医 诊 治

Microsoft Big Data 解決方案與案例分享

Machine Translation for Academic Purposes

ASSESSING ACCESS TO INFORMATION IN CHINA 评 析 中 国 的 政 府 信 息 公 开 制 度 JENS F. REINERTSEN 专 业 : 国 际 法 学 研 究 方 向 : 国 际 经 济 法 教 师 : 莫 世 健 教 授

歐 洲 難 民 潮 對 經 濟 的 影 響 The Economic Implications of Europe s Refugee Influx

BETTER TREATMENT OPTIONS NOW FOR OVARIAN CANCER ...HELPING READERS TO ACHIEVE GOOD HEALTH. AN NCCS QUARTERLY PUBLICATION April June 2014

EW-7438RPn Mini 安 裝 指 南 / v1.0

Grant Request Form. Request Form. (For continued projects)

北 京 地 区 成 人 本 科 学 士 学 位 英 语 统 一 考 试

The HKICPA Accounting and Business Management Case Competition Secondary School Group (Level 1)

JPShiKen.COM 全 日 本 最 新 の IT 認 定 試 験 問 題 集

轎 車 機 場 接 送 及 往 返 澳 門 與 香 港 機 場 接 送 服 務 禮 遇 ( 推 廣 ) 之 條 款 及 細 則 :

Tradition and Ideology

Customers' Trust and Purchase Intention towards. Taobao's Alipay (China online marketplace)

Market Access To Taiwan. By Jane Peng TÜV Rheinland Taiwan Ltd.

Sentiment Analysis in Chinese. Master's Thesis. Presented to. The Faculty of the Graduate School of Arts and Sciences. Brandeis University

2008 年 12 月 大 学 英 语 四 级 考 试 真 题

TUTORING SERVICE WITH CDAC 2016

Graduate School of Engineering. Master s Program, 2016 (October entrance)

Request for Taxpayer Identification Number and Certification 納 税 者 番 号 および 宣 誓 の 依 頼 書

Model 123A24. Rocket motor ICP pressure sensor, 1000 psi, 5 mv/psi, Helium bleed, Installation and Operating Manual

促 進 市 場 競 爭 加 強 保 障 消 費 者

weekly Our mission Our history Our footprint Our award-winning content 2015 Media Kit asian northwest

Transcription:

PyAlgoTrade Documentation 发 布 017 Gabriel Martín Becedillas Ruiz 2016 年 02 月 26 日

Contents 1 简 介 3 2 简 明 教 程 5 21 交 易 9 22 优 化 11 23 绘 图 15 3 代 码 的 文 档 说 明 19 31 bar 品 种 的 价 格 19 32 dataseries 数 据 序 列 基 类 19 33 feed 数 据 源 基 类 19 331 CSV 支 持 19 332 CSV 支 持 的 例 子 19 34 barfeed Bar 数 据 来 源 20 341 CSV 20 342 Yahoo! Finance 20 343 Google Finance 20 344 Quandl 20 345 Ninja Trader 20 35 technical 技 术 指 标 20 351 例 子 20 352 移 动 平 均 线 21 353 动 量 指 标 21 354 其 他 指 标 21 36 broker 订 单 管 理 类 21 361 基 类 和 模 块 21 362 回 测 模 块 和 类 21 37 strategy 策 略 基 类 21 371 Strategy 22 372 Position 22 38 stratanalyzer Strategy analyzers 22 381 Returns 22 382 Sharpe Ratio 22 383 DrawDown 22 384 Trades 23 385 Example 23 39 plotter Strategy plotter 26 310 optimizer Parallel optimizers 26 i

311 marketsession Market sessions 26 4 工 具 29 41 Yahoo! Finance 29 42 Quandl 29 43 BarFeed resampling 29 5 Event profiler 31 51 Example 31 6 Xignite support 35 61 xignite Xignite reference 35 611 Feeds 35 62 Xignite Example 35 7 Bitcoin 37 71 Bitstamp support 37 711 bitstamp Bitstamp reference 37 712 Bitstamp Example 37 72 Bitcoin Charts support 41 721 bitcoincharts Bitcoin Charts reference 41 722 Bitcoin Charts example 41 8 Twitter support 45 81 twitter Twitter feed reference 45 811 Feed 45 82 Twitter Example 45 9 TA-Lib integration 49 10 Computational Investing Part I 51 101 Homework 1 51 102 Homework 3 and 4 52 11 策 略 例 子 57 111 动 量 交 易 57 1111 VWAP 动 量 交 易 57 1112 SMA 交 叉 59 1113 均 线 交 叉 择 时 61 112 均 值 回 归 66 1121 Ernie Chan s 黄 金 vs 黄 金 矿 工 66 1122 布 林 带 69 1123 RSI2 71 113 其 他 75 1131 Quandl 合 集 75 12 Indices and tables 79 Python 模 块 索 引 81 ii

目 录 : Contents 1

2 Contents

CHAPTER 1 简 介 PyAlgoTrade 作 为 事 件 驱 动 型 的 算 法 交 易 工 具, 其 支 持 : 从 CSV 文 件 中 获 取 历 史 数 据 并 进 行 回 测 通 过 使 用 Xignite 和 Bitstamp 的 实 时 数 据 进 行 模 拟 交 易 在 Bitstamp 上 进 行 实 战 交 易 它 可 以 很 方 便 的 在 多 台 计 算 机 之 间 优 化 策 略 PyAlgoTrade 基 于 Python27 开 发 并 依 托 于 以 下 python 库 : NumPy and SciPy (http://numpyscipyorg/): 科 学 计 算 库 pytz (http://pytzsourceforgenet/) matplotlib (http://matplotlibsourceforgenet/) : 绘 图 库 ws4py (https://githubcom/lawouach/websocket-for-python) : 用 于 得 到 Bitstamp 支 持 tornado (http://wwwtornadoweborg/en/stable/) 用 于 得 到 Bitstamp 支 持 tweepy (https://githubcom/tweepy/tweepy) 用 于 得 到 推 特 支 持 因 此, 你 需 要 安 装 上 述 扩 展 库 才 能 使 用 本 扩 展 库 你 可 以 通 过 pip 来 安 装 PyAlgoTrade: pip install pyalgotrade 译 者 注 : 017 版 本 已 经 迁 移 到 Python3x 上, 原 版 只 能 在 py27 环 境 下 运 行, 请 谨 慎 升 级 down:pyalgotrade016_for_py3 2016-2-23: 目 前, 已 有 国 内 版 本 地 址 :https://githubcom/yam-cn/pyalgotrade-cn 基 于 py27, 支 持 A 股 交 易 以 及 封 装 了 tushare 的 A 股 即 时 行 情 项 目 QQ 群 :300349971 水 平 有 限, 如 有 疏 漏, 敬 请 谅 解! 欢 迎 一 起 翻 译 &debug! 3

4 Chapter 1 简 介

CHAPTER 2 简 明 教 程 本 教 程 的 目 的 是 让 你 能 够 快 速 入 门 PyAlgoTrade 如 之 前 简 介 中 所 述,PyAlgoTrade 的 目 标 是 帮 助 你 测 试 股 票 交 易 策 略 一 般 而 言, 如 果 你 有 一 个 交 易 策 略 的 构 思, 并 且 想 在 历 史 数 据 中 看 看 它 的 表 现 的 话, PyAlgoTrade 应 该 能 够 给 你 一 些 帮 助 感 谢 要 放 在 最 开 始 感 谢 豪 尔 赫 帮 助 审 查 初 步 设 计 和 文 档 本 教 程 是 在 UNIX 下 开 发 的, 但 是 把 它 调 整 为 windows 环 境 应 该 是 比 较 简 单 的 因 为 我 就 是 在 windows 下 翻 译 的 PyAlgoTrade 有 6 大 主 要 组 件 : Strategies 策 略 Feeds 数 据 源 Brokers 经 纪 商 DataSeries 数 据 序 列 Technicals 指 标 计 算 Optimizer 优 化 Strategies 这 是 你 定 义 的 实 现 交 易 逻 辑 的 类 : 何 时 买 何 时 卖, 等 等 Feeds These are data providing abstractions 例 如, 你 可 以 使 用 CSV 数 据 源 从 一 个 格 式 化 后 的 csv( 以 逗 号 分 割 ) 文 件 中 加 载 数 据 推 送 给 策 略 数 据 源 不 仅 限 于 bars 例 如, 可 以 利 用 Twitter 数 据 源 将 Twitter 的 事 件 信 息 转 化 为 交 易 决 策 ( 译 者 注 : 事 件 驱 动 ) Brokers 经 纪 商 模 块 负 责 执 行 订 单 DataSeries DataSeries 是 用 于 管 理 时 间 序 列 的 抽 象 类 Technicals 这 是 你 用 来 对 DataSeries 进 行 计 算 的 一 组 过 滤 ( 指 标 ) 器 例 如 简 单 移 动 平 均 线 (SMA), 相 对 强 弱 指 标 (RSI) 等 这 些 过 滤 ( 指 标 ) 器 被 建 模 为 DataSeries 的 装 饰 器 Optimizer 这 是 能 让 你 在 不 同 电 脑 之 间 或 多 进 程 或 二 者 结 合 以 加 快 回 测 效 率 的 一 组 类 说 到 这 里, 我 们 测 试 策 略 时 需 要 一 些 数 据, 让 我 们 使 用 以 下 命 令 获 取 甲 骨 文 (Oracle)2000 年 的 数 据 : python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('orcl', 2000, pyalgotradetoolsyahoofinance 从 雅 虎 金 融 (Yahoo! 2000csv 的 格 式 和 内 容 如 下 : Finance) 上 打 包 下 载 格 式 化 后 的 CSV 数 据 文 件 orcl- 5

Date,Open,High,Low,Close,Volume,Adj Close 2000-12-29,3087,3131,2869,2906,31655500,2835 2000-12-28,3056,3112,3037,3106,25055600,3030 2000-12-27,3037,3106,2937,3069,26441700,2994 2000-01-04,11550,11862,10500,10769,116850000,2626 2000-01-03,12462,12519,11162,11812,98122000,2881 让 我 们 从 一 个 简 单 的 策 略 开 始, 即 在 运 行 过 程 中 只 打 印 收 盘 价 : from pyalgotrade import strategy from pyalgotradebarfeed import yahoofeed class MyStrategy(strategyBacktestingStrategy): def init (self, feed, instrument): strategybacktestingstrategy init (self, feed) self instrument = instrument def onbars(self, bars): bar = bars[self instrument] selfinfo(bargetclose()) # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed's bars mystrategy = MyStrategy(feed, "orcl") mystrategyrun() 这 段 代 码 主 要 做 了 3 件 事 情 :(???) 1 声 明 了 一 个 新 的 策 略, 这 个 策 略 中 只 定 义 了 一 个 方 法 onbars, 这 个 方 法 会 在 推 送 的 每 一 个 bar 执 行 (???) 2 从 一 个 CSV 文 件 载 入 数 据 源 3 在 数 据 源 推 送 的 每 根 bar 上 执 行 策 略 如 果 你 运 行 了 这 个 脚 本, 你 应 该 在 命 令 中 看 到 收 盘 价 : 2000-01-03 00:00:00 strategy [INFO] 11812 2000-01-04 00:00:00 strategy [INFO] 10769 2000-01-05 00:00:00 strategy [INFO] 1020 2000-12-27 00:00:00 strategy [INFO] 3069 2000-12-28 00:00:00 strategy [INFO] 3106 2000-12-29 00:00:00 strategy [INFO] 2906 下 一 步 我 们 实 现 一 个 打 印 SMA 价 格 的 策 略, 用 来 演 示 technicals 是 如 何 被 使 用 的 : from pyalgotrade import strategy from pyalgotradebarfeed import yahoofeed from pyalgotradetechnical import ma 6 Chapter 2 简 明 教 程

class MyStrategy(strategyBacktestingStrategy): def init (self, feed, instrument): strategybacktestingstrategy init (self, feed) # We want a 15 period SMA over the closing prices self sma = masma(feed[instrument]getclosedataseries(), 15) self instrument = instrument def onbars(self, bars): bar = bars[self instrument] selfinfo("%s %s" % (bargetclose(), self sma[-1])) # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed's bars mystrategy = MyStrategy(feed, "orcl") mystrategyrun() 这 跟 之 前 的 例 子 很 相 似, 除 了 : 1 我 们 在 收 盘 价 基 础 上 初 始 化 了 一 个 SMA 过 滤 器 ( 指 标 ) 2 我 们 将 SMA 的 值 随 着 收 盘 价 一 起 打 印 出 来 运 行 该 脚 本 后 将 会 看 到 逐 行 对 应 的 收 盘 价 和 SMA 值, 但 是 实 际 情 况 是 前 14 行 的 SMA 值 是 None 这 是 因 为 我 们 至 少 需 要 15 个 值 才 能 把 SMA 计 算 出 来 : 2000-01-03 00:00:00 strategy [INFO] 11812 None 2000-01-04 00:00:00 strategy [INFO] 10769 None 2000-01-05 00:00:00 strategy [INFO] 1020 None 2000-01-06 00:00:00 strategy [INFO] 960 None 2000-01-07 00:00:00 strategy [INFO] 10337 None 2000-01-10 00:00:00 strategy [INFO] 11575 None 2000-01-11 00:00:00 strategy [INFO] 11237 None 2000-01-12 00:00:00 strategy [INFO] 10562 None 2000-01-13 00:00:00 strategy [INFO] 10506 None 2000-01-14 00:00:00 strategy [INFO] 10681 None 2000-01-18 00:00:00 strategy [INFO] 11125 None 2000-01-19 00:00:00 strategy [INFO] 5713 None 2000-01-20 00:00:00 strategy [INFO] 5925 None 2000-01-21 00:00:00 strategy [INFO] 5969 None 2000-01-24 00:00:00 strategy [INFO] 5419 942866666667 2000-01-25 00:00:00 strategy [INFO] 5644 901746666667 2000-12-27 00:00:00 strategy [INFO] 3069 299866666667 2000-12-28 00:00:00 strategy [INFO] 3106 300446666667 2000-12-29 00:00:00 strategy [INFO] 2906 300946666667 若 给 定 的 时 间 不 足 以 计 算, 则 所 有 的 technicals 将 返 回 None 一 件 重 要 的 事 情 是 technicals 可 以 进 行 组 合, 因 为 他 们 作 为 DataSeries 是 模 块 化 的 比 如, 计 算 收 盘 价 的 RSI 再 计 算 RSI 的 SMA 就 很 简 单 : from pyalgotrade import strategy from pyalgotradebarfeed import yahoofeed from pyalgotradetechnical import ma 7

from pyalgotradetechnical import rsi class MyStrategy(strategyBacktestingStrategy): def init (self, feed, instrument): strategybacktestingstrategy init (self, feed) self rsi = rsirsi(feed[instrument]getclosedataseries(), 14) self sma = masma(self rsi, 15) self instrument = instrument def onbars(self, bars): bar = bars[self instrument] selfinfo("%s %s %s" % (bargetclose(), self rsi[-1], self sma[-1])) # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed's bars mystrategy = MyStrategy(feed, "orcl") mystrategyrun() 运 行 该 脚 本, 你 应 该 看 到 在 屏 幕 上 有 一 堆 值 : 前 14 个 RSI 值 是 None 因 为 我 们 至 少 需 要 15 个 值 来 计 算 RSI 前 28 个 SMA 值 是 None 前 14 个 RSI 值 是 None,SMA 过 滤 器 ( 指 标 ) 接 收 的 数 据 从 第 15 个 开 始 才 不 是 None 我 们 计 算 SMA(15) 至 少 需 要 15 个 非 None 值 2000-01-03 00:00:00 strategy [INFO] 11812 None None 2000-01-04 00:00:00 strategy [INFO] 10769 None None 2000-01-05 00:00:00 strategy [INFO] 1020 None None 2000-01-06 00:00:00 strategy [INFO] 960 None None 2000-01-07 00:00:00 strategy [INFO] 10337 None None 2000-01-10 00:00:00 strategy [INFO] 11575 None None 2000-01-11 00:00:00 strategy [INFO] 11237 None None 2000-01-12 00:00:00 strategy [INFO] 10562 None None 2000-01-13 00:00:00 strategy [INFO] 10506 None None 2000-01-14 00:00:00 strategy [INFO] 10681 None None 2000-01-18 00:00:00 strategy [INFO] 11125 None None 2000-01-19 00:00:00 strategy [INFO] 5713 None None 2000-01-20 00:00:00 strategy [INFO] 5925 None None 2000-01-21 00:00:00 strategy [INFO] 5969 None None 2000-01-24 00:00:00 strategy [INFO] 5419 235673530141 None 2000-01-25 00:00:00 strategy [INFO] 5644 250687519877 None 2000-01-26 00:00:00 strategy [INFO] 5506 247476577095 None 2000-01-27 00:00:00 strategy [INFO] 5181 239690136517 None 2000-01-28 00:00:00 strategy [INFO] 4738 229108539956 None 2000-01-31 00:00:00 strategy [INFO] 4995 24980004823 None 2000-02-01 00:00:00 strategy [INFO] 540 282484181864 None 2000-02-02 00:00:00 strategy [INFO] 5431 28505177315 None 2000-02-03 00:00:00 strategy [INFO] 5669 305596770599 None 2000-02-04 00:00:00 strategy [INFO] 5781 315564353751 None 2000-02-07 00:00:00 strategy [INFO] 5994 335111056589 None 2000-02-08 00:00:00 strategy [INFO] 5956 333282358994 None 2000-02-09 00:00:00 strategy [INFO] 5994 337177605915 None 2000-02-10 00:00:00 strategy [INFO] 6231 362205441255 None 2000-02-11 00:00:00 strategy [INFO] 5969 346623493641 290368892505 2000-02-14 00:00:00 strategy [INFO] 6219 374284445543 299609620198 8 Chapter 2 简 明 教 程

2000-12-27 00:00:00 strategy [INFO] 3069 513196802735 498506368511 2000-12-28 00:00:00 strategy [INFO] 3106 521646203455 49997518354 2000-12-29 00:00:00 strategy [INFO] 2906 473776678335 500790646925 21 交 易 现 在 让 我 们 用 一 个 简 单 的 策 略 模 拟 实 际 的 交 易, 思 路 很 简 单 : 如 果 复 权 价 格 在 SMA(15) 之 上 时, 我 们 买 入 一 个 多 单 ( 我 们 发 出 一 张 买 订 单 ) 如 果 我 们 持 有 一 张 多 单, 当 复 权 价 跌 破 SMA(15) 时, 我 们 平 多 单 ( 我 们 发 出 一 张 卖 单 ) from pyalgotrade import strategy from pyalgotradebarfeed import yahoofeed from pyalgotradetechnical import ma class MyStrategy(strategyBacktestingStrategy): def init (self, feed, instrument, smaperiod): strategybacktestingstrategy init (self, feed, 1000) self position = None self instrument = instrument # We'll use adjusted close values instead of regular close values selfsetuseadjustedvalues(true) self sma = masma(feed[instrument]getpricedataseries(), smaperiod) def onenterok(self, position): execinfo = positiongetentryorder()getexecutioninfo() selfinfo("buy at $%2f" % (execinfogetprice())) def onentercanceled(self, position): self position = None def onexitok(self, position): execinfo = positiongetexitorder()getexecutioninfo() selfinfo("sell at $%2f" % (execinfogetprice())) self position = None def onexitcanceled(self, position): # If the exit was canceled, re-submit it self positionexitmarket() def onbars(self, bars): # Wait for enough bars to be available to calculate a SMA if self sma[-1] is None: return bar = bars[self instrument] # If a position was not opened, check if we should enter a long position if self position is None: if bargetprice() > self sma[-1]: # Enter a buy market order for 10 shares The order is good till canceled self position = selfenterlong(self instrument, 10, True) # Check if we have to exit the position 21 交 易 9

elif bargetprice() < self sma[-1] and not self positionexitactive(): self positionexitmarket() def run_strategy(smaperiod): # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed mystrategy = MyStrategy(feed, "orcl", smaperiod) mystrategyrun() print(("final portfolio value: $%2f" % mystrategygetbroker()getequity())) run_strategy(15) 运 行 该 脚 本, 你 将 看 到 跟 下 面 类 似 的 东 西 : 2000-01-26 00:00:00 strategy [INFO] BUY at $2726 2000-01-28 00:00:00 strategy [INFO] SELL at $2474 2000-02-03 00:00:00 strategy [INFO] BUY at $2660 2000-02-22 00:00:00 strategy [INFO] SELL at $2840 2000-02-23 00:00:00 strategy [INFO] BUY at $2891 2000-03-31 00:00:00 strategy [INFO] SELL at $3851 2000-04-07 00:00:00 strategy [INFO] BUY at $4019 2000-04-12 00:00:00 strategy [INFO] SELL at $3744 2000-04-19 00:00:00 strategy [INFO] BUY at $3776 2000-04-20 00:00:00 strategy [INFO] SELL at $3545 2000-04-28 00:00:00 strategy [INFO] BUY at $3770 2000-05-05 00:00:00 strategy [INFO] SELL at $3554 2000-05-08 00:00:00 strategy [INFO] BUY at $3617 2000-05-09 00:00:00 strategy [INFO] SELL at $3539 2000-05-16 00:00:00 strategy [INFO] BUY at $3728 2000-05-19 00:00:00 strategy [INFO] SELL at $3458 2000-05-31 00:00:00 strategy [INFO] BUY at $3518 2000-06-23 00:00:00 strategy [INFO] SELL at $3881 2000-06-27 00:00:00 strategy [INFO] BUY at $3956 2000-06-28 00:00:00 strategy [INFO] SELL at $3942 2000-06-29 00:00:00 strategy [INFO] BUY at $3941 2000-06-30 00:00:00 strategy [INFO] SELL at $3860 2000-07-03 00:00:00 strategy [INFO] BUY at $3896 2000-07-05 00:00:00 strategy [INFO] SELL at $3689 2000-07-21 00:00:00 strategy [INFO] BUY at $3719 2000-07-24 00:00:00 strategy [INFO] SELL at $3704 2000-07-26 00:00:00 strategy [INFO] BUY at $3593 2000-07-28 00:00:00 strategy [INFO] SELL at $3608 2000-08-01 00:00:00 strategy [INFO] BUY at $3611 2000-08-02 00:00:00 strategy [INFO] SELL at $3506 2000-08-04 00:00:00 strategy [INFO] BUY at $3761 2000-09-11 00:00:00 strategy [INFO] SELL at $4134 2000-09-29 00:00:00 strategy [INFO] BUY at $3907 2000-10-02 00:00:00 strategy [INFO] SELL at $3830 2000-10-20 00:00:00 strategy [INFO] BUY at $3471 2000-10-31 00:00:00 strategy [INFO] SELL at $3134 2000-11-20 00:00:00 strategy [INFO] BUY at $2335 2000-11-21 00:00:00 strategy [INFO] SELL at $2383 2000-12-01 00:00:00 strategy [INFO] BUY at $2533 2000-12-21 00:00:00 strategy [INFO] SELL at $2672 10 Chapter 2 简 明 教 程

2000-12-22 00:00:00 strategy [INFO] BUY at $2917 Final portfolio value: $97944 如 果 我 们 用 30 替 代 15 来 作 为 SMA 的 参 数 会 怎 样? 绩 效 会 变 好 还 是 变 坏? 我 们 很 确 定 可 以 通 过 以 下 方 法 来 验 证 : for i in range(10, 30): run_strategy(i) 我 们 会 发 现, 使 用 SMA(20) 后 能 得 到 更 好 的 回 报 : Final portfolio value: $107538 我 们 只 是 尝 试 了 一 组 有 限 的 参 数 组, 看 起 来 没 有 什 么 问 题 如 果 我 们 需 要 测 试 一 个 有 多 个 参 数 的 策 略 怎 么 办? 使 用 串 行 计 算 就 不 会 让 策 略 变 得 更 复 杂 22 优 化 了 解 优 化 组 件 后, 优 化 会 变 得 非 常 简 单 : 有 一 个 服 务 负 责 : 提 供 策 略 运 行 所 需 的 bars 提 供 策 略 运 行 所 需 的 参 数 记 录 来 自 每 个 工 作 过 程 的 交 易 记 录 结 果 并 且 有 多 个 工 作 过 程 负 责 : 在 由 服 务 提 供 的 参 数 K 线 基 础 上 运 行 策 略 我 们 使 用 一 个 名 为 RSI2 的 策 略 来 说 明 上 述 架 构 (http://stockchartscom/school/dokuphp?id=chart_school:trading_strategies:rsi2), 它 需 要 以 下 参 数 : entrysma: 一 个 SMA 趋 势 识 别 的 长 度 参 数 范 围 [150 ~ 250] exitsma: 一 个 较 短 的 SMA 出 场 点 长 度 参 数 范 围 [5~10] rsiperiod: 多 空 入 场 点 参 数 范 围 [2 ~ 10] oversoldthreshold:rsi 多 单 平 仓 点 参 数 范 围 [5 ~ 25] overboughtthreshold:rsi 空 单 平 仓 点 参 数 范 围 [75 ~ 95] 如 果 我 没 算 错 的 话 你 的 意 思 是 数 学 是 体 育 老 师 教 的?? 这 将 有 4409559 个 参 数 组 合 我 这 里 测 试 其 中 一 个 参 数 组 的 时 间 大 约 016 秒, 估 计 需 要 85 天 才 能 要 运 行 完 整 个 参 数 组 合 然 后 找 到 最 优 参 数 时 间 太 长 了 如 果 我 能 有 10 台 8 核 的 机 器 同 时 进 行 优 化, 那 么 时 间 将 缩 短 到 25 小 时 长 话 短 说, 我 们 需 要 并 行 计 算 我 们 先 下 载 道 琼 斯 工 业 指 数 3 年 的 日 线 数 据 : python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('dia', 2009, python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('dia', 2010, python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('dia', 2011, 保 存 以 下 代 码 到 rsi2py: 22 优 化 11

from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import rsi from pyalgotradetechnical import cross class RSI2(strategyBacktestingStrategy): def init (self, feed, instrument, entrysma, exitsma, rsiperiod, overboughtthreshold, oversoldt strategybacktestingstrategy init (self, feed) self instrument = instrument # We'll use adjusted close values, if available, instead of regular close values if feedbarshaveadjclose(): selfsetuseadjustedvalues(true) self priceds = feed[instrument]getpricedataseries() self entrysma = masma(self priceds, entrysma) self exitsma = masma(self priceds, exitsma) self rsi = rsirsi(self priceds, rsiperiod) self overboughtthreshold = overboughtthreshold self oversoldthreshold = oversoldthreshold self longpos = None self shortpos = None def getentrysma(self): return self entrysma def getexitsma(self): return self exitsma def getrsi(self): return self rsi def onentercanceled(self, position): if self longpos == position: self longpos = None elif self shortpos == position: self shortpos = None else: assert(false) def onexitok(self, position): if self longpos == position: self longpos = None elif self shortpos == position: self shortpos = None else: assert(false) def onexitcanceled(self, position): # If the exit was canceled, re-submit it positionexitmarket() def onbars(self, bars): # Wait for enough bars to be available to calculate SMA and RSI if self exitsma[-1] is None or self entrysma[-1] is None or self rsi[-1] is None: return bar = bars[self instrument] if self longpos is not None: 12 Chapter 2 简 明 教 程

if selfexitlongsignal(): self longposexitmarket() elif self shortpos is not None: if selfexitshortsignal(): self shortposexitmarket() else: if selfenterlongsignal(bar): shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) self longpos = selfenterlong(self instrument, shares, True) elif selfentershortsignal(bar): shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) self shortpos = selfentershort(self instrument, shares, True) def enterlongsignal(self, bar): return bargetprice() > self entrysma[-1] and self rsi[-1] <= self oversoldthreshold def exitlongsignal(self): return crosscross_above(self priceds, self exitsma) and not self longposexitactive() def entershortsignal(self, bar): return bargetprice() < self entrysma[-1] and self rsi[-1] >= self overboughtthreshold def exitshortsignal(self): return crosscross_below(self priceds, self exitsma) and not self shortposexitactive() 这 是 服 务 器 脚 本 : import itertools from pyalgotradebarfeed import yahoofeed from pyalgotradeoptimizer import server def parameters_generator(): instrument = ["dia"] entrysma = list(range(150, 251)) exitsma = list(range(5, 16)) rsiperiod = list(range(2, 11)) overboughtthreshold = list(range(75, 96)) oversoldthreshold = list(range(5, 26)) return itertoolsproduct(instrument, entrysma, exitsma, rsiperiod, overboughtthreshold, oversoldt # The if name == ' main ' part is necessary if running on Windows if name == ' main ': # Load the feed from the CSV files feed = yahoofeedfeed() feedaddbarsfromcsv("dia", "dia-2009csv") feedaddbarsfromcsv("dia", "dia-2010csv") feedaddbarsfromcsv("dia", "dia-2011csv") # Run the server serverserve(feed, parameters_generator(), "localhost", 5000) 服 务 器 脚 本 做 了 3 件 事 : 1 声 明 一 个 生 成 器 函 数, 这 个 函 数 可 以 根 据 参 数 范 围 为 策 略 生 成 参 数 组 合 2 从 我 们 下 载 的 CSV 文 件 载 入 数 据 源 3 运 行 服 务 端 并 在 端 口 5000 上 等 待 连 接 22 优 化 13

This is the worker script that uses the pyalgotradeoptimizerworker module to run the strategy in parallel with the data supplied by the server: from pyalgotradeoptimizer import worker import rsi2 # The if name == ' main ' part is necessary if running on Windows if name == ' main ': workerrun(rsi2rsi2, "localhost", 5000, workername="localworker") 当 您 运 行 服 务 器 和 客 户 端 时, 您 会 看 到 在 服 务 器 控 制 台 上 看 到 的 类 似 下 面 的 内 容 : 2014-05-03 15:04:01,083 server [INFO] Loading bars 2014-05-03 15:04:01,348 server [INFO] Waiting for workers 2014-05-03 15:04:58,277 server [INFO] Partial result 124217328754 with parameters: ('dia', 150, 5, 2 2014-05-03 15:04:58,566 server [INFO] Partial result 120326633502 with parameters: ('dia', 150, 5, 2 2014-05-03 15:05:50,965 server [INFO] Partial result 12207631579 with parameters: ('dia', 150, 5, 3, 2014-05-03 15:05:51,325 server [INFO] Partial result 122162750793 with parameters: ('dia', 150, 5, 3 在 客 户 机 控 制 台 可 以 看 到 下 面 的 内 容 : 2014-05-03 15:02:25,360 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 15 2014-05-03 15:02:25,377 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 94, 5) 2014-05-03 15:02:25,661 localworker [INFO] Result 109048106342 2014-05-03 15:02:25,661 localworker [INFO] Result 103147023717 2014-05-03 15:02:25,662 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 93, 25 2014-05-03 15:02:25,665 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 14 2014-05-03 15:02:25,995 localworker [INFO] Result 113555855667 2014-05-03 15:02:25,996 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 93, 24 2014-05-03 15:02:26,006 localworker [INFO] Result 108398718174 2014-05-03 15:02:26,007 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 13 2014-05-03 15:02:26,256 localworker [INFO] Result 109373617175 2014-05-03 15:02:26,257 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 12 2014-05-03 15:02:26,280 localworker [INFO] Result 113555855667 注 意!! 你 只 能 运 行 一 个 服 务 器 和 一 个 或 者 多 个 客 户 机 译 者 补 充 : 这 里 说 的 是 同 一 个 优 化 项 目 下 只 能 运 行 一 个 服 务 器 如 果 你 只 想 在 自 己 的 电 脑 上 对 策 略 并 行 计 算, 你 可 以 利 用 pyalgotradeoptimizerlocal 模 块 来 实 现 : import itertools from pyalgotradeoptimizer import local from pyalgotradebarfeed import yahoofeed import rsi2 def parameters_generator(): instrument = ["dia"] entrysma = list(range(150, 251)) exitsma = list(range(5, 16)) rsiperiod = list(range(2, 11)) overboughtthreshold = list(range(75, 96)) oversoldthreshold = list(range(5, 26)) return itertoolsproduct(instrument, entrysma, exitsma, rsiperiod, overboughtthreshold, oversoldt 14 Chapter 2 简 明 教 程

# The if name == ' main ' part is necessary if running on Windows if name == ' main ': # Load the feed from the CSV files feed = yahoofeedfeed() feedaddbarsfromcsv("dia", "dia-2009csv") feedaddbarsfromcsv("dia", "dia-2010csv") feedaddbarsfromcsv("dia", "dia-2011csv") localrun(rsi2rsi2, feed, parameters_generator()) 上 述 代 码 做 了 3 件 事 : 1 声 明 一 个 生 成 器 函 数, 这 个 函 数 可 以 根 据 参 数 范 围 为 策 略 生 成 参 数 组 合 2 从 我 们 下 载 的 CSV 文 件 载 入 数 据 源 3 通 过 pyalgotradeoptimizerlocal 模 块 运 行 策 略 并 寻 优 当 你 执 行 这 段 代 码 后, 你 可 以 看 到 类 似 下 面 的 结 果 : 2014-05-03 15:08:06,587 server [INFO] Loading bars 2014-05-03 15:08:06,910 server [INFO] Waiting for workers 2014-05-03 15:08:58,347 server [INFO] Partial result 124217328754 with parameters: ('dia', 150, 5, 2 2014-05-03 15:08:58,967 server [INFO] Partial result 120326633502 with parameters: ('dia', 150, 5, 2 2014-05-03 15:09:52,097 server [INFO] Partial result 12207631579 with parameters: ('dia', 150, 5, 3, 2014-05-03 15:09:52,921 server [INFO] Partial result 122162750793 with parameters: ('dia', 150, 5, 3 2014-05-03 15:10:40,826 server [INFO] Partial result 114216223912 with parameters: ('dia', 150, 5, 4 2014-05-03 15:10:41,318 server [INFO] Partial result 110748703214 with parameters: ('dia', 150, 5, 4 在 记 录 中, 最 优 结 果 为 $231440, 它 的 参 数 组 是 : 1 entrysma: 154 2 exitsma: 5 3 rsiperiod: 2 4 overboughtthreshold: 91 5 oversoldthreshold: 18 23 绘 图 PyAlgoTrade 可 以 很 容 易 的 对 策 略 运 行 进 行 绘 图 保 存 为 sma_crossoverpy: from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import cross class SMACrossOver(strategyBacktestingStrategy): def init (self, feed, instrument, smaperiod): strategybacktestingstrategy init (self, feed) self instrument = instrument self position = None # We'll use adjusted close values instead of regular close values 23 绘 图 15

selfsetuseadjustedvalues(true) self prices = feed[instrument]getpricedataseries() self sma = masma(self prices, smaperiod) def getsma(self): return self sma def onentercanceled(self, position): self position = None def onexitok(self, position): self position = None def onexitcanceled(self, position): # If the exit was canceled, re-submit it self positionexitmarket() def onbars(self, bars): # If a position was not opened, check if we should enter a long position if self position is None: if crosscross_above(self prices, self sma) > 0: shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) # Enter a buy market order The order is good till canceled self position = selfenterlong(self instrument, shares, True) # Check if we have to exit the position elif not self positionexitactive() and crosscross_below(self prices, self sma) > 0: self positionexitmarket() 保 存 这 段 代 码 到 另 外 一 个 文 件 : from pyalgotrade import plotter from pyalgotradebarfeed import yahoofeed from pyalgotradestratanalyzer import returns import sma_crossover # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed's bars mystrategy = sma_crossoversmacrossover(feed, "orcl", 20) # Attach a returns analyzers to the strategy returnsanalyzer = returnsreturns() mystrategyattachanalyzer(returnsanalyzer) # Attach the plotter to the strategy plt = plotterstrategyplotter(mystrategy) # Include the SMA in the instrument's subplot to get it displayed along with the closing prices pltgetinstrumentsubplot("orcl")adddataseries("sma", mystrategygetsma()) # Plot the simple returns on each bar pltgetorcreatesubplot("returns")adddataseries("simple returns", returnsanalyzergetreturns()) # Run the strategy mystrategyrun() mystrategyinfo("final portfolio value: $%2f" % mystrategygetresult()) # Plot the strategy 16 Chapter 2 简 明 教 程

pltplot() 上 述 代 码 做 了 3 件 事 : 1 从 一 个 CSV 文 件 中 加 载 数 据 源 2 通 过 数 据 源 提 供 的 bar 逐 根 运 行 策 略 ; 提 供 一 个 策 略 绘 图 程 序 3 绘 制 策 略 图 形 图 形 如 下 : 我 希 望 你 能 喜 欢 这 份 简 明 教 程 同 时 提 醒 你 下 载 地 址 在 这 里 : http://gbecedgithubio/pyalgotrade/downloads/indexhtml 开 始 编 写 你 自 己 的 策 略 吧! 你 可 以 在 策 略 例 子 找 到 更 多 的 例 子 23 绘 图 17

18 Chapter 2 简 明 教 程

CHAPTER 3 代 码 的 文 档 说 明 目 录 : 31 bar 品 种 的 价 格 32 dataseries 数 据 序 列 基 类 数 据 序 列 是 管 理 time-series 数 据 的 抽 象 类 33 feed 数 据 源 基 类 Feeds 是 将 时 间 序 列 抽 象 化 当 这 些 在 事 件 调 度 循 环 时, 会 推 送 一 个 新 的 可 用 数 据 When these are included in the event dispatch loop, they emit an event as new data is available Feeds are also responsible for updating the pyalgotradedataseriesdataseries associated with each piece of data that the feed provides 当 前 位 置 是 关 于 数 据 源 基 类 的, 要 了 解 bar 的 数 据 源 请 查 阅 : barfeed Bar 数 据 来 源 板 块 331 CSV 支 持 332 CSV 支 持 的 例 子 一 个 带 有 以 下 格 式 的 文 件 Date,USD,GBP,EUR 2013-09-29,13330,831203,98675 2013-09-22,134925,842755,997671 2013-09-15,13185,831546,993969 2013-09-08,13870,886885,1052911 像 这 样 引 入 并 使 用 : 19

from pyalgotradefeed import csvfeed feed = csvfeedfeed("date", "%Y-%m-%d") feedaddvaluesfromcsv("quandl_gold_2csv") for datetime, value in feed: print(datetime, value) 输 出 结 果 如 下 : 1968-04-07 00:00:00 {'USD': 370, 'GBP': 153875, 'EUR': ''} 1968-04-14 00:00:00 {'USD': 380, 'GBP': 158208, 'EUR': ''} 1968-04-21 00:00:00 {'USD': 3765, 'GBP': 156833, 'EUR': ''} 1968-04-28 00:00:00 {'USD': 3865, 'GBP': 161271, 'EUR': ''} 1968-05-05 00:00:00 {'USD': 391, 'GBP': 163188, 'EUR': ''} 1968-05-12 00:00:00 {'USD': 396, 'GBP': 165625, 'EUR': ''} 1968-05-19 00:00:00 {'USD': 415, 'GBP': 173958, 'EUR': ''} 1968-05-26 00:00:00 {'USD': 4175, 'GBP': 175104, 'EUR': ''} 1968-06-02 00:00:00 {'USD': 4195, 'GBP': 176, 'EUR': ''} 1968-06-09 00:00:00 {'USD': 4125, 'GBP': 173042, 'EUR': ''} 2013-07-28 00:00:00 {'USD': 13310, 'GBP': 86423, 'EUR': 1001505} 2013-08-04 00:00:00 {'USD': 130925, 'GBP': 858637, 'EUR': 986921} 2013-08-11 00:00:00 {'USD': 13090, 'GBP': 843156, 'EUR': 97979} 2013-08-18 00:00:00 {'USD': 136925, 'GBP': 875424, 'EUR': 1024964} 2013-08-25 00:00:00 {'USD': 13775, 'GBP': 885738, 'EUR': 10306} 2013-09-01 00:00:00 {'USD': 139475, 'GBP': 901292, 'EUR': 1055749} 2013-09-08 00:00:00 {'USD': 13870, 'GBP': 886885, 'EUR': 1052911} 2013-09-15 00:00:00 {'USD': 13185, 'GBP': 831546, 'EUR': 993969} 2013-09-22 00:00:00 {'USD': 134925, 'GBP': 842755, 'EUR': 997671} 2013-09-29 00:00:00 {'USD': 13330, 'GBP': 831203, 'EUR': 98675} 34 barfeed Bar 数 据 来 源 341 CSV 342 Yahoo! Finance 343 Google Finance 344 Quandl 345 Ninja Trader 35 technical 技 术 指 标 351 例 子 下 面 的 这 个 例 子 演 示 了 如 何 结 合 EventWindow 和 EventBasedFilter 来 计 算 一 个 指 标 : 20 Chapter 3 代 码 的 文 档 说 明

from pyalgotrade import dataseries from pyalgotrade import technical # An EventWindow is responsible for making calculations using a window of values class Accumulator(technicalEventWindow): def getvalue(self): ret = None if selfwindowfull(): ret = selfgetvalues()sum() return ret # Build a sequence based DataSeries seqds = dataseriessequencedataseries() # Wrap it with a filter that will get fed as new values get added to the underlying DataSeries accum = technicaleventbasedfilter(seqds, Accumulator(3)) # Put in some values for i in range(0, 50): seqdsappend(i) # Get some values print(accum[0]) # Not enough values yet print(accum[1]) # Not enough values yet print(accum[2]) # Ok, now we should have at least 3 values print(accum[3]) # Get the last value, which should be equal to 49 + 48 + 47 print(accum[-1]) 输 出 内 容 如 下 : None None 30 60 1440 352 移 动 平 均 线 353 动 量 指 标 354 其 他 指 标 36 broker 订 单 管 理 类 361 基 类 和 模 块 362 回 测 模 块 和 类 37 strategy 策 略 基 类 Strategies 类 是 你 定 义 的 何 时 买 何 时 卖 等 等 交 易 逻 辑 的 类 36 broker 订 单 管 理 类 21

做 多 和 做 空 可 以 通 过 以 下 两 种 方 式 : 使 用 下 面 任 意 一 个 方 法 下 单 : pyalgotradestrategybasestrategymarketorder() pyalgotradestrategybasestrategylimitorder() pyalgotradestrategybasestrategystoporder() pyalgotradestrategybasestrategystoplimitorder() 使 用 封 装 了 买 / 卖 的 高 级 接 口 下 单 : pyalgotradestrategybasestrategyenterlong() pyalgotradestrategybasestrategyentershort() pyalgotradestrategybasestrategyenterlonglimit() pyalgotradestrategybasestrategyentershortlimit() Positions are higher level abstractions for placing orders They are escentially a pair of entry-exit orders and provide easier tracking for returns and PnL than using individual orders 371 Strategy 372 Position 38 stratanalyzer Strategy analyzers Strategy analyzers provide an extensible way to attach different calculations to strategy executions class pyalgotradestratanalyzerstrategyanalyzer 基 类 :object Base class for strategy analyzers 注 解 : This is a base class and should not be used directly 381 Returns 382 Sharpe Ratio 383 DrawDown class pyalgotradestratanalyzerdrawdowndrawdown 基 类 :pyalgotradestratanalyzerstrategyanalyzer A pyalgotradestratanalyzerstrategyanalyzer that calculates max drawdown and longest drawdown duration for the portfolio getlongestdrawdownduration() Returns the duration of the longest drawdown 返 回 类 型 datetimetimedelta 22 Chapter 3 代 码 的 文 档 说 明

注 解 : Note that this is the duration of the longest drawdown, not necessarily the deepest one getmaxdrawdown() Returns the max (deepest) drawdown 384 Trades 385 Example Save this code as sma_crossoverpy: from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import cross class SMACrossOver(strategyBacktestingStrategy): def init (self, feed, instrument, smaperiod): strategybacktestingstrategy init (self, feed) self instrument = instrument self position = None # We'll use adjusted close values instead of regular close values selfsetuseadjustedvalues(true) self prices = feed[instrument]getpricedataseries() self sma = masma(self prices, smaperiod) def getsma(self): return self sma def onentercanceled(self, position): self position = None def onexitok(self, position): self position = None def onexitcanceled(self, position): # If the exit was canceled, re-submit it self positionexitmarket() def onbars(self, bars): # If a position was not opened, check if we should enter a long position if self position is None: if crosscross_above(self prices, self sma) > 0: shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) # Enter a buy market order The order is good till canceled self position = selfenterlong(self instrument, shares, True) # Check if we have to exit the position elif not self positionexitactive() and crosscross_below(self prices, self sma) > 0: self positionexitmarket() and save this code in a different file: from pyalgotradebarfeed import yahoofeed from pyalgotradestratanalyzer import returns from pyalgotradestratanalyzer import sharpe 38 stratanalyzer Strategy analyzers 23

from pyalgotradestratanalyzer import drawdown from pyalgotradestratanalyzer import trades import sma_crossover # Load the yahoo feed from the CSV file feed = yahoofeedfeed() feedaddbarsfromcsv("orcl", "orcl-2000csv") # Evaluate the strategy with the feed's bars mystrategy = sma_crossoversmacrossover(feed, "orcl", 20) # Attach different analyzers to a strategy before executing it retanalyzer = returnsreturns() mystrategyattachanalyzer(retanalyzer) sharperatioanalyzer = sharpesharperatio() mystrategyattachanalyzer(sharperatioanalyzer) drawdownanalyzer = drawdowndrawdown() mystrategyattachanalyzer(drawdownanalyzer) tradesanalyzer = tradestrades() mystrategyattachanalyzer(tradesanalyzer) # Run the strategy mystrategyrun() print("final portfolio value: $%2f" % mystrategygetresult()) print("cumulative returns: %2f %%" % (retanalyzergetcumulativereturns()[-1] * 100)) print("sharpe ratio: %2f" % (sharperatioanalyzergetsharperatio(005))) print("max drawdown: %2f %%" % (drawdownanalyzergetmaxdrawdown() * 100)) print("longest drawdown duration: %s" % (drawdownanalyzergetlongestdrawdownduration())) print() print("total trades: %d" % (tradesanalyzergetcount())) if tradesanalyzergetcount() > 0: profits = tradesanalyzergetall() print("avg profit: $%2f" % (profitsmean())) print("profits std dev: $%2f" % (profitsstd())) print("max profit: $%2f" % (profitsmax())) print("min profit: $%2f" % (profitsmin())) returns = tradesanalyzergetallreturns() print("avg return: %2f %%" % (returnsmean() * 100)) print("returns std dev: %2f %%" % (returnsstd() * 100)) print("max return: %2f %%" % (returnsmax() * 100)) print("min return: %2f %%" % (returnsmin() * 100)) print() print("profitable trades: %d" % (tradesanalyzergetprofitablecount())) if tradesanalyzergetprofitablecount() > 0: profits = tradesanalyzergetprofits() print("avg profit: $%2f" % (profitsmean())) print("profits std dev: $%2f" % (profitsstd())) print("max profit: $%2f" % (profitsmax())) print("min profit: $%2f" % (profitsmin())) returns = tradesanalyzergetpositivereturns() print("avg return: %2f %%" % (returnsmean() * 100)) print("returns std dev: %2f %%" % (returnsstd() * 100)) print("max return: %2f %%" % (returnsmax() * 100)) print("min return: %2f %%" % (returnsmin() * 100)) 24 Chapter 3 代 码 的 文 档 说 明

print() print("unprofitable trades: %d" % (tradesanalyzergetunprofitablecount())) if tradesanalyzergetunprofitablecount() > 0: losses = tradesanalyzergetlosses() print("avg loss: $%2f" % (lossesmean())) print("losses std dev: $%2f" % (lossesstd())) print("max loss: $%2f" % (lossesmin())) print("min loss: $%2f" % (lossesmax())) returns = tradesanalyzergetnegativereturns() print("avg return: %2f %%" % (returnsmean() * 100)) print("returns std dev: %2f %%" % (returnsstd() * 100)) print("max return: %2f %%" % (returnsmax() * 100)) print("min return: %2f %%" % (returnsmin() * 100)) The output should look like this: Final portfolio value: $129588722 Cumulative returns: 2959 % Sharpe ratio: 070 Max drawdown: 2453 % Longest drawdown duration: 277 days, 0:00:00 Total trades: 13 Avg profit: $14437 Profits std dev: $127539 Max profit: $420866 Min profit: $-89320 Avg return: 2 % Returns std dev: 13 % Max return: 46 % Min return: -7 % Profitable trades: 3 Avg profit: $197053 Profits std dev: $158987 Max profit: $420866 Min profit: $66537 Avg return: 21 % Returns std dev: 18 % Max return: 46 % Min return: 6 % Unprofitable trades: 10 Avg loss: $-40348 Losses std dev: $23601 Max loss: $-89320 Min loss: $-4516 Avg return: -3 % Returns std dev: 2 % Max return: -0 % Min return: -7 % 38 stratanalyzer Strategy analyzers 25

39 plotter Strategy plotter 310 optimizer Parallel optimizers 注 解 : The server component will split strategy executions in chunks which are distributed among the different workers pyalgotradeoptimizerserverserverdefaultbatchsize controls the chunk size The pyalgotradestrategybasestrategygetresult() method is used to select the best strategy execution You can override that method to rank executions using a different criteria 311 marketsession Market sessions class pyalgotrademarketsessionmarketsession 基 类 :object Base class for market sessions 注 解 : This is a base class and should not be used directly classmethod gettimezone() Returns the pytz timezone for the market session class pyalgotrademarketsessionnasdaq 基 类 :pyalgotrademarketsessionmarketsession NASDAQ market session class pyalgotrademarketsessionnyse 基 类 :pyalgotrademarketsessionmarketsession New York Stock Exchange market session class pyalgotrademarketsessionusequities 基 类 :pyalgotrademarketsessionmarketsession US Equities market session class pyalgotrademarketsessionmerval 基 类 :pyalgotrademarketsessionmarketsession Buenos Aires (Argentina) market session class pyalgotrademarketsessionbovespa 基 类 :pyalgotrademarketsessionmarketsession BOVESPA (Brazil) market session class pyalgotrademarketsessionftse 基 类 :pyalgotrademarketsessionmarketsession London Stock Exchange market session 26 Chapter 3 代 码 的 文 档 说 明

class pyalgotrademarketsessiontse 基 类 :pyalgotrademarketsessionmarketsession Tokyo Stock Exchange market session 311 marketsession Market sessions 27

28 Chapter 3 代 码 的 文 档 说 明

CHAPTER 4 工 具 41 Yahoo! Finance 42 Quandl 43 BarFeed resampling 29

30 Chapter 4 工 具

CHAPTER 5 Event profiler Inspired in QSTK (http://wikiquantsoftwareorg/indexphp?title=qstk_tutorial_9), the eventprofiler module is a tool to analyze, statistically, how events affect future equity prices 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 and the future over a certain lookback period The goal of this tool is to help you quickly validate an idea, before moving forward with the backtesting process 51 Example The following example is inspired on the Buy-on-Gap Model from Ernie Chan s book: Algorithmic Trading: Winning Strategies and Their Rationale : The idea is to select a stock near the market open whose returns from their previous day s lows to today s open are lower that one standard deviation The standard deviation is computed using the daily close-to-close returns of the last 90 days These are the stocks that gapped down This is narrowed down by requiring the open price to be higher than the 20-day moving average of the closing price from pyalgotrade import eventprofiler from pyalgotradetechnical import stats from pyalgotradetechnical import roc from pyalgotradetechnical import ma from pyalgotradetools import yahoofinance # Event inspired on an example from Ernie Chan's book: # 'Algorithmic Trading: Winning Strategies and Their Rationale' class BuyOnGap(eventprofilerPredicate): def init (self, feed): stddevperiod = 90 smaperiod = 20 self returns = {} self stddev = {} self ma = {} for instrument in feedgetregisteredinstruments(): priceds = feed[instrument]getadjclosedataseries() # Returns over the adjusted close values self returns[instrument] = rocrateofchange(priceds, 1) # StdDev over those returns 31

self stddev[instrument] = statsstddev(self returns[instrument], stddevperiod) # MA over the adjusted close values self ma[instrument] = masma(priceds, smaperiod) def gappeddown(self, instrument, bards): ret = False if self stddev[instrument][-1] is not None: prevbar = bards[-2] currbar = bards[-1] low2openret = (currbargetopen(true) - prevbargetlow(true)) / float(prevbargetlow(true) if low2openret < (self returns[instrument][-1] - self stddev[instrument][-1]): ret = True return ret def abovesma(self, instrument, bards): ret = False if self ma[instrument][-1] is not None and bards[-1]getopen(true) > self ma[instrument][ ret = True return ret def eventoccurred(self, instrument, bards): ret = False if self gappeddown(instrument, bards) and self abovesma(instrument, bards): ret = True return ret def main(plot): instruments = ["AA", "AES", "AIG"] feed = yahoofinancebuild_feed(instruments, 2008, 2009, "") predicate = BuyOnGap(feed) eventprofiler = eventprofilerprofiler(predicate, 5, 5) eventprofilerrun(feed, True) results = eventprofilergetresults() print("%d events found" % (resultsgeteventcount())) if plot: eventprofilerplot(results) if name == " main ": main(true) The code is doing 4 things: 1 Declaring a Predicate that implements the Buy-on-Gap Model event identification 2 Loading bars for some stocks 3 Running the analysis 4 Plotting the results This is what the output should look like: 32 Chapter 5 Event profiler

2013-09-20 23:30:45,340 yahoofinance [INFO] Creating data directory 2013-09-20 23:30:45,341 yahoofinance [INFO] Downloading AA 2008 to data/aa-2008-yahoofinancecsv 2013-09-20 23:30:46,092 yahoofinance [INFO] Downloading AES 2008 to data/aes-2008-yahoofinancecsv 2013-09-20 23:30:46,683 yahoofinance [INFO] Downloading AIG 2008 to data/aig-2008-yahoofinancecsv 2013-09-20 23:30:47,260 yahoofinance [INFO] Downloading AA 2009 to data/aa-2009-yahoofinancecsv 2013-09-20 23:30:48,019 yahoofinance [INFO] Downloading AES 2009 to data/aes-2009-yahoofinancecsv 2013-09-20 23:30:48,761 yahoofinance [INFO] Downloading AIG 2009 to data/aig-2009-yahoofinancecsv 14 events found Note that Cummulative returns are normalized to the time of the event 51 Example 33

34 Chapter 5 Event profiler

CHAPTER 6 Xignite support The xignite package adds support for paper trading strategies using Xignite s live feeds via the XigniteGlobalRealTime API (https://wwwxignitecom/product/global-real-time-stock-quote-data/) in conjunction with PyAlgoTrade backtesting capabilities Contents: 61 xignite Xignite reference 611 Feeds 62 Xignite Example This goal of this example is to show how to put all the pieces together to paper trade a strategy using realtime feeds supplied by Xignite (https://wwwxignitecom/) This example assumes that you re already familiar with the basic concepts presented in the 简 明 教 程 section The key things to highlight are: 1 We re using pyalgotradestrategybasestrategy instead of pyalgotradestrategybacktestingstrategy as the base class This is not a backtest 2 pyalgotradexignitebarfeedlivefeed is used to pull 5 minute bars directly from Xignite In order to use this service you need to sign up for the XigniteGlobalRealTime API (https://wwwxignitecom/product/global-real-time-stock-quote-data/) to get an API token 3 As described in https://wwwxignitecom/product/global-real-time-stock-quote-data/api/getbar/, indentifiers are fully qualified identifiers for the security as determined by the IdentifierType parameter You must include the exchange suffix 4 You can only paper trade while the market is open 5 The 5 minute bars are requested 60 seconds after the 5 minute window closes because data may not be immediately available from pyalgotrade import strategy from pyalgotradebar import Frequency from pyalgotradexignite import barfeed from pyalgotradebroker import backtesting from pyalgotradetechnical import ma 35

class Strategy(strategyBaseStrategy): def init (self, feed, brk): strategybasestrategy init (self, feed, brk) self sma = {} for instrument in feedgetregisteredinstruments(): self sma[instrument] = masma(feed[instrument]getclosedataseries(), 5) def onbars(self, bars): for instrument in barsgetinstruments(): bar = bars[instrument] selfinfo("%s: Open: %s High: %s Low: %s Close: %s Volume: %s SMA: %s" % (instrument, bar def main(): # Replace apitoken with your own API token apitoken = "<YOUR API TOKEN HERE>" # indentifiers are fully qualified identifiers for the security and must include the exchange suf indentifiers = ["RIOlCHIX", "HSBAlCHIX"] # apicalldelay is necessary because the bar may not be immediately available apicalldelay = 60 feed = barfeedlivefeed(apitoken, indentifiers, FrequencyMINUTE*5, apicalldelay) brk = backtestingbroker(1000, feed) mystrategy = Strategy(feed, brk) mystrategyrun() if name == " main ": main() The output should look like this: 2014-03-28 09:31:01,389 strategy [INFO] HSBAlCHIX: Open: 6093 High: 6102 Low: 6093 Close: 6101 V 2014-03-28 09:31:01,390 strategy [INFO] RIOlCHIX: Open: 3308 High: 3308 Low: 33065 Close: 3307 V 2014-03-28 09:36:01,494 strategy [INFO] HSBAlCHIX: Open: 6102 High: 6102 Low: 6099 Close: 6099 V 2014-03-28 09:36:01,495 strategy [INFO] RIOlCHIX: Open: 33075 High: 3309 Low: 33055 Close: 33055 2014-03-28 09:41:01,885 strategy [INFO] HSBAlCHIX: Open: 6101 High: 6101 Low: 6097 Close: 6097 V 2014-03-28 09:41:01,886 strategy [INFO] RIOlCHIX: Open: 3304 High: 3304 Low: 33005 Close: 33005 2014-03-28 09:46:00,943 strategy [INFO] HSBAlCHIX: Open: 6098 High: 6098 Low: 6093 Close: 6094 V 2014-03-28 09:46:00,943 strategy [INFO] RIOlCHIX: Open: 33005 High: 33035 Low: 32995 Close: 3299 2014-03-28 09:51:01,604 strategy [INFO] HSBAlCHIX: Open: 6093 High: 6099 Low: 6092 Close: 6097 V 2014-03-28 09:51:01,604 strategy [INFO] RIOlCHIX: Open: 3299 High: 33025 Low: 32985 Close: 3299 2014-03-28 09:56:01,511 strategy [INFO] HSBAlCHIX: Open: 6099 High: 61 Low: 6096 Close: 6098 Vol 2014-03-28 09:56:01,511 strategy [INFO] RIOlCHIX: Open: 3298 High: 3301 Low: 3296 Close: 3301 Vo When apicalldelay is not long enough, or when there is no data at all, you may receive the following error message: xignite [ERROR] No ticks available for Symbol:RIOlCHIX from 3/28/2014 1:10:00 PM to 3/28/2014 1:11:0 36 Chapter 6 Xignite support

CHAPTER 7 Bitcoin Contents: 71 Bitstamp support The bitstamp package adds support for paper trading strategies using Bitstamp s live trade and orderbook feeds (https://wwwbitstampnet/websocket/) in conjunction with PyAlgoTrade backtesting capabilities Future versions will support real trading Bitstamp support depends on ws4py (https://githubcom/lawouach/websocket-for-python) and tornado (http://wwwtornadoweborg/en/stable/) so be sure to have those installed before moving forward Contents: 711 bitstamp Bitstamp reference WebSocket This package has classes for the events emitted by Bitstamp s streaming service Check https://wwwbitstampnet/websocket/ for more information Feeds Brokers 712 Bitstamp Example This goal of this simple SMA crossover example is to show how to put all the pieces together to paper trade a strategy using realtime feeds supplied by Bitstamp (https://wwwbitstampnet/) This example assumes that you re already familiar with the basic concepts presented in the 简 明 教 程 section The key things to highlight are: 1 We re using pyalgotradestrategybasestrategy instead of pyalgotradestrategybacktestingstrategy as the base class This is not a backtest 2 Trade events get notified via the call to onbars No need to manually subscribe 37

3 Order book update events are handled by manually subscribing to pyalgotradebitstampbarfeedlivetradefeedgetorderbookupdateevent This is needed to be up to date with latest bid and ask prices from pyalgotradebitstamp import barfeed from pyalgotradebitstamp import broker from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import cross class Strategy(strategyBaseStrategy): def init (self, feed, brk): strategybasestrategy init (self, feed, brk) smaperiod = 20 self instrument = "BTC" self prices = feed[self instrument]getclosedataseries() self sma = masma(self prices, smaperiod) self bid = None self ask = None self position = None self possize = 005 # Subscribe to order book update events to get bid/ask prices to trade feedgetorderbookupdateevent()subscribe(self onorderbookupdate) def onorderbookupdate(self, orderbookupdate): bid = orderbookupdategetbidprices()[0] ask = orderbookupdategetaskprices()[0] if bid!= self bid or ask!= self ask: self bid = bid self ask = ask selfinfo("order book updated Best bid: %s Best ask: %s" % (self bid, self ask)) def onenterok(self, position): selfinfo("position opened at %s" % (positiongetentryorder()getexecutioninfo()getprice())) def onentercanceled(self, position): selfinfo("position entry canceled") self position = None def onexitok(self, position): self position = None selfinfo("position closed at %s" % (positiongetexitorder()getexecutioninfo()getprice())) def onexitcanceled(self, position): # If the exit was canceled, re-submit it self positionexitlimit(self bid) def onbars(self, bars): bar = bars[self instrument] selfinfo("price: %s Volume: %s" % (bargetclose(), bargetvolume())) # Wait until we get the current bid/ask prices if self ask is None: return # If a position was not opened, check if we should enter a long position 38 Chapter 7 Bitcoin

if self position is None: if crosscross_above(self prices, self sma) > 0: selfinfo("entry signal Buy at %s" % (self ask)) self position = selfenterlonglimit(self instrument, self ask, self possize, # Check if we have to close the position elif not self positionexitactive() and crosscross_below(self prices, self sma) > 0: selfinfo("exit signal Sell at %s" % (self bid)) self positionexitlimit(self bid) def main(): barfeed = barfeedlivetradefeed() brk = brokerpapertradingbroker(1000, barfeed) strat = Strategy(barFeed, brk) stratrun() if name == " main ": main() The output should look like this: 2014-03-15 00:35:59,085 bitstamp [INFO] Initializing websocket client 2014-03-15 00:35:59,452 bitstamp [INFO] Connection established 2014-03-15 00:35:59,453 bitstamp [INFO] Initialization ok 2014-03-15 00:36:30,726 strategy [INFO] Order book updated Best bid: 6296 Best ask: 6300 2014-03-15 00:39:04,829 strategy [INFO] Order book updated Best bid: 62889 Best ask: 6300 2014-03-15 00:44:18,845 strategy [INFO] Price: 6300 Volume: 001 2014-03-15 00:44:18,894 strategy [INFO] Order book updated Best bid: 6300 Best ask: 63149 2014-03-15 00:44:29,719 strategy [INFO] Price: 6300 Volume: 002 2014-03-15 00:44:59,861 strategy [INFO] Price: 63149 Volume: 003360823 2014-03-15 00:45:37,425 strategy [INFO] Order book updated Best bid: 6300 Best ask: 6316 2014-03-15 00:45:39,848 strategy [INFO] Price: 6316 Volume: 335089782 2014-03-15 00:45:39,918 strategy [INFO] Price: 63224 Volume: 0136 2014-03-15 00:45:39,971 strategy [INFO] Price: 63224 Volume: 0138 2014-03-15 00:45:40,057 strategy [INFO] Price: 63225 Volume: 009076537 2014-03-15 00:45:40,104 strategy [INFO] Price: 63242 Volume: 074011681 2014-03-15 00:45:40,205 strategy [INFO] Order book updated Best bid: 6300 Best ask: 63242 2014-03-15 00:48:30,005 strategy [INFO] Price: 6300 Volume: 497 2014-03-15 00:48:30,039 strategy [INFO] Price: 6296 Volume: 009 2014-03-15 00:48:30,121 strategy [INFO] Price: 62954 Volume: 009 2014-03-15 00:48:33,053 strategy [INFO] Price: 62555 Volume: 1296299 2014-03-15 00:48:33,164 strategy [INFO] Price: 62552 Volume: 00924981 2014-03-15 00:48:33,588 strategy [INFO] Price: 62545 Volume: 1346260589 2014-03-15 00:48:33,635 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63242 2014-03-15 00:48:33,727 strategy [INFO] Price: 62545 Volume: 175 2014-03-15 00:48:34,261 strategy [INFO] Price: 62548 Volume: 01 2014-03-15 00:48:34,908 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63139 2014-03-15 00:48:36,203 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63242 2014-03-15 00:49:01,945 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63139 2014-03-15 00:49:34,743 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63128 2014-03-15 00:49:57,651 strategy [INFO] Price: 62926 Volume: 066893865 71 Bitstamp support 39

2014-03-15 00:49:57,651 strategy [INFO] Entry signal Buy at 63128 2014-03-15 00:50:09,934 strategy [INFO] Order book updated Best bid: 62926 Best ask: 63139 2014-03-15 00:50:20,786 strategy [INFO] Order book updated Best bid: 62702 Best ask: 63139 2014-03-15 00:50:25,658 strategy [INFO] Price: 63139 Volume: 001 2014-03-15 00:50:25,732 strategy [INFO] Price: 63139 Volume: 001 2014-03-15 00:50:25,791 strategy [INFO] Price: 63193 Volume: 05 2014-03-15 00:50:25,847 strategy [INFO] Price: 63242 Volume: 025988319 2014-03-15 00:50:25,900 strategy [INFO] Price: 63242 Volume: 0184 2014-03-15 00:50:25,952 strategy [INFO] Price: 63242 Volume: 0184 2014-03-15 00:50:26,000 strategy [INFO] Price: 63242 Volume: 0184 2014-03-15 00:50:26,065 strategy [INFO] Price: 63244 Volume: 0184 2014-03-15 00:50:26,139 strategy [INFO] Price: 63297 Volume: 0092 2014-03-15 00:50:26,300 strategy [INFO] Order book updated Best bid: 62702 Best ask: 6290 2014-03-15 00:50:26,398 strategy [INFO] Price: 6331 Volume: 016211681 2014-03-15 00:50:29,850 strategy [INFO] Position opened at 6290 2014-03-15 00:50:29,850 strategy [INFO] Price: 6290 Volume: 025152623 2014-03-15 00:50:29,850 strategy [INFO] Exit signal Sell at 62702 2014-03-15 00:50:37,294 strategy [INFO] Order book updated Best bid: 62702 Best ask: 6331 2014-03-15 00:50:43,526 strategy [INFO] Order book updated Best bid: 62702 Best ask: 63308 2014-03-15 00:51:07,604 strategy [INFO] Order book updated Best bid: 62702 Best ask: 63299 2014-03-15 00:51:46,194 strategy [INFO] Order book updated Best bid: 62702 Best ask: 63089 2014-03-15 00:52:08,223 strategy [INFO] Position closed at 62702 2014-03-15 00:52:08,223 strategy [INFO] Price: 62702 Volume: 00924979 2014-03-15 00:52:08,290 strategy [INFO] Price: 62702 Volume: 002 2014-03-15 00:52:08,530 strategy [INFO] Order book updated Best bid: 62701 Best ask: 62702 2014-03-15 00:52:35,347 strategy [INFO] Price: 63089 Volume: 007 2014-03-15 00:52:35,347 strategy [INFO] Entry signal Buy at 62702 2014-03-15 00:52:35,348 strategy [INFO] Price: 63095 Volume: 009 2014-03-15 00:52:35,428 strategy [INFO] Price: 6310 Volume: 005 2014-03-15 00:54:14,077 strategy [INFO] Order book updated Best bid: 62881 Best ask: 6324 2014-03-15 00:54:21,084 strategy [INFO] Order book updated Best bid: 6310 Best ask: 6324 2014-03-15 00:54:34,484 strategy [INFO] Price: 6324 Volume: 0296299 2014-03-15 00:54:34,484 strategy [INFO] Exit signal Sell at 6310 2014-03-15 00:54:34,484 strategy [INFO] Position entry canceled 2014-03-15 00:54:34,578 strategy [INFO] Price: 63245 Volume: 35 2014-03-15 00:54:34,642 strategy [INFO] Price: 63245 Volume: 05715 2014-03-15 00:54:34,708 strategy [INFO] Price: 63246 Volume: 0136 2014-03-15 00:54:34,789 strategy [INFO] Price: 63246 Volume: 12682 2014-03-15 00:54:34,885 strategy [INFO] Price: 63246 Volume: 35 2014-03-15 00:54:34,949 strategy [INFO] Price: 63246 Volume: 525 2014-03-15 00:54:35,029 strategy [INFO] Price: 63247 Volume: 988740834 2014-03-15 00:54:35,165 strategy [INFO] Price: 63289 Volume: 1824259574 2014-03-15 00:54:35,165 strategy [INFO] Entry signal Buy at 6324 2014-03-15 00:54:35,286 strategy [INFO] Price: 6330 Volume: 0092 2014-03-15 00:54:35,357 strategy [INFO] Price: 6331 Volume: 037612853 2014-03-15 00:56:48,885 strategy [INFO] Order book updated Best bid: 6321 Best ask: 63435 2014-03-15 00:56:57,234 strategy [INFO] Position opened at 6321 2014-03-15 00:56:57,235 strategy [INFO] Price: 6321 Volume: 066267992 2014-03-15 00:56:57,235 strategy [INFO] Exit signal Sell at 6321 2014-03-15 00:56:57,268 strategy [INFO] Price: 63183 Volume: 5933732008 2014-03-15 00:56:57,356 strategy [INFO] Order book updated Best bid: 63183 Best ask: 63435 2014-03-15 00:57:03,528 strategy [INFO] Price: 63183 Volume: 008267992 40 Chapter 7 Bitcoin

2014-03-15 00:57:03,604 strategy [INFO] Order book updated Best bid: 6310 Best ask: 63183 2014-03-15 00:57:04,824 strategy [INFO] Order book updated Best bid: 6310 Best ask: 63434 2014-03-15 00:57:09,775 strategy [INFO] Order book updated Best bid: 6310 Best ask: 63433 2014-03-15 00:57:11,112 strategy [INFO] Order book updated Best bid: 6321 Best ask: 63433 2014-03-15 00:57:15,822 strategy [INFO] Position closed at 6321 2014-03-15 00:57:15,822 strategy [INFO] Price: 6321 Volume: 02 2014-03-15 00:57:20,839 strategy [INFO] Price: 6321 Volume: 046267992 2014-03-15 00:57:22,065 strategy [INFO] Price: 6312 Volume: 003732008 2014-03-15 00:57:22,122 strategy [INFO] Price: 63433 Volume: 0135 2014-03-15 00:57:22,122 strategy [INFO] Entry signal Buy at 63433 2014-03-15 00:57:22,184 strategy [INFO] Price: 63434 Volume: 097972409 In order to live trade this strategy you should use pyalgotradebitstampbrokerlivebroker instead of pyalgotradebitstampbrokerpapertradingbroker Note that if you try to live trade this strategy you will probably loose money Before jumping to live trading, be sure to write your own strategy, backtest and paper trade it thoroughly before risking real money 72 Bitcoin Charts support The bitcoincharts package adds support for integrating with historical trade data supplied by http://wwwbitcoinchartscom/ for backtesting Bitcoin strategies Historical trade data in CSV format is described in http://wwwbitcoinchartscom/about/markets-api/, and files can be downloaded from http://apibitcoinchartscom/v1/csv/ Contents: 721 bitcoincharts Bitcoin Charts reference Feeds 722 Bitcoin Charts example Although it is absolutely possible to backtest a strategy with tick data as supplied by http://wwwbitcoinchartscom/about/markets-api/ using pyalgotradebitcoinchartsbarfeedcsvtradefeed, you may want to to backtest using summarized bars at a different frequency to make backtesting faster As of 12-Aug-2014, http://apibitcoinchartscom/v1/csv/bitstampusdcsvgz has 4588830 events so we ll transform a portion of it into 30 minute bars for backtesting purposes with the following script: from pyalgotradebitcoincharts import barfeed from pyalgotradetools import resample from pyalgotrade import bar import datetime def main(): barfeed = barfeedcsvtradefeed() barfeedaddbarsfromcsv("bitstampusdcsv", fromdatetime=datetimedatetime(2014, 1, 1)) resampleresample_to_csv(barfeed, barfrequencyminute*30, "30min-bitstampUSDcsv") 72 Bitcoin Charts support 41

if name == " main ": main() It will take some time to execute, so be patient The resampled file should look like this: Date Time,Open,High,Low,Close,Volume,Adj Close 2014-01-01 00:00:00,7320,73825,72901,73481,26617955488, 2014-01-01 00:30:00,73481,7399,73447,73902,30896802502, 2014-01-01 01:00:00,73902,73997,73765,73811,6566924473, 2014-01-01 01:30:00,7380,7420,73765,74189,71027165024, 2014-01-01 02:00:00,74189,75799,74189,75223,108513335011, 2014-01-01 02:30:00,75223,7550,7470,7472,27203949342, 2014-01-01 04:00:00,74498,74802,74498,74719,10465989075, We can now take advantage of pyalgotradebarfeedcsvfeedgenericbarfeed to load the resampled file and backtest a Bitcoin strategy We ll be using a VWAP momentum strategy for illustration purposes: from pyalgotrade import bar from pyalgotrade import strategy from pyalgotrade import plotter from pyalgotradetechnical import vwap from pyalgotradebarfeed import csvfeed from pyalgotradebitstamp import broker from pyalgotrade import broker as basebroker class VWAPMomentum(strategyBacktestingStrategy): MIN_TRADE = 5 def init (self, feed, brk, instrument, vwapwindowsize, buythreshold, sellthreshold): strategybacktestingstrategy init (self, feed, brk) self instrument = instrument self vwap = vwapvwap(feed[instrument], vwapwindowsize) self buythreshold = buythreshold self sellthreshold = sellthreshold def _getactiveorders(self): orders = selfgetbroker()getactiveorders() buy = [o for o in orders if oisbuy()] sell = [o for o in orders if oissell()] return buy, sell def _cancelorders(self, orders): brk = selfgetbroker() for o in orders: selfinfo("canceling order %s" % (ogetid())) brkcancelorder(o) def _buysignal(self, price): buyorders, sellorders = self_getactiveorders() self_cancelorders(sellorders) brk = selfgetbroker() cashavail = brkgetcash() * 098 size = round(cashavail / price, 3) 42 Chapter 7 Bitcoin

if len(buyorders) == 0 and price*size > VWAPMomentumMIN_TRADE: selfinfo("buy %s at %s" % (size, price)) try: selflimitorder(self instrument, price, size) except Exception as e: selferror("failed to buy: %s" % (e)) def _sellsignal(self, price): buyorders, sellorders = self_getactiveorders() self_cancelorders(buyorders) brk = selfgetbroker() shares = brkgetshares(self instrument) if len(sellorders) == 0 and shares > 0: selfinfo("sell %s at %s" % (shares, price)) selflimitorder(self instrument, price, shares*-1) def getvwap(self): return self vwap def onbars(self, bars): vwap = self vwap[-1] if vwap is None: return price = bars[self instrument]getclose() if price > vwap * (1 + self buythreshold): self_buysignal(price) elif price < vwap * (1 - self sellthreshold): self_sellsignal(price) def onorderupdated(self, order): if orderisbuy(): ordertype = "Buy" else: ordertype = "Sell" selfinfo("%s order %d updated - Status: %s - %s" % ( ordertype, ordergetid(), basebrokerorderstatetostring(ordergetstate()), ordergetexecutioninfo(), )) def main(plot): instrument = "BTC" initialcash = 1000 vwapwindowsize = 100 buythreshold = 002 sellthreshold = 001 barfeed = csvfeedgenericbarfeed(barfrequencyminute*30) barfeedaddbarsfromcsv(instrument, "30min-bitstampUSDcsv") brk = brokerbacktestingbroker(initialcash, barfeed) strat = VWAPMomentum(barFeed, brk, instrument, vwapwindowsize, buythreshold, sellthreshold) if plot: plt = plotterstrategyplotter(strat) 72 Bitcoin Charts support 43

pltgetinstrumentsubplot(instrument)adddataseries("vwap", stratgetvwap()) stratrun() if plot: pltplot() if name == " main ": main(true) This is what the plot looks like: 44 Chapter 7 Bitcoin

CHAPTER 8 Twitter support The twitter package adds support for receiving Twitter events in your strategy and incorporate those in your trading decisions Note that since this is a realtime feed, it only makes sense in paper trading or real trading scenarios, but not in backtesting Twitter support depends on tweepy (https://githubcom/tweepy/tweepy) so be sure to have it installed before moving forward Contents: 81 twitter Twitter feed reference 811 Feed 82 Twitter Example This goal of this simple example is to show you how to put all the pieces together to incorporate Twitter events in a strategy We will be using Bitstamp s live feed since backtesting with Twitter is not supported so please take a look at the Bitstamp Example section before moving forward In order to connect to Twitter s API you ll need: Consumer key Consumer secret Access token Access token secret Go to http://devtwittercom and create an app The consumer key and secret will be generated for you after that Then you ll need to create an access token under the Your access token section The key things to highlight are: 1 We re using pyalgotradestrategybasestrategy instead of pyalgotradestrategybacktestingstrategy as the base class This is not a backtest 2 The pyalgotradetwitterfeedtwitterfeed instance has to be included in the strategy event dispatch loop before running the strategy 45

from pyalgotrade import strategy from pyalgotradebitstamp import barfeed from pyalgotradebitstamp import broker from pyalgotradetwitter import feed as twitterfeed class Strategy(strategyBaseStrategy): def init (self, feed, brk, twitterfeed): strategybasestrategy init (self, feed, brk) self instrument = "BTC" # Subscribe to Twitter events twitterfeedsubscribe(self ontweet) def ontweet(self, data): # Refer to https://devtwittercom/docs/streaming-apis/messages#public_stream_messages for # the information available in data try: selfinfo("twitter: %s" % (data["text"])) except KeyError: pass def onbars(self, bars): bar = bars[self instrument] selfinfo("price: %s Volume: %s" % (bargetclose(), bargetvolume())) def main(): # Go to http://devtwittercom and create an app # The consumer key and secret will be generated for you after that consumer_key = "<YOUR-CONSUMER-KEY-HERE>" consumer_secret = "<YOUR-CONSUMER-SECRET-HERE>" # After the step above, you will be redirected to your app's page # Create an access token under the the "Your access token" section access_token = "<YOUR-ACCESS-TOKEN-HERE>" access_token_secret = "<YOUR-ACCESS-TOKEN-SECRET-HERE>" # Create a twitter feed to track BitCoin related events track = ["bitcoin", "btc", "mtgox", "bitstamp", "xapo"] follow = [] languages = ["en"] twitterfeed = twitterfeedtwitterfeed(consumer_key, consumer_secret, access_token, access_token_s barfeed = barfeedlivetradefeed() brk = brokerpapertradingbroker(1000, barfeed) strat = Strategy(barFeed, brk, twitterfeed) # It is VERY important to add twitterfeed to the event dispatch loop before running the strategy stratgetdispatcher()addsubject(twitterfeed) stratrun() if name == " main ": main() The output should look like this: 46 Chapter 8 Twitter support

2014-03-15 02:42:07,696 bitstamp [INFO] Initializing client 2014-03-15 02:42:08,174 bitstamp [INFO] Connection established 2014-03-15 02:42:08,175 bitstamp [INFO] Initialization ok 2014-03-15 02:42:08,175 twitter [INFO] Initializing client 2014-03-15 02:42:09,238 twitter [INFO] Connected 2014-03-15 02:42:15,107 strategy [INFO] Twitter: Warren Buffett Urges Investors to Stay Away from B 2014-03-15 02:42:22,926 strategy [INFO] Twitter: FundingUnion Inc Bitcoin MLM Social Build - The Cu 2014-03-15 02:42:23,577 strategy [INFO] Twitter: #News Alleged Bitcoin creator denies he's the one ht 2014-03-15 02:42:29,378 strategy [INFO] Twitter: RT @Tom_Cruis3: #News Alleged Bitcoin creator denies 2014-03-15 02:42:38,012 strategy [INFO] Twitter: Need for speed dulu at BTC XXI 2014-03-15 02:42:40,162 strategy [INFO] Price: 63297 Volume: 051896614 2014-03-15 02:42:45,867 strategy [INFO] Twitter: Analysis of the leaked MTGOX database http://tco/6t 2014-03-15 02:42:46,761 strategy [INFO] Twitter: [ANN] After MtGox implosion, this is the first excha 2014-03-15 02:42:46,825 strategy [INFO] Twitter: @abatalion @naval @cdixon you guys remember CentMail 2014-03-15 02:42:47,285 strategy [INFO] Twitter: Help me convince my Dad to mine Bitcoins! http://tc 2014-03-15 02:42:47,492 strategy [INFO] Twitter: RT @VentureBeat: Warren Buffett: Bitcoin is a 'mirag 2014-03-15 02:42:47,625 strategy [INFO] Twitter: I believe in bitcoin as not only a easy exchange of 2014-03-15 02:42:47,979 strategy [INFO] Twitter: Bitcoin protocol/algorithm is being managed by a bun 2014-03-15 02:42:48,388 strategy [INFO] Twitter: Does anyone still think Dorian Nakomoto is Satoshi N 2014-03-15 02:42:53,313 strategy [INFO] Twitter: Last sale price at #Bitstamp was $63299 per #Bitcoi 2014-03-15 02:42:54,058 strategy [INFO] Twitter: Last sale price at #BTCe was $62300 per #Bitcoin - 2014-03-15 02:42:54,059 strategy [INFO] Twitter: Last sale price at #Coinbase was $63299 per #Bitcoi 2014-03-15 02:42:57,148 strategy [INFO] Twitter: GET PAID WITH USD OR BITCOIN TO PROMOTE LINKS!!! 2014-03-15 02:43:00,845 strategy [INFO] Twitter: We're back! Bitcoin expert Pete Watson explaining ju 2014-03-15 02:43:28,001 strategy [INFO] Twitter: Bitcoin's COO Explains What #Bitcoin Is http://tco/ 2014-03-15 02:43:31,888 strategy [INFO] Twitter: #BINARY-OPTIONS! and BITCOIN! Trade Area! With fanta 2014-03-15 02:43:42,148 strategy [INFO] Twitter: GET PAID WITH USD OR BITCOIN TO PROMOTE LINKS!!! 82 Twitter Example 47

48 Chapter 8 Twitter support

CHAPTER 9 TA-Lib integration The pyalgotradetalibextindicator module provides integration with Python wrapper for TA-Lib (http://mrjbq7githubcom/ta-lib/) to enable calling TA-Lib functions directly with pyalgotradedataseriesdataseries or pyalgotradedataseriesbardsbardataseries instances instead of numpy arrays If you re familiar with the talib module, then using the pyalgotradetalibextindicator module should be straightforward When using talib standalone you do something like this: import numpy import talib data = numpyrandomrandom(100) upper, middle, lower = talibbbands(data, matype=talibma_t3) To use the pyalgotradetalibextindicator module in your strategies you should do something like this: def onbars(self, bars): closeds = selfgetfeed()getdataseries("orcl")getclosedataseries() upper, middle, lower = pyalgotradetalibextindicatorbbands(closeds, 100, matype=talibma_t3) if upper!= None: print "%s" % upper[-1] Every function in the pyalgotradetalibextindicator module receives one or more dataseries (most receive just one) and the number of values to use from the dataseries In the example above, we re calculating Bollinger Bands over the last 100 closing prices If the parameter name is ds, then you should pass a regular pyalgotradedataseriesdataseries instance, like the one shown in the example above If the parameter name is bards, then you should pass a pyalgotradedataseriesbardsbardataseries instance, like in the next example: def onbars(self, bars): bards = selfgetfeed()getdataseries("orcl") sar = indicatorsar(bards, 100) if sar!= None: print "%s" % sar[-1] The following TA-Lib functions are available through the pyalgotradetalibextindicator module: 49

50 Chapter 9 TA-Lib integration

CHAPTER 10 Computational Investing Part I As I was taking the Computational Investing Part I course in 2012 I had to work on a set of assignments and for some of them I used PyAlgoTrade 101 Homework 1 For this assignment I had to pick 4 stocks, invest a total of $100000 during 2011, and calculate: Final portfolio value Anual return Average daily return Std dev of daily returns Sharpe ratio Download the data with the following commands: python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('aeti', 2011, python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('egan', 2011, python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('glng', 2011, python -c "from pyalgotradetools import yahoofinance; yahoofinancedownload_daily_bars('simo', 2011, Although the deliverable was an Excel spreadsheet, I validated the results using this piece of code: from pyalgotrade import strategy from pyalgotradebarfeed import yahoofeed from pyalgotradestratanalyzer import returns from pyalgotradestratanalyzer import sharpe from pyalgotradeutils import stats class MyStrategy(strategyBacktestingStrategy): def init (self, feed): strategybacktestingstrategy init (self, feed, 1000000) # We wan't to use adjusted close prices instead of close selfsetuseadjustedvalues(true) # Place the orders to get them processed on the first bar orders = { "aeti": 297810, 51

"egan": 81266, "glng": 11095, "simo": 17293, } for instrument, quantity in list(ordersitems()): selfmarketorder(instrument, quantity, onclose=true, allornone=true) def onbars(self, bars): pass # Load the yahoo feed from CSV files feed = yahoofeedfeed() feedaddbarsfromcsv("aeti", "aeti-2011-yahoofinancecsv") feedaddbarsfromcsv("egan", "egan-2011-yahoofinancecsv") feedaddbarsfromcsv("glng", "glng-2011-yahoofinancecsv") feedaddbarsfromcsv("simo", "simo-2011-yahoofinancecsv") # Evaluate the strategy with the feed's bars mystrategy = MyStrategy(feed) # Attach returns and sharpe ratio analyzers retanalyzer = returnsreturns() mystrategyattachanalyzer(retanalyzer) sharperatioanalyzer = sharpesharperatio() mystrategyattachanalyzer(sharperatioanalyzer) # Run the strategy mystrategyrun() # Print the results print("final portfolio value: $%2f" % mystrategygetresult()) print("anual return: %2f %%" % (retanalyzergetcumulativereturns()[-1] * 100)) print("average daily return: %2f %%" % (statsmean(retanalyzergetreturns()) * 100)) print("std dev daily return: %4f" % (statsstddev(retanalyzergetreturns()))) print("sharpe ratio: %2f" % (sharperatioanalyzergetsharperatio(0))) The results were: Final portfolio value: $160497911 Anual return: 6050 % Average daily return: 020 % Std dev daily return: 00136 Sharpe ratio: 230 102 Homework 3 and 4 For these assignments I had to build a market simulation tool that loads orders from a file, executes those, and prints the results for each day The orders file for homework 3 look like this: 2011,1,10,AAPL,Buy,1500, 2011,1,13,AAPL,Sell,1500, 2011,1,13,IBM,Buy,4000, 2011,1,26,GOOG,Buy,1000, 2011,2,2,XOM,Sell,4000, 52 Chapter 10 Computational Investing Part I

2011,2,10,XOM,Buy,4000, 2011,3,3,GOOG,Sell,1000, 2011,3,3,IBM,Sell,2200, 2011,6,3,IBM,Sell,3300, 2011,5,3,IBM,Buy,1500, 2011,6,10,AAPL,Buy,1200, 2011,8,1,GOOG,Buy,55, 2011,8,1,GOOG,Sell,55, 2011,12,20,AAPL,Sell,1200, This is the market simulation tool that I built: import csv import datetime import os from pyalgotradebarfeed import yahoofeed from pyalgotradebarfeed import csvfeed from pyalgotrade import strategy from pyalgotradeutils import stats from pyalgotradestratanalyzer import returns from pyalgotradestratanalyzer import sharpe class OrdersFile: def init (self, ordersfile): self orders = {} self firstdate = None self lastdate = None self instruments = [] # Load orders from the file reader = csvdictreader(open(ordersfile, "r"), fieldnames=["year", "month", "day", "symbol", for row in reader: datetime = datetimedatetime(int(row["year"]), int(row["month"]), int(row["day"])) self orderssetdefault(datetime, []) order = (row["symbol"], row["action"], int(row["qty"])) self orders[datetime]append(order) # As we process the file, store instruments, first date, and last date if row["symbol"] not in self instruments: self instrumentsappend(row["symbol"]) if self firstdate is None: self firstdate = datetime else: self firstdate = min(self firstdate, datetime) if self lastdate is None: self lastdate = datetime else: self lastdate = max(self lastdate, datetime) def getfirstdate(self): return self firstdate def getlastdate(self): return self lastdate 102 Homework 3 and 4 53

def getinstruments(self): return self instruments def getorders(self, datetime): return self ordersget(datetime, []) class MyStrategy(strategyBacktestingStrategy): def init (self, feed, cash, ordersfile, useadjustedclose): # Suscribe to the feed bars event before the broker just to place the orders properly feedgetnewvaluesevent()subscribe(self onbarsbeforebroker) strategybacktestingstrategy init (self, feed, cash) self ordersfile = ordersfile selfsetuseadjustedvalues(useadjustedclose) # We will allow buying more shares than cash allows selfgetbroker()setallownegativecash(true) def onbarsbeforebroker(self, datetime, bars): for instrument, action, quantity in self ordersfilegetorders(datetime): if actionlower() == "buy": selfmarketorder(instrument, quantity, onclose=true) else: selfmarketorder(instrument, quantity*-1, onclose=true) def onorderupdated(self, order): if orderiscanceled(): raise Exception("Order canceled Ran out of cash?") def onbars(self, bars): portfoliovalue = selfgetbroker()getequity() selfinfo("portfolio value: $%2f" % (portfoliovalue)) def main(): # Load the orders file ordersfile = OrdersFile("orderscsv") print("first date", ordersfilegetfirstdate()) print("last date", ordersfilegetlastdate()) print("symbols", ordersfilegetinstruments()) # Load the data from QSTK storage QS environment variable has to be defined if osgetenv("qs") is None: raise Exception("QS environment variable not defined") feed = yahoofeedfeed() feedsetbarfilter(csvfeeddaterangefilter(ordersfilegetfirstdate(), ordersfilegetlastdate())) feedsetdailybartime(datetimetime(0, 0, 0)) # This is to match the dates loaded with the ones i for symbol in ordersfilegetinstruments(): feedaddbarsfromcsv(symbol, ospathjoin(osgetenv("qs"), "QSData", "Yahoo", symbol + "csv") # Run the strategy cash = 1000000 useadjustedclose = True mystrategy = MyStrategy(feed, cash, ordersfile, useadjustedclose) # Attach returns and sharpe ratio analyzers retanalyzer = returnsreturns() mystrategyattachanalyzer(retanalyzer) sharperatioanalyzer = sharpesharperatio() 54 Chapter 10 Computational Investing Part I

main() mystrategyattachanalyzer(sharperatioanalyzer) mystrategyrun() # Print the results print("final portfolio value: $%2f" % mystrategygetresult()) print("anual return: %2f %%" % (retanalyzergetcumulativereturns()[-1] * 100)) print("average daily return: %2f %%" % (statsmean(retanalyzergetreturns()) * 100)) print("std dev daily return: %4f" % (statsstddev(retanalyzergetreturns()))) print("sharpe ratio: %2f" % (sharperatioanalyzergetsharperatio(0))) The output for homework 3 looks like this: First date 2011-01-10 00:00:00 Last date 2011-12-20 00:00:00 Symbols ['AAPL', 'IBM', 'GOOG', 'XOM'] 2011-01-10 00:00:00: Portfolio value: $100000000 2011-01-11 00:00:00: Portfolio value: $99878500 2011-01-12 00:00:00: Portfolio value: $100294000 2011-01-13 00:00:00: Portfolio value: $100481500 2011-12-15 00:00:00: Portfolio value: $111353200 2011-12-16 00:00:00: Portfolio value: $111601600 2011-12-19 00:00:00: Portfolio value: $111744400 2011-12-20 00:00:00: Portfolio value: $113386000 Final portfolio value: $113386000 Anual return: 1339 % Average daily return: 005 % Std dev daily return: 00072 Sharpe ratio: 121 102 Homework 3 and 4 55

56 Chapter 10 Computational Investing Part I

CHAPTER 11 策 略 例 子 111 动 量 交 易 1111 VWAP 动 量 交 易 本 例 基 于 : https://wwwquantopiancom/posts/momentum-trade from pyalgotrade import strategy from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradetechnical import vwap from pyalgotradestratanalyzer import sharpe class VWAPMomentum(strategyBacktestingStrategy): def init (self, feed, instrument, vwapwindowsize, threshold): strategybacktestingstrategy init (self, feed) self instrument = instrument self vwap = vwapvwap(feed[instrument], vwapwindowsize) self threshold = threshold def getvwap(self): return self vwap def onbars(self, bars): vwap = self vwap[-1] if vwap is None: return shares = selfgetbroker()getshares(self instrument) price = bars[self instrument]getclose() notional = shares * price if price > vwap * (1 + self threshold) and notional < 1000000: selfmarketorder(self instrument, 100) elif price < vwap * (1 - self threshold) and notional > 0: selfmarketorder(self instrument, -100) def main(plot): 57

instrument = "aapl" vwapwindowsize = 5 threshold = 001 # Download the bars feed = yahoofinancebuild_feed([instrument], 2011, 2012, "") strat = VWAPMomentum(feed, instrument, vwapwindowsize, threshold) sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) if plot: plt = plotterstrategyplotter(strat, True, False, True) pltgetinstrumentsubplot(instrument)adddataseries("vwap", stratgetvwap()) stratrun() print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 2013-09-21 00:01:23,813 yahoofinance [INFO] Creating data directory 2013-09-21 00:01:23,814 yahoofinance [INFO] Downloading aapl 2011 to data/aapl-2011-yahoofinancecsv 2013-09-21 00:01:25,275 yahoofinance [INFO] Downloading aapl 2012 to data/aapl-2012-yahoofinancecsv Sharpe ratio: 089 最 终 结 果 绘 制 如 下 图 所 示 : 58 Chapter 11 策 略 例 子

你 可 以 通 过 调 整 VWAP 和 参 数 阈 值 得 到 更 高 的 回 报 1112 SMA 交 叉 保 存 代 码 到 sma_crossoverpy: from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import cross class SMACrossOver(strategyBacktestingStrategy): def init (self, feed, instrument, smaperiod): strategybacktestingstrategy init (self, feed) self instrument = instrument self position = None # We'll use adjusted close values instead of regular close values selfsetuseadjustedvalues(true) self prices = feed[instrument]getpricedataseries() self sma = masma(self prices, smaperiod) def getsma(self): 111 动 量 交 易 59

return self sma def onentercanceled(self, position): self position = None def onexitok(self, position): self position = None def onexitcanceled(self, position): # If the exit was canceled, re-submit it self positionexitmarket() def onbars(self, bars): # If a position was not opened, check if we should enter a long position if self position is None: if crosscross_above(self prices, self sma) > 0: shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) # Enter a buy market order The order is good till canceled self position = selfenterlong(self instrument, shares, True) # Check if we have to exit the position elif not self positionexitactive() and crosscross_below(self prices, self sma) > 0: self positionexitmarket() 通 过 以 下 代 码 运 行 策 略 : import sma_crossover from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradestratanalyzer import sharpe def main(plot): instrument = "aapl" smaperiod = 163 # Download the bars feed = yahoofinancebuild_feed([instrument], 2011, 2012, "") strat = sma_crossoversmacrossover(feed, instrument, smaperiod) sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) if plot: plt = plotterstrategyplotter(strat, True, False, True) pltgetinstrumentsubplot(instrument)adddataseries("sma", stratgetsma()) stratrun() print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 60 Chapter 11 策 略 例 子

2013-09-21 00:01:23,813 yahoofinance [INFO] Creating data directory 2013-09-21 00:01:23,814 yahoofinance [INFO] Downloading aapl 2011 to data/aapl-2011-yahoofinancecsv 2013-09-21 00:01:25,275 yahoofinance [INFO] Downloading aapl 2012 to data/aapl-2012-yahoofinancecsv Sharpe ratio: 112 最 终 结 果 绘 制 如 下 图 所 示 : 你 可 以 通 过 调 整 sma 的 周 期 得 到 更 好 的 回 报 1113 均 线 交 叉 择 时 This example is inspired on the Market Timing / GTAA model described in: http://mebfabercom/timing-model/ http://papersssrncom/sol3/paperscfm?abstract_id=962461 The stragegy supports analyzing more than one instrument per asset class, and selects the one that has highest returns in the last month from pyalgotrade import strategy from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradetechnical import ma 111 动 量 交 易 61

from pyalgotradetechnical import cumret from pyalgotradestratanalyzer import sharpe from pyalgotradestratanalyzer import returns class MarketTiming(strategyBacktestingStrategy): def init (self, feed, instrumentsbyclass, initialcash): strategybacktestingstrategy init (self, feed, initialcash) selfsetuseadjustedvalues(true) self instrumentsbyclass = instrumentsbyclass self rebalancemonth = None self sharestobuy = {} # Initialize indicators for each instrument self sma = {} for assetclass in instrumentsbyclass: for instrument in instrumentsbyclass[assetclass]: priceds = feed[instrument]getpricedataseries() self sma[instrument] = masma(priceds, 200) def _shouldrebalance(self, datetime): return datetimemonth!= self rebalancemonth def _getrank(self, instrument): # If the price is below the SMA, then this instrument doesn't rank at # all smas = self sma[instrument] price = selfgetlastprice(instrument) if len(smas) == 0 or smas[-1] is None or price < smas[-1]: return None # Rank based on 20 day returns ret = None lookback = 20 priceds = selfgetfeed()[instrument]getpricedataseries() if len(priceds) >= lookback and smas[-1] is not None and smas[-1*lookback] is not None: ret = (priceds[-1] - priceds[-1*lookback]) / float(priceds[-1*lookback]) return ret def _gettopbyclass(self, assetclass): # Find the instrument with the highest rank ret = None highestrank = None for instrument in self instrumentsbyclass[assetclass]: rank = self_getrank(instrument) if rank is not None and (highestrank is None or rank > highestrank): highestrank = rank ret = instrument return ret def _gettop(self): ret = {} for assetclass in self instrumentsbyclass: ret[assetclass] = self_gettopbyclass(assetclass) return ret def _placependingorders(self): remainingcash = selfgetbroker()getcash() * 09 # Use less chash just in case price changes 62 Chapter 11 策 略 例 子

for instrument in self sharestobuy: ordersize = self sharestobuy[instrument] if ordersize > 0: # Adjust the order size based on available cash lastprice = selfgetlastprice(instrument) cost = ordersize * lastprice while cost > remainingcash and ordersize > 0: ordersize -= 1 cost = ordersize * lastprice if ordersize > 0: remainingcash -= cost assert(remainingcash >= 0) if ordersize!= 0: selfinfo("placing market order for %d %s shares" % (ordersize, instrument)) selfmarketorder(instrument, ordersize, goodtillcanceled=true) self sharestobuy[instrument] -= ordersize def _logpossize(self): totalequity = selfgetbroker()getequity() positions = selfgetbroker()getpositions() for instrument in selfgetbroker()getpositions(): possize = positions[instrument] * selfgetlastprice(instrument) / totalequity * 100 selfinfo("%s - %02f %%" % (instrument, possize)) def _rebalance(self): selfinfo("rebalancing") # Cancel all active/pending orders for order in selfgetbroker()getactiveorders(): selfgetbroker()cancelorder(order) cashperassetclass = selfgetbroker()getequity() / float(len(self instrumentsbyclass)) self sharestobuy = {} # Calculate which positions should be open during the next period topbyclass = self_gettop() for assetclass in topbyclass: instrument = topbyclass[assetclass] selfinfo("best for class %s: %s" % (assetclass, instrument)) if instrument is not None: lastprice = selfgetlastprice(instrument) cashforinstrument = cashperassetclass - selfgetbroker()getshares(instrument) * last # This may yield a negative value and we have to reduce this # position self sharestobuy[instrument] = int(cashforinstrument / lastprice) # Calculate which positions should be closed for instrument in selfgetbroker()getpositions(): if instrument not in list(topbyclassvalues()): currentshares = selfgetbroker()getshares(instrument) assert(instrument not in self sharestobuy) self sharestobuy[instrument] = currentshares * -1 def getsma(self, instrument): return self sma[instrument] def onbars(self, bars): 111 动 量 交 易 63

currentdatetime = barsgetdatetime() if self_shouldrebalance(currentdatetime): self rebalancemonth = currentdatetimemonth self_rebalance() self_placependingorders() def main(plot): initialcash = 10000 instrumentsbyclass = { "US Stocks": ["VTI"], "Foreign Stocks": ["VEU"], "US 10 Year Government Bonds": ["IEF"], "Real Estate": ["VNQ"], "Commodities": ["DBC"], } # Download the bars instruments = ["SPY"] for assetclass in instrumentsbyclass: instrumentsextend(instrumentsbyclass[assetclass]) feed = yahoofinancebuild_feed(instruments, 2007, 2013, "data", skiperrors=true) strat = MarketTiming(feed, instrumentsbyclass, initialcash) sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) returnsanalyzer = returnsreturns() stratattachanalyzer(returnsanalyzer) if plot: plt = plotterstrategyplotter(strat, False, False, True) pltgetorcreatesubplot("cash")addcallback("cash", lambda x: stratgetbroker()getcash()) # Plot strategy vs SPY cumulative returns pltgetorcreatesubplot("returns")adddataseries("spy", cumretcumulativereturn(feed["spy"]ge pltgetorcreatesubplot("returns")adddataseries("strategy", returnsanalyzergetcumulativeretu stratrun() print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) print("returns: %2f %%" % (returnsanalyzergetcumulativereturns()[-1] * 100)) if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 2014-05-25 22:36:59,740 yahoofinance [INFO] Creating data directory 2014-05-25 22:36:59,740 yahoofinance [INFO] Downloading SPY 2007 to data/spy-2007-yahoofinancecsv 2014-05-25 22:37:06,332 yahoofinance [INFO] Downloading VTI 2007 to data/vti-2007-yahoofinancecsv 2014-05-25 22:37:10,666 yahoofinance [INFO] Downloading DBC 2007 to data/dbc-2007-yahoofinancecsv 2014-05-25 22:37:13,102 yahoofinance [INFO] Downloading IEF 2007 to data/ief-2007-yahoofinancecsv 2014-05-25 22:37:19,394 yahoofinance [INFO] Downloading VEU 2007 to data/veu-2007-yahoofinancecsv 2014-05-25 22:37:27,378 yahoofinance [INFO] Downloading VNQ 2007 to data/vnq-2007-yahoofinancecsv 2014-05-25 22:37:32,771 yahoofinance [INFO] Downloading SPY 2008 to data/spy-2008-yahoofinancecsv 64 Chapter 11 策 略 例 子

2014-05-25 22:37:38,326 yahoofinance [INFO] Downloading VTI 2008 to data/vti-2008-yahoofinancecsv 2014-05-25 22:37:42,262 yahoofinance [INFO] Downloading DBC 2008 to data/dbc-2008-yahoofinancecsv 2013-12-02 00:00:00 strategy [INFO] Rebalancing 2013-12-02 00:00:00 strategy [INFO] Best for class US Stocks: VTI 2013-12-02 00:00:00 strategy [INFO] Best for class Commodities: None 2013-12-02 00:00:00 strategy [INFO] Best for class US 10 Year Government Bonds: None 2013-12-02 00:00:00 strategy [INFO] Best for class Foreign Stocks: VEU 2013-12-02 00:00:00 strategy [INFO] Best for class Real Estate: None 2013-12-02 00:00:00 strategy [INFO] Placing market order for -1 VTI shares 2013-12-02 00:00:00 strategy [INFO] Placing market order for -39 VNQ shares Sharpe ratio: -006 Returns: 3297 % 最 终 结 果 绘 制 如 下 图 所 示 : 111 动 量 交 易 65

112 均 值 回 归 1121 Ernie Chan s 黄 金 vs 黄 金 矿 工 本 例 基 于 : http://epchanblogspotcomar/2006/11/gold-vs-gold-miners-another-arbitragehtml https://wwwquantopiancom/posts/ernie-chans-gold-vs-gold-miners-stat-arb from pyalgotrade import strategy from pyalgotrade import dataseries from pyalgotradedataseries import aligned from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradestratanalyzer import sharpe import numpy as np import statsmodelsapi as sm def get_beta(values1, values2): # http://statsmodelssourceforgenet/stable/regressionhtml model = smols(values1, values2) results = modelfit() return resultsparams[0] class StatArbHelper: def init (self, ds1, ds2, windowsize): # We're going to use datetime aligned versions of the dataseries self ds1, self ds2 = aligneddatetime_aligned(ds1, ds2) self windowsize = windowsize self hedgeratio = None self spread = None self spreadmean = None self spreadstd = None self zscore = None def getspread(self): return self spread def getspreadmean(self): return self spreadmean def getspreadstd(self): return self spreadstd def getzscore(self): return self zscore def gethedgeratio(self): return self hedgeratio def updatehedgeratio(self, values1, values2): self hedgeratio = get_beta(values1, values2) def updatespreadmeanandstd(self, values1, values2): 66 Chapter 11 策 略 例 子

if self hedgeratio is not None: spread = values1 - values2 * self hedgeratio self spreadmean = spreadmean() self spreadstd = spreadstd(ddof=1) def updatespread(self): if self hedgeratio is not None: self spread = self ds1[-1] - self hedgeratio * self ds2[-1] def updatezscore(self): if self spread is not None and self spreadmean is not None and self spreadstd is not No self zscore = (self spread - self spreadmean) / float(self spreadstd) def update(self): if len(self ds1) >= self windowsize: values1 = npasarray(self ds1[-1*self windowsize:]) values2 = npasarray(self ds2[-1*self windowsize:]) self updatehedgeratio(values1, values2) self updatespread() self updatespreadmeanandstd(values1, values2) self updatezscore() class StatArb(strategyBacktestingStrategy): def init (self, feed, instrument1, instrument2, windowsize): strategybacktestingstrategy init (self, feed) selfsetuseadjustedvalues(true) self statarbhelper = StatArbHelper(feed[instrument1]getAdjCloseDataSeries(), feed[instrume self i1 = instrument1 self i2 = instrument2 # These are used only for plotting purposes self spread = dataseriessequencedataseries() self hedgeratio = dataseriessequencedataseries() def getspreadds(self): return self spread def gethedgeratiods(self): return self hedgeratio def getordersize(self, bars, hedgeratio): cash = selfgetbroker()getcash(false) price1 = bars[self i1]getadjclose() price2 = bars[self i2]getadjclose() size1 = int(cash / (price1 + hedgeratio * price2)) size2 = int(size1 * hedgeratio) return (size1, size2) def buyspread(self, bars, hedgeratio): amount1, amount2 = self getordersize(bars, hedgeratio) selfmarketorder(self i1, amount1) selfmarketorder(self i2, amount2 * -1) def sellspread(self, bars, hedgeratio): amount1, amount2 = self getordersize(bars, hedgeratio) selfmarketorder(self i1, amount1 * -1) selfmarketorder(self i2, amount2) 112 均 值 回 归 67

def reduceposition(self, instrument): currentpos = selfgetbroker()getshares(instrument) if currentpos > 0: selfmarketorder(instrument, currentpos * -1) elif currentpos < 0: selfmarketorder(instrument, currentpos * -1) def onbars(self, bars): self statarbhelperupdate() # These is used only for plotting purposes self spreadappendwithdatetime(barsgetdatetime(), self statarbhelpergetspread()) self hedgeratioappendwithdatetime(barsgetdatetime(), self statarbhelpergethedgeratio() if barsgetbar(self i1) and barsgetbar(self i2): hedgeratio = self statarbhelpergethedgeratio() zscore = self statarbhelpergetzscore() if zscore is not None: currentpos = abs(selfgetbroker()getshares(self i1)) + abs(selfgetbroker()getsha if abs(zscore) <= 1 and currentpos!= 0: selfreduceposition(self i1) selfreduceposition(self i2) elif zscore <= -2 and currentpos == 0: # Buy spread when its value drops below 2 sta selfbuyspread(bars, hedgeratio) elif zscore >= 2 and currentpos == 0: # Short spread when its value rises above 2 st selfsellspread(bars, hedgeratio) def main(plot): instruments = ["gld", "gdx"] windowsize = 50 # Download the bars feed = yahoofinancebuild_feed(instruments, 2006, 2012, "") strat = StatArb(feed, instruments[0], instruments[1], windowsize) sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) if plot: plt = plotterstrategyplotter(strat, False, False, True) pltgetorcreatesubplot("hedge")adddataseries("hedge Ratio", stratgethedgeratiods()) pltgetorcreatesubplot("spread")adddataseries("spread", stratgetspreadds()) stratrun() print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 2013-09-21 00:02:35,136 yahoofinance [INFO] Creating data directory 2013-09-21 00:02:35,136 yahoofinance [INFO] Downloading gld 2006 to data/gld-2006-yahoofinancecsv 68 Chapter 11 策 略 例 子

2013-09-21 00:02:35,899 yahoofinance [INFO] Downloading gdx 2006 to data/gdx-2006-yahoofinancecsv 2013-09-21 00:02:36,637 yahoofinance [INFO] Downloading gld 2007 to data/gld-2007-yahoofinancecsv 2013-09-21 00:02:37,265 yahoofinance [INFO] Downloading gdx 2007 to data/gdx-2007-yahoofinancecsv 2013-09-21 00:02:37,881 yahoofinance [INFO] Downloading gld 2008 to data/gld-2008-yahoofinancecsv 2013-09-21 00:02:38,462 yahoofinance [INFO] Downloading gdx 2008 to data/gdx-2008-yahoofinancecsv 2013-09-21 00:02:39,243 yahoofinance [INFO] Downloading gld 2009 to data/gld-2009-yahoofinancecsv 2013-09-21 00:02:39,996 yahoofinance [INFO] Downloading gdx 2009 to data/gdx-2009-yahoofinancecsv 2013-09-21 00:02:40,577 yahoofinance [INFO] Downloading gld 2010 to data/gld-2010-yahoofinancecsv 2013-09-21 00:02:42,630 yahoofinance [INFO] Downloading gdx 2010 to data/gdx-2010-yahoofinancecsv 2013-09-21 00:02:43,397 yahoofinance [INFO] Downloading gld 2011 to data/gld-2011-yahoofinancecsv 2013-09-21 00:02:44,153 yahoofinance [INFO] Downloading gdx 2011 to data/gdx-2011-yahoofinancecsv 2013-09-21 00:02:44,901 yahoofinance [INFO] Downloading gld 2012 to data/gld-2012-yahoofinancecsv 2013-09-21 00:02:45,965 yahoofinance [INFO] Downloading gdx 2012 to data/gdx-2012-yahoofinancecsv Sharpe ratio: -020 最 终 结 果 绘 制 如 下 图 所 示 : You can get better returns by tunning the window size as well as the entry and exit values for the z-score 1122 布 林 带 This example is based on: http://wwwinvestopediacom/articles/trading/07/bollingerasp 112 均 值 回 归 69

from pyalgotrade import strategy from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradetechnical import bollinger from pyalgotradestratanalyzer import sharpe class BBands(strategyBacktestingStrategy): def init (self, feed, instrument, bbandsperiod): strategybacktestingstrategy init (self, feed) self instrument = instrument self bbands = bollingerbollingerbands(feed[instrument]getclosedataseries(), bbandsperiod, def getbollingerbands(self): return self bbands def onbars(self, bars): lower = self bbandsgetlowerband()[-1] upper = self bbandsgetupperband()[-1] if lower is None: return shares = selfgetbroker()getshares(self instrument) bar = bars[self instrument] if shares == 0 and bargetclose() < lower: sharestobuy = int(selfgetbroker()getcash(false) / bargetclose()) selfmarketorder(self instrument, sharestobuy) elif shares > 0 and bargetclose() > upper: selfmarketorder(self instrument, -1*shares) def main(plot): instrument = "yhoo" bbandsperiod = 40 # Download the bars feed = yahoofinancebuild_feed([instrument], 2011, 2012, "") strat = BBands(feed, instrument, bbandsperiod) sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) if plot: plt = plotterstrategyplotter(strat, True, True, True) pltgetinstrumentsubplot(instrument)adddataseries("upper", stratgetbollingerbands()getuppe pltgetinstrumentsubplot(instrument)adddataseries("middle", stratgetbollingerbands()getmid pltgetinstrumentsubplot(instrument)adddataseries("lower", stratgetbollingerbands()getlowe stratrun() print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) if plot: pltplot() if name == " main ": main(true) 70 Chapter 11 策 略 例 子

本 例 输 出 结 果 如 下 : 2013-09-21 00:06:07,740 yahoofinance [INFO] Creating data directory 2013-09-21 00:06:07,741 yahoofinance [INFO] Downloading yhoo 2011 to data/yhoo-2011-yahoofinancecsv 2013-09-21 00:06:09,621 yahoofinance [INFO] Downloading yhoo 2012 to data/yhoo-2012-yahoofinancecsv Sharpe ratio: 071 最 终 结 果 绘 制 如 下 图 所 示 : You can get better returns by tunning the Bollinger Bands period as well as the entry and exit points 1123 RSI2 This example is based on a strategy known as RSI2 (http://stockchartscom/school/dokuphp?id=chart_school:trading_strategies:rsi2) which requires the following parameters: An SMA period for trend identification We ll call this entrysma A smaller SMA period for the exit point We ll call this exitsma An RSI period for entering both short/long positions We ll call this rsiperiod An RSI oversold threshold for long position entry We ll call this oversoldthreshold An RSI overbought threshold for short position entry We ll call this overboughtthreshold 112 均 值 回 归 71

Save this code as rsi2py: from pyalgotrade import strategy from pyalgotradetechnical import ma from pyalgotradetechnical import rsi from pyalgotradetechnical import cross class RSI2(strategyBacktestingStrategy): def init (self, feed, instrument, entrysma, exitsma, rsiperiod, overboughtthreshold, oversoldt strategybacktestingstrategy init (self, feed) self instrument = instrument # We'll use adjusted close values, if available, instead of regular close values if feedbarshaveadjclose(): selfsetuseadjustedvalues(true) self priceds = feed[instrument]getpricedataseries() self entrysma = masma(self priceds, entrysma) self exitsma = masma(self priceds, exitsma) self rsi = rsirsi(self priceds, rsiperiod) self overboughtthreshold = overboughtthreshold self oversoldthreshold = oversoldthreshold self longpos = None self shortpos = None def getentrysma(self): return self entrysma def getexitsma(self): return self exitsma def getrsi(self): return self rsi def onentercanceled(self, position): if self longpos == position: self longpos = None elif self shortpos == position: self shortpos = None else: assert(false) def onexitok(self, position): if self longpos == position: self longpos = None elif self shortpos == position: self shortpos = None else: assert(false) def onexitcanceled(self, position): # If the exit was canceled, re-submit it positionexitmarket() def onbars(self, bars): # Wait for enough bars to be available to calculate SMA and RSI if self exitsma[-1] is None or self entrysma[-1] is None or self rsi[-1] is None: return bar = bars[self instrument] 72 Chapter 11 策 略 例 子

if self longpos is not None: if selfexitlongsignal(): self longposexitmarket() elif self shortpos is not None: if selfexitshortsignal(): self shortposexitmarket() else: if selfenterlongsignal(bar): shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) self longpos = selfenterlong(self instrument, shares, True) elif selfentershortsignal(bar): shares = int(selfgetbroker()getcash() * 09 / bars[self instrument]getprice()) self shortpos = selfentershort(self instrument, shares, True) def enterlongsignal(self, bar): return bargetprice() > self entrysma[-1] and self rsi[-1] <= self oversoldthreshold def exitlongsignal(self): return crosscross_above(self priceds, self exitsma) and not self longposexitactive() def entershortsignal(self, bar): return bargetprice() < self entrysma[-1] and self rsi[-1] >= self overboughtthreshold def exitshortsignal(self): return crosscross_below(self priceds, self exitsma) and not self shortposexitactive() and use the following code to execute the strategy: import rsi2 from pyalgotrade import plotter from pyalgotradetools import yahoofinance from pyalgotradestratanalyzer import sharpe def main(plot): instrument = "DIA" entrysma = 200 exitsma = 5 rsiperiod = 2 overboughtthreshold = 90 oversoldthreshold = 10 # Download the bars feed = yahoofinancebuild_feed([instrument], 2009, 2012, "") strat = rsi2rsi2(feed, instrument, entrysma, exitsma, rsiperiod, overboughtthreshold, oversoldth sharperatioanalyzer = sharpesharperatio() stratattachanalyzer(sharperatioanalyzer) if plot: plt = plotterstrategyplotter(strat, True, False, True) pltgetinstrumentsubplot(instrument)adddataseries("entry SMA", stratgetentrysma()) pltgetinstrumentsubplot(instrument)adddataseries("exit SMA", stratgetexitsma()) pltgetorcreatesubplot("rsi")adddataseries("rsi", stratgetrsi()) pltgetorcreatesubplot("rsi")addline("overbought", overboughtthreshold) pltgetorcreatesubplot("rsi")addline("oversold", oversoldthreshold) stratrun() 112 均 值 回 归 73

print("sharpe ratio: %2f" % sharperatioanalyzergetsharperatio(005)) if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 2014-05-03 13:49:35,354 yahoofinance [INFO] Downloading DIA 2009 to /DIA-2009-yahoofinancecsv 2014-05-03 13:49:36,388 yahoofinance [INFO] Downloading DIA 2010 to /DIA-2010-yahoofinancecsv 2014-05-03 13:49:36,900 yahoofinance [INFO] Downloading DIA 2011 to /DIA-2011-yahoofinancecsv 2014-05-03 13:49:37,457 yahoofinance [INFO] Downloading DIA 2012 to /DIA-2012-yahoofinancecsv Sharpe ratio: -011 最 终 结 果 绘 制 如 下 图 所 示 : You can get better returns by tunning the different parameters 74 Chapter 11 策 略 例 子

113 其 他 1131 Quandl 合 集 The purpose of this example is to show how to integrate price data along with any time-series data in CSV format from Quandl into a strategy We ll use the following CSV data from Quandl: http://wwwquandlcom/ofdp-open-financial-data- Project/GOLD_2-LBMA-Gold-Price-London-Fixings-P-M from pyalgotrade import strategy from pyalgotrade import plotter from pyalgotradetools import quandl from pyalgotradefeed import csvfeed import datetime class MyStrategy(strategyBacktestingStrategy): def init (self, feed, quandlfeed, instrument): strategybacktestingstrategy init (self, feed) selfsetuseadjustedvalues(true) self instrument = instrument # It is VERY important to add the the extra feed to the event dispatch loop before # running the strategy selfgetdispatcher()addsubject(quandlfeed) # Subscribe to events from the Quandl feed quandlfeedgetnewvaluesevent()subscribe(selfonquandldata) def onquandldata(self, datetime, values): selfinfo(values) def onbars(self, bars): selfinfo(bars[self instrument]getadjclose()) def main(plot): instruments = ["GORO"] # Download GORO bars using WIKI source code feed = quandlbuild_feed("wiki", instruments, 2006, 2012, "") # Load Quandl CSV downloaded from http://wwwquandlcom/ofdp-open-financial-data-project/gold_2-l quandlfeed = csvfeedfeed("date", "%Y-%m-%d") quandlfeedsetdaterange(datetimedatetime(2006, 1, 1), datetimedatetime(2012, 12, 31)) quandlfeedaddvaluesfromcsv("quandl_gold_2csv") mystrategy = MyStrategy(feed, quandlfeed, instruments[0]) if plot: plt = plotterstrategyplotter(mystrategy, True, False, False) pltgetorcreatesubplot("quandl")adddataseries("usd", quandlfeed["usd"]) pltgetorcreatesubplot("quandl")adddataseries("eur", quandlfeed["eur"]) pltgetorcreatesubplot("quandl")adddataseries("gbp", quandlfeed["gbp"]) mystrategyrun() 113 其 他 75

if plot: pltplot() if name == " main ": main(true) 本 例 输 出 结 果 如 下 : 2006-01-01 00:00:00 strategy [INFO] {'USD': 5130, 'GBP': 298204, 'EUR': 433533} 2006-01-08 00:00:00 strategy [INFO] {'USD': 53525, 'GBP': 302572, 'EUR': 440173} 2006-01-15 00:00:00 strategy [INFO] {'USD': 54825, 'GBP': 309781, 'EUR': 454489} 2006-01-22 00:00:00 strategy [INFO] {'USD': 56725, 'GBP': 321152, 'EUR': 468802} 2006-01-29 00:00:00 strategy [INFO] {'USD': 56175, 'GBP': 315147, 'EUR': 460526} 2006-02-05 00:00:00 strategy [INFO] {'USD': 5690, 'GBP': 322562, 'EUR': 474167} 2006-02-12 00:00:00 strategy [INFO] {'USD': 5570, 'GBP': 317198, 'EUR': 46378} 2006-02-19 00:00:00 strategy [INFO] {'USD': 5517, 'GBP': 317251, 'EUR': 463224} 2006-02-26 00:00:00 strategy [INFO] {'USD': 55415, 'GBP': 316838, 'EUR': 465555} 2006-03-05 00:00:00 strategy [INFO] {'USD': 5650, 'GBP': 322029, 'EUR': 469854} 2012-12-19 00:00:00 strategy [INFO] 1543 2012-12-20 00:00:00 strategy [INFO] 1539 2012-12-21 00:00:00 strategy [INFO] 1535 2012-12-23 00:00:00 strategy [INFO] {'USD': 16515, 'GBP': 1019256, 'EUR': 1253701} 2012-12-24 00:00:00 strategy [INFO] 152 2012-12-26 00:00:00 strategy [INFO] 1556 2012-12-27 00:00:00 strategy [INFO] 1524 2012-12-28 00:00:00 strategy [INFO] 1509 2012-12-30 00:00:00 strategy [INFO] {'USD': 16575, 'GBP': 1027206, 'EUR': 1253024} 2012-12-31 00:00:00 strategy [INFO] 1541 最 终 结 果 绘 制 如 下 图 所 示 : 76 Chapter 11 策 略 例 子

113 其 他 77

78 Chapter 11 策 略 例 子

CHAPTER 12 Indices and tables genindex modindex search 79

80 Chapter 12 Indices and tables

Python 模 块 索 引 p pyalgotradebroker, 21 pyalgotrademarketsession, 26 pyalgotradestratanalyzer, 22 pyalgotradestratanalyzerdrawdown, 22 81

82 Python 模 块 索 引

索 引 B BOVESPA (pyalgotrademarketsession 中 的 类 ), 26 D DrawDown (pyalgotradestratanalyzerdrawdown 中 的 类 ), 22 F FTSE (pyalgotrademarketsession 中 的 类 ), 26 G getlongestdrawdownduration() (pyalgotradestratanalyzerdrawdowndrawdown 方 法 ), 22 getmaxdrawdown() (pyalgotradestratanalyzerdrawdowndrawdown 方 法 ), 23 gettimezone() (pyalgotrademarketsessionmarketsession 类 方 法 ), 26 M MarketSession (pyalgotrademarketsession 中 的 类 ), 26 MERVAL (pyalgotrademarketsession 中 的 类 ), 26 N NASDAQ (pyalgotrademarketsession 中 的 类 ), 26 NYSE (pyalgotrademarketsession 中 的 类 ), 26 P pyalgotradebroker ( 模 块 ), 21 pyalgotrademarketsession ( 模 块 ), 26 pyalgotradestratanalyzer ( 模 块 ), 22 pyalgotradestratanalyzerdrawdown ( 模 块 ), 22 S StrategyAnalyzer (pyalgotradestratanalyzer 中 的 类 ), 22 T TSE (pyalgotrademarketsession 中 的 类 ), 26 U USEquities (pyalgotrademarketsession 中 的 类 ), 26 83