精华内容
下载资源
问答
  • 在本系列二(多因子Alpha策略回测)中,我们对美股市场的4个量化因子进行了回测。在这里,我们将使用 DolphinDB database 内置的quadprog函数,对各个因子的权重进行均值方差优化,以决定最佳因子权重。本系列二提供的...

    在本系列二(多因子Alpha策略回测)中,我们对美股市场的4个量化因子进行了回测。在这里,我们将使用 DolphinDB database 内置的quadprog函数,对各个因子的权重进行均值方差优化,以决定最佳因子权重。

    本系列二提供的脚本执行完毕后,数据表factorPnl会存有每个因子每天的收益。以下脚本基于数据表factorPnl。retMatrix=matrix(select C0, C1, C2, C3 from factorPnl)

    H = cross(covar, retMatrix, retMatrix)

    f=-0.25*each(avg, retMatrix)

    A=-(1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 $4:4)

    b=-0.1*1 1 1 1

    Aeq=matrix([1],[1],[1],[1])

    beq=[1]

    result=quadprog(H, f, A, b, Aeq, beq)

    result[1]

    这段代码解决了以下优化问题:

    24f7bd1f49d0bcda03c2ec22a2d91e7f.png

    这里我们的目标函数为最大化(1/4*期望收益 – 1/2*方差),同时保证四个因子的权重之和为1,并且每个因子最小权重为10%。 H是四个因子收益率的方差-协方差矩阵。请注意这里的H,A和Aeq必须是列数相等的矩阵; f,b和beq必须是向量。A是负的单位矩阵,与b一起使用,保证每个因子权重大于10%。关于quadprog函数细节请参考DolphinDB用户开发文档quadprog。

    result[1]给出基于给定优化条件的最佳因子权重。结果如下:[0.3612, 0.1, 0.438804, 0.1]

    在本系列二提供的脚本中,signalNames = `signal_mom`signal_vol`signal_beta`signal_size

    因此,最佳权重为36.1%的动量因子,10%的波动率因子,43.9%的beta因子和10%的市值因子。

    以上计算使用了所有历史时期的数据。也可选用最近10年的数据进行计算,只需对以上脚本的第一行代码做如下改动:retMatrix=matrix(select C0, C1, C2, C3 from factorPnl where date>=2007.01.01)

    结果如下:[0.19277, 0.1, 0.1, 0.60723]

    因此,最佳权重为19.3%的动量因子,10%的波动率因子,10%的beta因子和60.7%的市值因子。

    由此可见,进行均值方差优化时选用的历史时期对结果有重要影响。

    展开全文
  • 本文将介绍在华尔街广泛应用的多因子Alpha策略的回测。多因子模型是量化交易选股中最重要的一类模型,基本思路是找到某些和回报率最相关的指标,并根据这些指标,构建股票投资组合(做多正相关的股票,做空负相关的...

    本系列文章将会介绍如何使用DolphinDB优雅而高效的实现量化交易策略回测。本文将介绍在华尔街广泛应用的多因子Alpha策略的回测。多因子模型是量化交易选股中最重要的一类模型,基本思路是找到某些和回报率最相关的指标,并根据这些指标,构建股票投资组合(做多正相关的股票,做空负相关的股票)。多因子模型中,单独一个因子的个股权重一般实现多空均衡(市场中性),没有暴露市场风险的头寸(beta为0,所以称之为alpha策略),能实现绝对收益。多个因子之间相互正交,方便策略配置,实现回报和风险的最优控制。另外,相比于套利策略(通常可以实现更高的sharpe ratio,但是scale不好),多因子alpha策略有很好的scale,可以配置大量的资金。多因子Alpha策略在对冲基金中的使用非常普遍。

    1. 生成因子

    本文的重点是实现多因子Alpha策略的回测框架。因子不是重点,这部分通常由金融工程师或策略分析师来完成。为了方便大家理解,文章以动量因子、beta因子、规模因子和波动率因子4个常用的风险因子为例,介绍如何在 DolphinDB database 中实现多因子回测。

    输入数据表inData包含6个字段:sym (股票代码), date(日期), close (收盘价), RET(日回报), MV(市值), VOL(交易量)def genSignals(inData){

    USstocks = select sym, date, close, RET, MV from inData where weekday(date) between 1:5, close>5, VOL>0, MV>100000 order by sym, date

    update USstocks set prevMV=prev(MV), cumretIndex=cumprod(1+RET), signal_size=-sqrt(MV), signal_vol=-mstd(RET, 21)*sqrt(252) context by sym

    update USstocks set mRet = wavg(RET, prevMV) context by date

    update USstocks set signal_mom = move(cumretIndex,21)/move(cumretIndex,252)-1, signal_beta=mbeta(RET, mRet, 63) from USstocks context by sym

    return select sym, date, close, RET as ret, signal_size, signal_beta, signal_vol, signal_mom from USstocks where date>=1991.01.01

    }

    DolphinDB函数说明:

    _abs:_取绝对值。

    _prev:_把向量中的所有元素向右移动一个位置。

    _cumprod:_计算累计乘积。

    _sqrt:_计算平方根。

    _mstd(X, k):_计算移动标准差。

    _wavg(X, k):_计算加权平均数。

    _move(X, k):_如果k为正数,则把向量的所有元素向右移动k个位置,如果k为负数,则把向量的所有元素向左移动k个位置。

    _mbeta(X, Y, k):_计算普通最小二乘回归的系数估计。

    genSignals 函数说明:

    首先数据过滤,选择市值较高的股票在交易日中的数据。接着使用过滤后的数据计算4个风险因子:规模因子(signal_size):MV的平方根的相反数

    波动率因子(signal_vol):过去一个月的股价波动率的相反数

    动量因子(signal_mom):过去12个月(去除最近一个月)的动量因子

    beta因子(signal_beta):利用过去三个月的数据计算个股跟市场的beta

    2. 回测框架

    多因子Alpha策略的回测框架包含3个部分。首先是在每个历史周期上,生成每个股票在每个策略上的权重。一个历史周期上的所有仓位可以成为一个tranche。然后根据tranche的持有时间,生成每一个股票在每一个tranche的每一个策略上每一天的仓位和盈亏。最后统计分析每个策略和所有策略的业绩。

    2.1 计算历史周期的投资仓位

    首先定义一个函数formPeriodPort计算一个周期(一天)的股票仓位。然后使用并行计算获得历史上每一个周期的投资仓位。

    2.1.1 计算一天的股票投资组合

    这一步的输入是每一个股票在不同因子上的值,输出是每一个股票在每一个因子上的投资权重。股票权重要满足两个条件:(1)一个因子中所有股票的权重和为零,也就是说多空均衡。(2)不同因子之间相互正交,也就是说第i个因子的权重wi和第j个因子的值sj的内积为0(i<>j)。为了实现上述目标,我们引入了因子矩阵(矩阵的每一列表示一个因子,每一行表示一个股票),并且将单位因子(所有元素均为1)添加到因子矩阵中。

    实践中,还需要考虑的一个问题是,去除权重较小的股票。一个股票池有几千个股票,大部分的股票获得的权重很小,几乎可以忽略。我们定义了一个嵌套函数f来调整单个因子中股票的权重。

    函数formPeriodPort的输入参数有3个:signals 是由genSignals函数生成的数据表,包含8个字段:股票代码、日期、收盘价格、回报率和4个因子。

    signalNames 是所有因子的名称,用向量表示。

    stockPercentile 用于控制股票的数量。

    函数的输出是一个数据表,存储一天的股票投资组合,包括4个字段:tranche, sym, signalIdx, exposure。def formPeriodPort(signals, signalNames, stockPercentile){

    stockCount = signals.size()

    signalCount = signalNames.size()

    tranche = signals.date.first()

    //demean all signals and add a unit column to the signal matrix

    sigMat = matrix(take(1, stockCount), each(x->x - avg(x), signals[signalNames]))

    //form weight matrix.

    transSigMat = sigMat.transpose()

    weightMat = transSigMat.dot(sigMat).inv().dot(transSigMat).transpose()[1:]

    /* form exposures. allocate two dollars on each signal, one for long and one for short

    trim small weights. In practice, we don't want to trade too many stocks */

    f = def(sym, tranche, stockPercentile, signalVec, signalIdx){

    t = table(sym, signalVec as exposure, iif(signalVec > 0, 1, -1) as sign)

    update t set exposure = exposure * (abs(exposure) < percentile(abs(exposure), stockPercentile)) context by sign

    update t set exposure = exposure / sum(exposure).abs() context by sign

    return select tranche as tranche, sym, signalIdx as signalIdx, exposure from t where exposure != 0

    }

    return loop(f{signals.sym, tranche, stockPercentile}, weightMat, 1..signalCount - 1).unionAll(false)

    }

    DolphinDB函数说明:

    _size:_返回向量中元素的个数

    _first:_返回第一个元素

    _matrix:_构建矩阵

    _transpose:_矩阵转置

    dot:矩阵或向量内积

    _inv:_矩阵求逆

    _iif(condition, trueResult, falseResult):_如果满足条件condition,则返回trueResult,否则返回falseResult。它相当于对每个元素分别运行if...else语句。

    _loop(func,args):_高价模板函数,把函数func应用到参数args的每一个元素上,并将结果汇总到一个元组中。如果args包含三个k个参数,每个参数的长度是n,那么loop将运行n次。

    _unionAll:_合并多个表

    2.1.2 计算过去每天的股票投资组合

    回测时使用 的数据量非常庞大,因此我们把数据放到内存的分区数据库中,然后使用并行计算。如果想要了解更多关于分区数据库的内容,可以参考DolphinDB分区数据库教程。我们把genSignals函数生成的数据保存到分区表partSignals中,一个分区表示一天。接着,创建一个分区表ports,用于保存计算出来的股票投资组合,一个分区表示一年。然后,使用 map-reduce函数,把formPeriodPort函数应用到每一天,把每个结果合并到分区表ports中。def formPortfolio(signals, signalNames, stockPercentile){

    dates = (select count(*) from signals group by date having count(*)>1000).date.sort()

    db = database("", VALUE, dates)

    partSignals = db.createPartitionedTable(signals, "signals", `date).append!(signals)

    db = database("", RANGE, datetimeParse(string(year(dates.first()) .. (year(dates.last()) + 1)) + ".01.01", "yyyy.MM.dd"))

    symType = (select top 10 sym from signals).sym.type()

    ports = db.createPartitionedTable(table(1:0, `tranche`sym`signalIdx`exposure, [DATE,symType,INT,DOUBLE]), "", `tranche)

    return mr(sqlDS(), formPeriodPort{,signalNames,stockPercentile},,unionAll{,ports})

    }

    DolphinDB函数说明:

    _sort:_把向量中的元素排序

    _database(directory, [partitionType], [partitionScheme], [locations]):_创建数据库。如果directory为空,则创建内存数据库。

    _createPartitionedTable(dbHandle, table, [tableName], partitionColumns):_在数据库中创建分区表。

    _datatimeParse(X, format):_把字符串转换成DolphinDB中时间类型数据。

    _unionAll:_合并表

    _type:_返回数据类型的ID。

    _mr(ds, mapFunc, [reduceFunc], [finalFunc], [parallel=true]):_map-reduce函数。

    2.2 计算仓位和盈亏

    这一步的任务是根据持有的仓位以及持有时间,生成每一个股票在每一个tranche的每一个因子上每一天的仓位和盈亏。首先定义一个嵌套函数来f来计算部分股票投资仓位的盈亏,接着把嵌套函数应用到所有股票投资组合(使用mr函数),计算所有股票投资组合的盈亏,并把结果保存到分区表pnls中。

    函数caclStockPnL的输入参数包括:ports: 每一天的投资组合表,包括4个字段 tranche, sym, signalIdx, exposure

    dailyRtn:股票每天的回报表,包括3个字段 date, sym, ret

    holdingDays: 股票持有的天数

    函数的输出是股票的盈亏明细表,包括字段8个字段 date, sym, signalIdx, tranche, age, ret, exposure, pnldef calcStockPnL(ports, dailyRtn, holdingDays){

    ages = table(1..holdingDays as age)

    dates = sort exec distinct(tranche) from ports

    dictDateIndex = dict(dates, 1..dates.size())

    dictIndexDate = dict(1..dates.size(), dates)

    lastDaysTable = select max(date) as date from dailyRtn group by sym

    lastDays = dict(lastDaysTable.sym, lastDaysTable.date)

    // define a anonymous function to calculate the pnl for a part of the porfolios.

    f = def(ports, dailyRtn, holdingDays, ages, dictDateIndex, dictIndexDate,lastDays){

    pos = select dictIndexDate[dictDateIndex[tranche]+age] as date, sym, signalIdx, tranche, age, take(0.0,size age) as ret, exposure, take(0.0,size age) as pnl from cj(ports,ages) where isValid(dictIndexDate[dictDateIndex[tranche]+age]), dictIndexDate[dictDateIndex[tranche]+age]<=lastDays[sym]

    update pos set ret = dailyRtn.ret from ej(pos, dailyRtn,`date`sym)

    update pos set exposure = exposure*cumprod(1+ret) from pos context by tranche, signalIdx, sym

    update pos set pnl = exposure*ret/(1+ret)

    return pos

    }

    // calculate pnls for all portfolios and save the result to a partitioned in-memory table pnls

    db = database("", RANGE, datetimeParse(string(year(dates.first()) .. (year(dates.last()) + 1)) + ".01.01", "yyyy.MM.dd"))

    symType = (select top 10 sym from ports).sym.type()

    modelPnls = table(1:0, `date`sym`signalIdx`tranche`age`ret`exposure`pnl, [DATE,symType,INT,DATE,INT,DOUBLE,DOUBLE,DOUBLE])

    pnls = db.createPartitionedTable(modelPnls, "", `tranche)

    return mr(sqlDS(), f{,dailyRtn,holdingDays,ages,dictDateIndex, dictIndexDate,lastDays},,unionAll{,pnls})

    }

    DolphinDB函数说明:

    _dict(key, value):_创建字典。

    _cj(leftTable, rightTable) :_交叉连接两个表。

    _isValid:_检查元素是否为NULL。如果不是NULL,则返回1,如果是NULL,则返回0。

    _ej(leftTable, rightTable, matchingCols, [rightMatchingCols]) :_等值连接两个表。

    3. 运行实例

    我们以美国股市为例,运行多因子Alpha策略回测。输入的股票日数据表USPrices包含6个字段:sym (股票代码), date(日期), close (收盘价), RET(日回报), MV(市值)和VOL(交易量)。//加载数据

    USPrices = ...

    holdingDays = 5

    stockPercentile = 20

    signalNames = `signal_mom`signal_vol`signal_beta`signal_size

    //生成因子

    signals=genSignals(USPrices)

    //计算每天的股票投资组合

    ports = formPortfolio(signals, signalNames, stockPercentile)

    //计算盈亏

    dailyRtn = select sym,date,ret from signals

    pos = calcStockPnL(ports, dailyRtn, holdingDays)

    //绘制四个因子的累计盈亏走势图

    pnls = select sum(pnl) as pnl from pos group by date, signalIdx

    factorPnl = select pnl from pnls pivot by date, signalIdx

    plot(each(cumsum,factorPnl[`C0`C1`C2`C3]).rename!(signalNames), factorPnl.date, "The Cumulative Pnl of All Four Signals")

    //绘制动量因子不同持仓日的累计盈亏走势图

    pnls = select sum(pnl) as pnl from pos where signalIdx=0 group by date, age

    momAgePnl = select pnl from pnls pivot by date, age

    plot(each(cumsum,momAgePnl[`C1`C2`C3`C4`C5]).rename!(`C1`C2`C3`C4`C5), momAgePnl.date)

    76923e21615cd2b9b26334d3fcebea8a.png

    4个因子的累计盈亏走势图

    620fdcda7784d05701b1a004dc735712.png

    动量因子的累计盈亏走势图

    DolphinDB虽然是一个通用的分布式时序数据库,但因为内置极其高效的多范式编程语言,用于量化交易,开发效率非常高。上面的多因子回测框架,仅用了3个自定义函数,50余行代码。DolphinDB的运行效率更是惊人,对美国股市25年中市值较高的股票按日进行回测,最后产生的盈亏明细表包含1亿余条记录。如此复杂的计算量,在单机(4核)上执行耗时仅50秒。

    4. 讨论

    前面的回测框架,仅仅解决了多因子策略的一部分问题,也就是说单个因子中股票的配置。我们还有两个重要的问题需要解决:(1)多个因子之间,如何配置权重,平衡投资的回报和风险。(2)一个新的因子有没有带来额外的Alpha,换句话说,一个新的因子是不是可以有已经存在的多个因子来表示,如果可以,那么这个新因子可能没有存在的必要。下一篇文章,我们会介绍如何使用DolphinDB来回答上面两个问题。

    展开全文
  • 本文将介绍在华尔街广泛应用的多因子Alpha策略的回测。多因子模型是量化交易选股中最重要的一类模型,基本思路是找到某些和回报率最相关的指标,并根据这些指标,构建股票投资组合(做多正相关的股票,做空负相关的...

    本系列文章将会介绍如何使用DolphinDB优雅而高效的实现量化交易策略回测。本文将介绍在华尔街广泛应用的多因子Alpha策略的回测。多因子模型是量化交易选股中最重要的一类模型,基本思路是找到某些和回报率最相关的指标,并根据这些指标,构建股票投资组合(做多正相关的股票,做空负相关的股票)。多因子模型中,单独一个因子的个股权重一般实现多空均衡(市场中性),没有暴露市场风险的头寸(beta为0,所以称之为alpha策略),能实现绝对收益。多个因子之间相互正交,方便策略配置,实现回报和风险的最优控制。另外,相比于套利策略(通常可以实现更高的sharpe ratio,但是scale不好),多因子alpha策略有很好的scale,可以配置大量的资金。多因子Alpha策略在对冲基金中的使用非常普遍。

    1. 生成因子

    本文的重点是实现多因子Alpha策略的回测框架。因子不是重点,这部分通常由金融工程师或策略分析师来完成。为了方便大家理解,文章以动量因子、beta因子、规模因子和波动率因子4个常用的风险因子为例,介绍如何在 DolphinDB database 中实现多因子回测。

    输入数据表inData包含6个字段:sym (股票代码), date(日期), close (收盘价), RET(日回报), MV(市值), VOL(交易量)def genSignals(inData){

    USstocks = select sym, date, close, RET, MV from inData where weekday(date) between 1:5, close>5, VOL>0, MV>100000 order by sym, date

    update USstocks set prevMV=prev(MV), cumretIndex=cumprod(1+RET), signal_size=-sqrt(MV), signal_vol=-mstd(RET, 21)*sqrt(252) context by sym

    update USstocks set mRet = wavg(RET, prevMV) context by date

    update USstocks set signal_mom = move(cumretIndex,21)/move(cumretIndex,252)-1, signal_beta=mbeta(RET, mRet, 63) from USstocks context by sym

    return select sym, date, close, RET as ret, signal_size, signal_beta, signal_vol, signal_mom from USstocks where date>=1991.01.01

    }

    DolphinDB函数说明:

    abs:取绝对值。

    prev:把向量中的所有元素向右移动一个位置。

    cumprod:计算累计乘积。

    sqrt:计算平方根。

    mstd(X, k):计算移动标准差。

    wavg(X, k):计算加权平均数。

    move(X, k):如果k为正数,则把向量的所有元素向右移动k个位置,如果k为负数,则把向量的所有元素向左移动k个位置。

    mbeta(X, Y, k):计算普通最小二乘回归的系数估计。

    genSignals函数说明:

    首先数据过滤,选择市值较高的股票在交易日中的数据。接着使用过滤后的数据计算4个风险因子:规模因子(signal_size):MV的平方根的相反数

    波动率因子(signal_vol):过去一个月的股价波动率的相反数

    动量因子(signal_mom):过去12个月(去除最近一个月)的动量因子

    beta因子(signal_beta):利用过去三个月的数据计算个股跟市场的beta

    2. 回测框架

    多因子Alpha策略的回测框架包含3个部分。首先是在每个历史周期上,生成每个股票在每个策略上的权重。一个历史周期上的所有仓位可以成为一个tranche。然后根据tranche的持有时间,生成每一个股票在每一个tranche的每一个策略上每一天的仓位和盈亏。最后统计分析每个策略和所有策略的业绩。

    2.1 计算历史周期的投资仓位

    首先定义一个函数formPeriodPort计算一个周期(一天)的股票仓位。然后使用并行计算获得历史上每一个周期的投资仓位。

    2.1.1 计算一天的股票投资组合

    这一步的输入是每一个股票在不同因子上的值,输出是每一个股票在每一个因子上的投资权重。股票权重要满足两个条件:(1)一个因子中所有股票的权重和为零,也就是说多空均衡。(2)不同因子之间相互正交,也就是说第i个因子的权重wi和第j个因子的值sj的内积为0(i<>j)。为了实现上述目标,我们引入了因子矩阵(矩阵的每一列表示一个因子,每一行表示一个股票),并且将单位因子(所有元素均为1)添加到因子矩阵中。

    实践中,还需要考虑的一个问题是,去除权重较小的股票。一个股票池有几千个股票,大部分的股票获得的权重很小,几乎可以忽略。我们定义了一个嵌套函数f来调整单个因子中股票的权重。

    函数formPeriodPort的输入参数有3个:signals 是由genSignals函数生成的数据表,包含8个字段:股票代码、日期、收盘价格、回报率和4个因子。

    signalNames 是所有因子的名称,用向量表示。

    stockPercentile 用于控制股票的数量。

    函数的输出是一个数据表,存储一天的股票投资组合,包括4个字段:tranche, sym, signalIdx, exposure。def formPeriodPort(signals, signalNames, stockPercentile){

    stockCount = signals.size()

    signalCount = signalNames.size()

    tranche = signals.date.first()

    //demean all signals and add a unit column to the signal matrix

    sigMat = matrix(take(1, stockCount), each(x->x - avg(x), signals[signalNames]))

    //form weight matrix.

    transSigMat = sigMat.transpose()

    weightMat = transSigMat.dot(sigMat).inv().dot(transSigMat).transpose()[1:]

    /* form exposures. allocate two dollars on each signal, one for long and one for short

    trim small weights. In practice, we don't want to trade too many stocks */

    f = def(sym, tranche, stockPercentile, signalVec, signalIdx){

    t = table(sym, signalVec as exposure, iif(signalVec > 0, 1, -1) as sign)

    update t set exposure = exposure * (abs(exposure) 

    update t set exposure = exposure / sum(exposure).abs() context by sign

    return select tranche as tranche, sym, signalIdx as signalIdx, exposure from t where exposure != 0

    }

    return loop(f{signals.sym, tranche, stockPercentile}, weightMat, 1..signalCount - 1).unionAll(false)

    }

    DolphinDB函数说明:

    size:返回向量中元素的个数

    first:返回第一个元素

    matrix:构建矩阵

    transpose:矩阵转置

    dot:矩阵或向量内积

    inv:矩阵求逆

    iif(condition, trueResult, falseResult):如果满足条件condition,则返回trueResult,否则返回falseResult。它相当于对每个元素分别运行if...else语句。

    loop(func,args):高价模板函数,把函数func应用到参数args的每一个元素上,并将结果汇总到一个元组中。如果args包含三个k个参数,每个参数的长度是n,那么loop将运行n次。

    unionAll:合并多个表

    2.1.2 计算过去每天的股票投资组合

    回测时使用 的数据量非常庞大,因此我们把数据放到内存的分区数据库中,然后使用并行计算。如果想要了解更多关于分区数据库的内容,可以参考DolphinDB分区数据库教程。我们把genSignals函数生成的数据保存到分区表partSignals中,一个分区表示一天。接着,创建一个分区表ports,用于保存计算出来的股票投资组合,一个分区表示一年。然后,使用 map-reduce函数,把formPeriodPort函数应用到每一天,把每个结果合并到分区表ports中。def formPortfolio(signals, signalNames, stockPercentile){

    dates = (select count(*) from signals group by date having count(*)>1000).date.sort()

    db = database("", VALUE, dates)

    partSignals = db.createPartitionedTable(signals, "signals", `date).append!(signals)

    db = database("", RANGE, datetimeParse(string(year(dates.first()) .. (year(dates.last()) + 1)) + ".01.01", "yyyy.MM.dd"))

    symType = (select top 10 sym from signals).sym.type()

    ports = db.createPartitionedTable(table(1:0, `tranche`sym`signalIdx`exposure, [DATE,symType,INT,DOUBLE]), "", `tranche)

    return mr(sqlDS(), formPeriodPort{,signalNames,stockPercentile},,unionAll{,ports})

    }

    DolphinDB函数说明:

    sort:把向量中的元素排序

    database(directory, [partitionType], [partitionScheme], [locations]):创建数据库。如果directory为空,则创建内存数据库。

    createPartitionedTable(dbHandle, table, [tableName], partitionColumns):在数据库中创建分区表。

    datatimeParse(X, format):把字符串转换成DolphinDB中时间类型数据。

    unionAll:合并表

    type:返回数据类型的ID。

    mr(ds, mapFunc, [reduceFunc], [finalFunc], [parallel=true]):map-reduce函数。

    2.2 计算仓位和盈亏

    这一步的任务是根据持有的仓位以及持有时间,生成每一个股票在每一个tranche的每一个因子上每一天的仓位和盈亏。首先定义一个嵌套函数来f来计算部分股票投资仓位的盈亏,接着把嵌套函数应用到所有股票投资组合(使用mr函数),计算所有股票投资组合的盈亏,并把结果保存到分区表pnls中。

    函数caclStockPnL的输入参数包括:ports: 每一天的投资组合表,包括4个字段 tranche, sym, signalIdx, exposure

    dailyRtn:股票每天的回报表,包括3个字段 date, sym, ret

    holdingDays: 股票持有的天数

    函数的输出是股票的盈亏明细表,包括字段8个字段 date, sym, signalIdx, tranche, age, ret, exposure, pnldef calcStockPnL(ports, dailyRtn, holdingDays){

    ages = table(1..holdingDays as age)

    dates = sort exec distinct(tranche) from ports

    dictDateIndex = dict(dates, 1..dates.size())

    dictIndexDate = dict(1..dates.size(), dates)

    lastDaysTable = select max(date) as date from dailyRtn group by sym

    lastDays = dict(lastDaysTable.sym, lastDaysTable.date)

    // define a anonymous function to calculate the pnl for a part of the porfolios.

    f = def(ports, dailyRtn, holdingDays, ages, dictDateIndex, dictIndexDate,lastDays){

    pos = select dictIndexDate[dictDateIndex[tranche]+age] as date, sym, signalIdx, tranche, age, take(0.0,size age) as ret, exposure, take(0.0,size age) as pnl from cj(ports,ages) where isValid(dictIndexDate[dictDateIndex[tranche]+age]), dictIndexDate[dictDateIndex[tranche]+age]<=lastDays[sym]

    update pos set ret = dailyRtn.ret from ej(pos, dailyRtn,`date`sym)

    update pos set exposure = exposure*cumprod(1+ret) from pos context by tranche, signalIdx, sym

    update pos set pnl = exposure*ret/(1+ret)

    return pos

    }

    // calculate pnls for all portfolios and save the result to a partitioned in-memory table pnls

    db = database("", RANGE, datetimeParse(string(year(dates.first()) .. (year(dates.last()) + 1)) + ".01.01", "yyyy.MM.dd"))

    symType = (select top 10 sym from ports).sym.type()

    modelPnls = table(1:0, `date`sym`signalIdx`tranche`age`ret`exposure`pnl, [DATE,symType,INT,DATE,INT,DOUBLE,DOUBLE,DOUBLE])

    pnls = db.createPartitionedTable(modelPnls, "", `tranche)

    return mr(sqlDS(), f{,dailyRtn,holdingDays,ages,dictDateIndex, dictIndexDate,lastDays},,unionAll{,pnls})

    }

    DolphinDB函数说明:

    dict(key, value):创建字典。

    cj(leftTable, rightTable) :交叉连接两个表。

    isValid:检查元素是否为NULL。如果不是NULL,则返回1,如果是NULL,则返回0。

    ej(leftTable, rightTable, matchingCols, [rightMatchingCols]) :等值连接两个表。

    3. 运行实例

    我们以美国股市为例,运行多因子Alpha策略回测。输入的股票日数据表USPrices包含6个字段:sym (股票代码), date(日期), close (收盘价), RET(日回报), MV(市值)和VOL(交易量)。//加载数据

    USPrices = ...

    holdingDays = 5

    stockPercentile = 20

    signalNames = `signal_mom`signal_vol`signal_beta`signal_size

    //生成因子

    signals=genSignals(USPrices)

    //计算每天的股票投资组合

    ports = formPortfolio(signals, signalNames, stockPercentile)

    //计算盈亏

    dailyRtn = select sym,date,ret from signals

    pos = calcStockPnL(ports, dailyRtn, holdingDays)

    //绘制四个因子的累计盈亏走势图

    pnls = select sum(pnl) as pnl from pos group by date, signalIdx

    factorPnl = select pnl from pnls pivot by date, signalIdx

    plot(each(cumsum,factorPnl[`C0`C1`C2`C3]).rename!(signalNames), factorPnl.date, "The Cumulative Pnl of All Four Signals")

    //绘制动量因子不同持仓日的累计盈亏走势图

    pnls = select sum(pnl) as pnl from pos where signalIdx=0 group by date, age

    momAgePnl = select pnl from pnls pivot by date, age

    plot(each(cumsum,momAgePnl[`C1`C2`C3`C4`C5]).rename!(`C1`C2`C3`C4`C5), momAgePnl.date)

    d10be41a3d925f1b8406ea8ca7096b6b.png4个因子的累计盈亏走势图

    3fc8ec57b25c560ecff6d77753bed1dd.png动量因子的累计盈亏走势图

    DolphinDB虽然是一个通用的分布式时序数据库,但因为内置极其高效的多范式编程语言,用于量化交易,开发效率非常高。上面的多因子回测框架,仅用了3个自定义函数,50余行代码。DolphinDB的运行效率更是惊人,对美国股市25年中市值较高的股票按日进行回测,最后产生的盈亏明细表包含1亿余条记录。如此复杂的计算量,在单机(4核)上执行耗时仅50秒。

    4. 讨论

    前面的回测框架,仅仅解决了多因子策略的一部分问题,也就是说单个因子中股票的配置。我们还有两个重要的问题需要解决:(1)多个因子之间,如何配置权重,平衡投资的回报和风险。(2)一个新的因子有没有带来额外的Alpha,换句话说,一个新的因子是不是可以有已经存在的多个因子来表示,如果可以,那么这个新因子可能没有存在的必要。下一篇文章,我们会介绍如何使用DolphinDB来回答上面两个问题。

    展开全文
  • 在本系列二(多因子Alpha策略回测)中,我们对美股市场的4个量化因子进行了回测。在这里,我们将使用 DolphinDB database 内置的quadprog函数,对各个因子的权重进行均值方差优化,以决定最佳因子权重。 本系列二...

    在本系列二(多因子Alpha策略回测)中,我们对美股市场的4个量化因子进行了回测。在这里,我们将使用 DolphinDB database 内置的quadprog函数,对各个因子的权重进行均值方差优化,以决定最佳因子权重。

    本系列二提供的脚本执行完毕后,数据表factorPnl会存有每个因子每天的收益。以下脚本基于数据表factorPnl。

    retMatrix=matrix(select C0, C1, C2, C3 from factorPnl)
    H = cross(covar, retMatrix, retMatrix)
    f=-0.25*each(avg, retMatrix)
    A=-(1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 $4:4)
    b=-0.1*1 1 1 1 
    Aeq=matrix([1],[1],[1],[1]) 
    beq=[1]
    result=quadprog(H, f, A, b, Aeq, beq)
    result[1]

    这段代码解决了以下优化问题:

    这里我们的目标函数为最大化(1/4*期望收益 – 1/2*方差),同时保证四个因子的权重之和为1,并且每个因子最小权重为10%。 H是四个因子收益率的方差-协方差矩阵。请注意这里的H,A和Aeq必须是列数相等的矩阵; f,b和beq必须是向量。A是负的单位矩阵,与b一起使用,保证每个因子权重大于10%。关于quadprog函数细节请参考DolphinDB用户开发文档quadprog

    result[1]给出基于给定优化条件的最佳因子权重。结果如下:

    [0.3612, 0.1, 0.438804, 0.1]

    在本系列二提供的脚本中,

    signalNames = `signal_mom`signal_vol`signal_beta`signal_size

    因此,最佳权重为36.1%的动量因子,10%的波动率因子,43.9%的beta因子和10%的市值因子。

    以上计算使用了所有历史时期的数据。也可选用最近10年的数据进行计算,只需对以上脚本的第一行代码做如下改动:

    retMatrix=matrix(select C0, C1, C2, C3 from factorPnl where date>=2007.01.01)

    结果如下:

    [0.19277, 0.1, 0.1, 0.60723]

    因此,最佳权重为19.3%的动量因子,10%的波动率因子,10%的beta因子和60.7%的市值因子。

    由此可见,进行均值方差优化时选用的历史时期对结果有重要影响。

     

    欢迎访问官网下载 DolphinDB database

    联系邮箱:info@dolphindb.com

    展开全文
  • 在本系列二(多因子Alpha策略回测)中,我们对美股市场的4个量化因子进行了回测。在这里,我们将使用 DolphinDB database 内置的quadprog函数,对各个因子的权重进行均值方差优化,以决定最佳因子权重。 本系列二...
  • 信息具有时间维度信息是具有时间维度的,以不同速度到达的信息,将在不同长短的时间区间内具有其相应的价值,某些信号可能在发出的瞬间同时也失去其价值,而某些信号可能在随后的一两年期间依然保持较大...Alpha因子存在...
  • 证券研究报告_量化投资专题报告2011年08月15日大浪淘金Alpha因子何处寻2011年金融工程研讨会专题报告系列之二罗军金融工程分析师胡海涛金融工程分析师电话020-87555888-8655电话
  • 本文将介绍在华尔街广泛应用的多因子Alpha策略的回测。多因子模型是量化交易选股中最重要的一类模型,基本思路是找到某些和回报率最相关的指标,并根据这些指标,构建股票投资组合(做多正相关的股票,做空负相关的...
  • 1反转因子再优化更精准的拐点把握严佳炜广发证券金融工程2015年8月CONTENTS目录201020304基于价格分反转因子介绍因子回测与段构建SLP总结与改进思路对比反转因子>301020103|反
  • Table_TopTable_Header1证券研究报告_量化投资专题Table_AuthorTemp证券研究报告_XX报告2011年12月20日153062011年6月14日Table_Title观
  • 基于风险的配置风险平价及在Alpha策略中的应用严佳炜广发证券金融工程2015年6月1CONTENTS目录01020304风险平价在风险平价方法Alpha风险风格配置中总结介绍平价策略的应用>2/27
  • 多因子选股Alpha策略

    千次阅读 2019-07-22 14:25:40
    根据著名的Fama三因子模型,可以用Beta、市值、估值来预测股票的回报率。该模型通过实证研究发现,通常小盘价值股票的业绩表现是最优的。这可能是由于小盘股相对大盘股更不容易受到投资者关注,从而更可能形成价值...
  • 看知乎上很矿友一直在讨论如何挑选因子、使用因子进行预测。找了一下社区的相关帖子,做个整理。本文从因子的相关性入手,用不同因子组合、剔除高度相关因子后再进行降维处理,利用所得各风格信号预测股价走势。将...
  • 这个问题其实很好,我只讲一点供大家参考。考虑一个多因子模型,其中股票的超额期望收益(超过无风险回报的那部分收益)...这就是为什么学术界在讨论阿尔法的时候,正确的名字是定价差(pricing error)多因子是否能带来...
  • 原标题:多因子系列之二:Alpha因子高维度与非线性问题——基于Lasso的收益预测模型 | 量化专题报告本篇报告尝试解决Alpha预测中高维度与非线性两个问题。我们使用基于Lasso的模型,针对性的解决了上述两个问题,...
  • 资本资产定价模型将投资组合的期望收益由两部分组成:alpha收益为投资组合超越市场基准的收益,beta收益为投资组合承担市场系统风险而获得的收益。通过对冲交易剥离或降低投资组合的系统风险(beta收益),获取纯粹的...
  • 报告要点多因子目的在于解释了个股的收益多因子体系的核心在于找出可以对个股有解释的因素。风格内因子的确定一般考虑两方面内容:风格内因子的整理,目的在于构建代表风格的因子,对于相关性高的风格以替代法选择,...
  • 1、什么是alpha?超额收益就是alpha。超额收益是相对一个基准来说的。那么这个基准是什么?2、基准是什么?这就涉及联合假设问题(joint hypothesis problem):几乎所有的资产定价模型都假设资产市场是有效的,因此...
  • 我们不讨论这些因子是否都是有效的,我们更的是希望通过我们分析因子背后的金融意义,帮助大家更好的理解市场,明白如何去做一些类似的研究、策略。这是我们的初衷。Alpha #002 因子➢ 公式:➢ 公...
  • 近一个半月疯狂的接触多因子模型,其中对于单个因子的回测,是最熟的。而对于单个因子,或者叫做signal(这一系列文章后续都这么叫),是多因子模型的基础。当然,如果你认为,世界上没有alpha,那么只要bet style...
  • DolphinDB Database是一款高性能分布式时序数据库(time-series database),它特别适用于投资银行、对冲基金和交易所的定量查询...很个人和机构尝试用不同的语言来实现这101个Alpha因子。本文中,我们例举了较为简单..
  • 二、多因子(Alpha因子)的种类按照因子分析的角度:1.基本面因子价值因子盈利脑子成长因子资本结构因子运营因子流通性因子2.技术因子动量因子趋势因子市值因子波动因子成交量因子按照因子来源的角度如下:公司层面:...
  • Craftmanship Alpha——因子策略的实施细节对结果的影响 来自量化哥-优矿Uqer的雪球原创专栏 作者:量化哥-优矿Uqer 链接:https://xueqiu.com/4105947155/109610673 来源:雪球 著作权归作者所有。商业转载请...
  • 二、多因子Alpha因子)的种类 按照因子分析的角度: 1.基本面因子 价值因子 盈利脑子 成长因子 资本结构因子 运营因子 流通性因子 2.技术因子 动量因子 趋势因子 市值因子 波动因子 成交量因子 按照因子来源的角度...
  • 简单多因子选股——小市值 大概思路就是按照:所有股票池中,按如下条件筛选,pe<20 , pb<2 ,ps<5 , total_mv<1000000。然后选择排名靠前的10只,方法下面会说到。完了等权重分配。 具体如下: 1....
  • 多因子权重优化方法比较

    千次阅读 2017-12-14 13:32:22
    多因子量化投资体系中,具有稳定的预期收益,可解释的经济驱动理论,与其他因子的低相关性是选择alpha因子的关键指标。本篇文章中,我们以此为因子选取标准,简单地构建了自己的因子库,总共包括八个大类因子,每...
  • 我是不是直接说因子有点快了,我入门是从优矿开始的,如果有不太了解多因子模型的,可以去优矿网找找文章来看看。这里推荐几篇帖子供给大家。量化分析师的Python日记【第14天:如何在优矿上做Alpha对冲模型】​uq...

空空如也

空空如也

1 2 3 4 5 6
收藏数 108
精华内容 43
关键字:

多因子alpha