一、RQalpha

github 地址  https://github.com/ricequant/rqalpha

1、运行test.py文件,显示 No module named 'logbook.base'。

解决先卸载再安装: pip uninstall logbook   pip install logbook

2、出现:RuntimeError: 请设置账户及初始资金。

解决:

二、Zipline

github地址  https://github.com/quantopian/zipline     Zipline学习资料      http://www.zipline.io/

zipline代码比较多,不好复制

三、qstrade

github地址  https://github.com/mhallsmoore/qstrader

听从朋友建议,暂时学习qstrade。代码少,上手快。

测试:

Could not subscribe ticker SPY as no data CSV found for pricing.
Could not subscribe ticker AGG as no data CSV found for pricing.
Traceback (most recent call last):
File "E:/qstrader-master/examples/monthly_liquidate_rebalance_backtest.py", line 108, in <module>
run(config, testing, tickers, filename)
File "E:/qstrader-master/examples/monthly_liquidate_rebalance_backtest.py", line 94, in run

在策略代码后面添加如下即可:

import os
from munch import munchify
os.chdir('E:\\qstrader-master')
config = munchify({"CSV_DATA_DIR": "data", "OUTPUT_DIR": "out", 'testing': True})

保存图片需要修改:

将trading_session.py文件最后部分,倒数第二行self.statistics.plot_results()-------->self.statistics.save()

将tearsheet.py文件最后部分,self.plot_results()---------->self.plot_results(filename)

学习问题汇总:

1、运行buy_and_hold_backtest.py文件时出错。

File "E:\qstrader-master\qstrader\price_handler\yahoo_daily_csv_bar.py", line 68, in _merge_sort_ticker_data
df = pd.concat(self.tickers_data.values()).sort_index()
File "D:\Anaconda3\lib\site-packages\pandas\core\reshape\concat.py", line 206, in concat
copy=copy)
File "D:\Anaconda3\lib\site-packages\pandas\core\reshape\concat.py", line 239, in __init__
raise ValueError('No objects to concatenate')
ValueError: No objects to concatenate

1、self.tickers_data 是什么内容?-->self.csv_dir=csv_dir,csv_dir在哪里?-->

2、出错位置在:backtest=TradingSession(config,strategy,tickers,initial_equity,start_date,end_date,events_queue,title=title)  为实例化,

参数如下:

config -->    TEST==munchify({"CSV_DATA_DIR":"data","OUTPUT_DIR":"out"})

strategy -->实例化,BuyAndHoldStrategy(tickers[0],events_queue)

tickers -->['SPY']

initial_equity -->10000

start_date -->datetime.datetime(2000,1,1)

end_date -->datetime.datetime(2014,1,1)

events_queue -->queue.Queue()

title -->['Buy and Hold Example on SPY']

进一步查看,错误地点在 qstrader\trading_session.py ,交易阶段。为什么/如何  进入这一阶段?

还是要细心看代码,在__init__下面有self._config_session(),启动了该方法。功能是初始化回测期间必须类。

-->   def _config_session(self):    -->self.price_handler=YahooDailyCsvBarPriceHandler(self.config.CSV_DATA_DIR,self.events_queue,self.tickers,

start_date=self.start_date,end_date=self.end_date)

“”“设计去读取csv文件、开-高-低-收-交易量(OHLCV),for each requested financial instrument and stream those to the provided events queue as BarEvents"""

-->self.subscribe_ticker(ticker)  订阅ticker  -->订阅之前 self._open_ticker_price_csv(ticker)

-->self._merge_sort_ticker_data() 合并ticker

debug后,发现代码错误位于读取SPY.csv文件部分。代码如下:

ticker_path=os.path.join(self.csv_dir,'%s.csv"%ticker)

self.tickers_data[ticker]=pd.io.parser.read_csv(ticker_path,....) ,好奇怪,这里debug下一步,ticker_path没有了。

进一步发现是路径的问题,buy_and_hold_backtest.py是在examples路径下面,即:

import os
os.getcwd()
Out[7]: 'E:\\qstrader-master\\examples'

而文件在路径为:'E:\qstrader-master\data',ticker_path='data\\SPY.csv'。

即文件读取位置为:'E:\qstrader-master\examples\data\SPY.csv' ,所有读取不到文件,可以如下修改:

一、可以在buy_and_hold_backtest.py 文件中加入: import os  os.chdir('E:\\qstrader-master')

二、在python console中运行buy_and_hold_backtest.py文件, run examples\buy_and_hold_backtest.py

run examples\buy_and_hold_backtest.py
Backend Qt5Agg is interactive backend. Turning interactive mode on.
ticker: SPY self.tickers: {}
ticker_path: data\SPY.csv
Running Backtest...
---------------------------------
Backtest complete.
Sharpe Ratio: 0.25
Max Drawdown: 79.59%

2、后面debug过程中,循环的流程为:

trading_session.py  只有一个类:TradingSession(object)

注意:在 __init__时,运行了self._config_session()

方法有4个:1、 _config_session(self):   2、_continue_loop_condition(self) 3、_run_session(self) 4、start_trading(self,testing=False)

在buy_and_hold_backtest.py中先TradingSession实例化,再运行start_trading方法,流程如下

self._config_session()---->

self.start_trading()----->self._run_session()---->self._continue_loop_condition

运行到方法3、_run_session(self)中

while self._continue_loop_condition():

  try:

    event=self.events_queue.get(False)

  except queue.Empty:

    self.price_handler.stream_next()

  else:

    if event is not None:

      if (event.type==EventType.TICK or event.type==EventType.BAR): 。。。。

#dir(event)  -->有6个属性  action print_order quantity ticker type typename  --->event.type=EventType.ORDER,不满足条件进入elif

      elif event.type==EventType.ORDER:

        self.execution_handler.execute_order(event)

        

---->

def _continue_loop_condition(self):

  if self.session_type=='backtest':

    return self.price_handler.continue_backtest

  else:

    return datetime.now()<self.end_session_time

查看可知:self.price_handler来自:qstrader.price_handler.yahoo_daily_csv_bar.YahooDailyCsvBarPriceHandler object

debug   self.price_handler.continue_backtest 为True,继续,

-->

self.execution_handler.execute_order(event)  来自于:qstrader/execution_handler/ib_simulated.py

def execute_order(self,event):

  if event.type==EventType.ORDER:

    #obtain values from the OrderEvent

    timestamp=self.price_handler.get_last_timestamp(event.ticker)

    ticker=event.ticker

    action=event.action

    quantity=event.quantity

    #obtain the fill price

    if self.price_handler.istick():

      bid,ask=self.price_handler.get_best_bid_ask(ticker)

      fill_price=ask if event.action=="BOT" elst bid

    else:

      close_price=self.price_handler.get_last_close(ticker)

      fill_price=close_price

    #set a dummy exchange and calculate trade commission    dummy:模拟交易

    exchange='ARCA'

    commission=self.calculate_ib_commission(quantity,fill_price) 

    #create the FillEvent and place on the events queue

    fill_event=FillEvent(timestamp,ticker,action,quantity,exchange,fill_price,commission)    #实例化FillEvent类  

    self.events_queue.put(fill_event)

    if self.compliance is not None:

      self.compliance.record_trade(fill_event)

---->

self.price_handler  来自于:qstrader/price_handler/base.py ,包含三个类:

1、AbstractPriceHandler(object) 类   2、AbstractTickPriceHandler(AbstractPriceHandler) 类    3、AbstractBarPriceHandler(AbstractPriceHandler)类

self.price_handler.get_last_timestamp()为AbstractPriceHandler(object)类中方法,

def get_last_timestamp(self,ticker):

  if ticker in self.tickers:

    timestamp=self.tickers[ticker]["timestamp"]

    return timestamp

self.price_handler.istick()  类:AbstractBarPriceHandler(AbstractPriceHandler)    方法:istick

def istick(self):

  return False

def isbar(self):

  return True

self.price_handler.get_last_close(self,ticker) 为 AbstractBarPriceHandler(AbstractPriceHandler)类中的方法

def get_last_clost(self,ticker):  #ticker:‘SPY’

  if ticker in self.tickers:    #self.tickers为:{'SPY': {'close': 1454375000, 'adj_close': 1058253320, 'timestamp': Timestamp('2000-01-03 00:00:00')}}

    close_price=self.tickers[ticker]['close']  #self.tickers[ticker]为字典

    return close_price  #在ib_similated.py中得到close_price

----->

ticker=event.ticker   action=event.action  event来自于: qstrader.event.OrderEvent  位置 qstrader/event.py

event.py内容如下:总共有7个类,一个基础类Event。

from enum import Enum

EventType=Enum("EventType","TICK BAR SIGNAL ORDER FILL SENTIMENT")

1、Event(object): 2、TickEvent(Event) 3、BarEvent(Event) 4、SignalEvent(Event) 5、OrderEvent(Event) 6、FillEvent(Event) 7、SentimentEvent(Event)

fill_event=FillEvent(timestamp,ticker,action,quantity,exchange,fill_price,commission)

干的活不多,就只是进入类,带入属性值

---->

self.calculate_ib_commission(quantity,fill_price) 来自于:execution_handler/ib_simulated.py

包含一个类:IBSimulatedExecutionHandler(AbstractExecutionHandler)

def __init__(self,events_queue,price_handler,compliance=None):

def calculate_ib_commission(self,quantity,fill_price):

  commission=min(0.5*fill_price*quantity,max(1.0,0.005*quantity))

  return PriceParser.parse(commission)

----->

self.compliance.record_trade(fill_event) 来自于:qstrader/compliance/example.py文件,只有一个类

from .base import AbstractCompliance   来自于:qstrader/compliance/base.py  没啥内容

类:ExampleCompliance(AbstractCompliance)   #

保存交易记录的

3、debug查看运行流程

一、交易之前创建投资过清单。

run(config,testing,tickers,filename)---->

title/initial_equity/start_date/end_date/events_queue/strategy,加载策略类(MonthlyLiquidateRebalanceStrategy(tickers,events_queue)----->

tickers_invested:{'SPY': False, 'AGG': False},初始化投资过的股票代码,----->

ticker_weights/position_sizer,加载头寸数量类(LiquidateRebalancePositionSizer(ticker_weights))------->

qstrader/position_sizer/rebalance.py-----position_sizer-----得到initial_order----------->

建立回测backtest=TradingSession(config,strategy,tickers,initial_equity,start_date,end_date,events_queue,position_sizer=position_sizer,title,

benchmark=tickers[0])--------->

开始回测 results=backtest.start_trading(testing=testing)------------->

前面工作都干完了,现在开始回测,backtest是实例化,start_trading调用实例化的方法。------->

self._config_session(),初始化回测期间必须类,self.price_handler=YahooDailyCsvBarPriceHandler(self.config.CSV_DATA_DIR,self.events_queue,

self.tickers,start_date=self.start_date,end_date=self.end_date)----->

event=self.events_queue.get(False)获得队列中的事件,每个bar事件--------->

self.strategy.calculate_signals(event),加载策略,计算信号----->

self.portfolio_handler.update_portfolio_value(),更新组合价值,------>

self.statistics.update(event.time,self.portfolio_handler),统计更新------->

返回结果 return results

4、思考问题,复杂的看不太懂地方

一、event.py中EventType,EventType=Enum('EventTye','TICK BAR SIGNAL FILL')

TICK BAR 分别对应TICK BAR数据事件,SIGNAL FILL作用是什么?各个事件运行的流程是怎样的?

以buy_and_hold_backtest.py debug来看,事件流程如下:

---->

buy_and_hold_backtest.py  功能:calculate_signals(self,event)

signal=SignalEvent(self.ticker,'BOT',suggested_quantity=self.base_quantity)

self.events_queue.put(signal)                              ----------------------->    trading_session.py  self.portfolio_handler.on_signal(event)

portfolio_handler.py 功能:on_signal(self,signal_event)

#从单个信号事件中创建初始订单列表

initial_order=self._create_order_from_signal(signal_event)

#从初始的订单中设置买卖数量

sized_order=self.position_sizer.size_order(self.portfolio,initial_order)

#从整体风控角度重新修改或者消除订单

order_events=self.risk_manager.refine_orders(self.portfolio,sized_order)

#把订单放入事件队列

self._place_orders_onto_queue(order_events)

二、如何复制一个简易版本回测系统?

去除掉不需要部分,哪些是不需要的,需要看懂整体运行流程和各个部分功能。

1、去掉risk_manager   完成

2、去掉  ORDER事件,直接从SIGNAL事件到FILL事件

ORDER事件干了什么事情,1、order_event---->fill_event 2、self.compliance.record_trade(fill_event)

3、修改trading_session.py中的self.position_sizer文件,(1)、trading_session.py中self.position_sizer有点重复;(2)、产生信号的时候直接生成需要的order参数,tick、action、quantity、

现在问题是什么呢?是带入SIGNAL中的问题,1、有信号要交易,立即生成交易需要参数(代码量比较大,可以写入策略模板)

这样有个问题是在portfolio_handler.py中,在生成order_events时加载了一下self.position_sizer,已经被我删除了。写法如下:

order_events=self.position_sizer.size_order(self.portfolio,initial_order)

可以在portfolio_handler.py 中on_signal中修改,带入需要参数。

现在问题是,主策略中quantity数量为0,又没有加载position_sizer修改数量,

4、先分析portfolio.py、postition.py、tearsheet.py文件,了解各个文件功能。

portfolio.py要传入两个参数,price_handler和cash;price_handler也是portfolio_handler.py、trading_session.py参数,在trading_session.py中入参。

self.price_handler=YahooDailyCsvBarPriceHandler(self.config.CSV_DATA_DIR,self.events_quue,self.tickers,start_date=self.start_date,end_date=self.end_date)

参数传递过程:buy_and_hold_backtest.py initial_equity----> trading_session self.equity=PriceParser.parse(equity)---->portfolio_handler.py self.initial_cash=initial_cash ---->Portfolio.py self.init_cash=cash

portfolio.py 参数设置:

self.price_handler=price_handler  self.init_cash=cash self.equity=cash self.cur_cash=cash self.position={} self.closed_positions=[] self.realised_pnl=0

5、修改掉乘以1000万部分,看着好奇怪。

三、问题是:TradingSession类中的属性(self.price_handler self.suggested_order self.position_size self.portfolio_handler self.compliance self.statistics)定义为另一个类的实例化对象,该属性在实例化的过程中,该如何输入参数?为什么要这样写,有没有别的写法呢?

解答:本质上类时数据结构,实例化后的对象也是一个参数,所以是可以的。它的主要作用是:在父级对象内直接使用子级对象的功能和参数。

子级对象实例化过程中传参可以直接使用父级全局变量参数,子级对象实例化传入的参数可为另一子级实例化的属性,如下图所示:self.portfolio_handler为类PortfolioHandler实例化对象,为trading_session的属性之一,self.portfolio_handler又为TearsheetStatistics实例化过程中的参数。

四、修改

1、重写position.py transact_shares函数

2、position中整除修改为除,在price_parser.py中保留两位小数  44行 round(x,2)

将ib_simulated.py中commission设置为0,print发现quantity非常大,中间有bug

解决:使用vnpy中数据调整函数,如下所示,复制到price_parser.py文件中,再修改一个地方:一、compliance文件中,record_trade方法下display,删去参数4即可。

3、

    

量化交易回测系统---RQalpha、qstrade学习笔记的更多相关文章

  1. 量化投资学习笔记01——初识Pyalgotrade量化交易回测框架

    年初学习量化投资,一开始想自己从头写,还是受了C/C++的影响.结果困在了计算回测数据那里,结果老也不对,就暂时放下了.最近试了一下python的各个量化投资框架,发现一个能用的——pyalgotra ...

  2. FMZ发明者量化平台回测机制说明

    原文连接:https://www.fmz.com/digest-topic/4009 大部分策略在实盘之前都需要回测进行验证,FMZ支持部分品种数字货币现货.期货和永续合约,以及商品期货所有品种.但发 ...

  3. .NET 云原生架构师训练营(系统架构)--学习笔记

    目录 对外展现的功能 内部功能 功能交互与价值通路 系统架构 目标 认识系统的价值通路 认识功能架构,通过把功能结构与形式结构结合来描述系统架构 受益原则 好的架构必须使人受益,要想把架构做好,就要专 ...

  4. wind量化交易

    https://www.joinquant.com/study?f=home&m=memu https://www.v2ex.com/member/mushroomqiu https://sa ...

  5. OnePy--构建属于自己的量化回测框架

    本文主要记录我构建量化回测系统的学习历程. 被遗弃的项目:Chandlercjy/OnePy_Old 新更新中的项目:Chandlercjy/OnePy 目录 1. 那究竟应该学习哪种编程语言比较好呢 ...

  6. WeQuant教程—1.3 利用回测工具降低交易风险

    量化系统投入实际使用之前,人们会希望提前测试交易的效果.这个期间往往涉及代码的改动和参数的调整.最常见的做法是将历史数据输入量化系统,让量化系统根据既定的交易逻辑进行操作,观察和分析交易结果,找到问题 ...

  7. 手把手教你用Python搭建自己的量化回测框架【均值回归策略】

    手把手教你用Python搭建自己的量化回测框架[均值回归策略] 引言 大部分量化策略都可以归类为均值回归与动量策略.事实上,只有当股票价格是均值回归或趋势的,交易策略才能盈利.否则,价格是随机游走的, ...

  8. Linux 学习笔记 1 使用最小的系统,从分区安装系统开始

    我们常用的linux系统在安装过程中大多都省略了对系统进行分区的操作,以至于后期,不了解什么是分区以及分区当中最基本的一些概念, 我们不说最细的知识,只求了解这个过程,那直接步入正题,开始第一节的学习 ...

  9. Vue学习笔记-API调试工具--->国产apipost按装(比postman好按装好用)

    一  使用环境: windows 7 64位操作系统 二   Vue学习笔记-API调试工具--->apipost按装 1.下载: https://www.apipost.cn/ (比postm ...

随机推荐

  1. PyQt5点击菜单栏弹出新窗口,解决新窗口闪退的实现方法

    实现的功能为:当点击菜单中某个菜单时,会弹出一个新窗口,下面就列出部分代码 def mail_setting(self): log.debug("open mail settings&quo ...

  2. git+jenkins jar包代码的发布加新建项目

    1.本地仓库  java开发 把代码上传上来 ,问一下他要上传到的主机ip , 分支 2.本地 , 设置-->仓库 更新数据,让他同步到南阳gitlab, 若没有这个项目,需要创建相同名字的项目 ...

  3. 2017北京网络赛 F Secret Poems 蛇形回路输出

    #1632 : Secret Poems 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 The Yongzheng Emperor (13 December 1678 – ...

  4. Windows驱动开发-DeviceIoControl函数参数dwIoControlCode

    函数语法 BOOL DeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBuffer ...

  5. OpenJ_Bailian - 1088 滑雪(记忆化搜索)

    题意:给定一个二维数组,一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小,输出可以滑行的最长区域的长度. 分析:对于每一个点,进行记忆化搜索.若某点可以向四周某几个点滑行,记忆化搜索求出 ...

  6. CodeForces - 869A The Artful Expedient

    题意:有两个序列X和Y,各含n个数,这2n个数互不相同,若满足xi^yj的结果在序列X内或序列Y内的(xi,yj)对数为偶数,则输出"Karen",否则输出"Koyomi ...

  7. 编程练习 将一个字符串中的空格替换为 "%20"

    重点:字符串和元组一样, 是不可变对象. 所以将创建一个新的字符串对象,将改变后的字符加入到该新的对象里. 两种方法: 1.python的 replace函数 2.判断修改 def replace(a ...

  8. tools.quartz.about

    官方网站,中文文档,demo,  参考零, 参考一, 参考二, 参考三, 参考四 , 参考五 ,文档下载 .

  9. thinkphp5+python.apscheduler实现计划任务

    1.thinkphp5配置自定义命令行 /application/console/command namespace app\console\command; use think\console\Co ...

  10. chrome警告:Synchronous XMLHttpRequest on the main thread

    警告 原因 ajax同步请求会触发此警告 分析 这段英文翻译:主线程上的同步XMLHttpRequest不受欢迎,因为它对最终用户的体验有害: ajax同步,在向后台请求的过程中,前台代码执行会停留在 ...