如何使用Orca开发量化策略?

Posted DolphinDB

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Orca开发量化策略?相关的知识,希望对你有一定的参考价值。

本文讲述关于动量策略的一个实例。动量策略是最著名的定量长短期股票策略之一。自从Jegadeesh和Titman(1993)首次提出这个概念以来,它已广泛出现在学术研究和销售方面的著作中。投资者在动量策略中相信,个股中,过去的赢家将超越过去的输家。

最常用的动量因素是股票除去最近一个月在过去12个月的收益。 在学术出版物中,动量策略通常是一个月调整一次且持有期也是一个月。在本例中,我们每天重新平衡我们的投资组合的1 / 21,并持有新份额21天。为简单起见,我们不考虑交易成本。

步骤 1:加载股票交易数据,对数据进行清洗和筛选,然后为每个公司股票构建一个过去12个月忽略最近一个月的动量信号。

def load_price_data(df):
    USstocks = df[df.date.dt.weekday.between(0, 4), df.PRC.notnull(), df.VOL.notnull()][
                   [\'PERMNO\', \'date\', \'PRC\', \'VOL\', \'RET\', \'SHROUT\']
               ].sort_values(by=[\'PERMNO\', \'date\'])
    USstocks[\'PRC\'] = USstocks.PRC.abs()
    USstocks[\'MV\'] = USstocks.SHROUT * USstocks.PRC
    USstocks[\'cumretIndex\'] = (USstocks + 1)[\'RET\'].groupby(\'PERMNO\', lazy=True).cumprod()
    USstocks[\'signal\'] = (USstocks.shift(21) / USstocks.shift(252) - 1).groupby(
                            \'PERMNO\', lazy=True)[\'cumretIndex\'].transform()
    return USstocks


df = orca.read_csv(\'C:/DolphinDB/Orca/databases/USstocks.csv\')
price_data = load_price_data(df)

注意:以上代码使用了Orca的两个扩展功能。

  1. Orca支持在条件过滤语句中使用逗号代替&,在某些场景下会更高效,参见教程。
  2. Orca的groupby函数提供了lazy参数,配合transform函数使用能够实现DolphinDB的context by功能,参见教程。

步骤 2:为动量策略生成投资组合。

首先,选择满足以下条件的流通股:动量信号值无缺失、当日是正交易量、市值超过1亿美金、以及每股价格超过5美元。

def gen_trade_tables(df):
    USstocks = df[(df.PRC > 5), (df.MV > 100000), (df.VOL > 0), (df.signal.notnull())]
    USstocks = USstocks[[\'date\', \'PERMNO\', \'MV\', \'signal\']].sort_values(by=\'date\')
    return USstocks


tradables = gen_trade_tables(price_data)

其次,根据动量信号,制定10组可交易股票。只保留2个最极端的群体(赢家和输家)。假设我们总是想在21天内,每天多头1美元和空头$1,所以我们每天在赢家组多头$1/21,在输家组每天空头$1/21。在每组中,我们可以使用等权重或值权重, 来计算投资组合形成日期上每个股票的权重。

def form_portfolio(start_date, end_date, tradables, holding_days, groups, wt_scheme):
    ports = tradables[tradables.date.between(start_date, end_date)].groupby(\'date\').filter(\'count(PERMNO) >= 100\')
    ports[\'rank\'] = ports.groupby(\'date\')[\'signal\'].transform(\'rank{{,true,{groups}}}\'.format(groups=groups))
    ports[\'wt\'] = 0.0
    
    ports_rank_eq_0 = (ports[\'rank\'] == 0)
    ports_rank_eq_groups_sub_1 = (ports[\'rank\'] == groups-1)
    if wt_scheme == 1:
        ports.loc[ports_rank_eq_0, \'wt\'] = 
            ports[ports_rank_eq_0].groupby([\'date\'])[\'PERMNO\'].transform(
                r\'(PERMNO->-1count(PERMNO){holding_days})\'.format(holding_days=holding_days)
            )
        ports.loc[ports_rank_eq_groups_sub_1, \'wt\'] = 
            ports[ports_rank_eq_groups_sub_1].groupby([\'date\'])[\'PERMNO\'].transform(
                r\'(PERMNO->1count(PERMNO){holding_days})\'.format(holding_days=holding_days)
            )
    elif wt_scheme == 2:
        ports.loc[ports_rank_eq_0, \'wt\'] = 
            ports[ports_rank_eq_0].groupby([\'date\'])[\'MV\'].transform(
                r\'(MV->-MVsum(MV){holding_days})\'.format(holding_days=holding_days)
            )
        ports.loc[ports_rank_eq_groups_sub_1, \'wt\'] = 
            ports[ports_rank_eq_groups_sub_1].groupby([\'date\'])[\'MV\'].transform(
                r\'(MV->MVsum(MV){holding_days})\'.format(holding_days=holding_days)
            )
    ports = ports.loc[ports.wt != 0, [\'PERMNO\', \'date\', \'wt\']].sort_values(by=[\'PERMNO\', \'date\'])
    ports.rename(columns={\'date\': \'tranche\'}, inplace=True)
    return ports


start_date, end_date = orca.Timestamp("1996.01.01"), orca.Timestamp("2017.01.01")
holding_days = 5
groups = 10
ports = form_portfolio(start_date, end_date, tradables, holding_days, groups, 2)
daily_rtn = price_data.loc[price_data.date.between(start_date, end_date), [\'date\', \'PERMNO\', \'RET\']]

注意:以上代码使用了Orca的扩展功能,即,允许filter, transform等高阶函数,接受一个表示DolphinDB函数或脚本的字符串。参见教程。

步骤3:计算我们的投资组合中的每支股票随后的21天利润/损失。投资组合形成日后21天关闭股票。

def calc_stock_pnl(ports, daily_rtn, holding_days, end_date, last_days):
    dates = ports[[\'tranche\']].drop_duplicates().sort_values(by=\'tranche\')

    dates_after_ages = orca.DataFrame()
    for age in range(1, holding_days+1):
        dates_after_age_i = dates.copy()
        dates_after_age_i[\'age\'] = age
        dates_after_age_i[\'date_after_age\'] = dates_after_age_i[\'tranche\'].shift(-age)
        dates_after_ages.append(dates_after_age_i, inplace=True)

    pos = ports.merge(dates_after_ages, on=\'tranche\')
    pos = pos.join(last_days, on=\'PERMNO\')
    pos = pos.loc[(pos.date_after_age.notnull() & (pos.date_after_age <= pos.last_day.clip(upper=end_date))),
                  [\'date_after_age\', \'PERMNO\', \'tranche\', \'age\', \'wt\']]
    pos = pos.compute()
    pos.rename(columns={\'date_after_age\': \'date\', \'wt\': \'expr\'}, inplace=True)
    pos[\'ret\'] = 0.0
    pos[\'pnl\'] = 0.0

    # use set_index to make it easy to equal join two Frames
    daily_rtn.set_index([\'date\', \'PERMNO\'], inplace=True)
    pos.set_index([\'date\', \'PERMNO\'], inplace=True)
    pos[\'ret\'] = daily_rtn[\'RET\']
    pos.reset_index(inplace=True)
    pos[\'expr\'] = (pos.expr * (1 + pos.ret).cumprod()).groupby(
                    [\'PERMNO\', \'tranche\'], lazy=True).transform()
    pos[\'pnl\'] = pos.expr * pos.ret / (1 + pos.ret)

    return pos


last_days = price_data.groupby(\'PERMNO\')[\'date\'].max()
last_days.rename("last_day", inplace=True)
stock_pnl = calc_stock_pnl(ports, daily_rtn, holding_days, end_date, last_days)

注意:以上代码有一句pos.compute()语句,将一个中间表达式(带有条件过滤的DataFrame)的结果直接计算出来。因为我们只需要对过滤以后的结果进行赋值操作。

此外,以上代码对两个DataFrame调用了set_index函数,然后将一个DataFrame的列赋值给另一个,这类似于将两个DataFrame按索引列进行left join,然后将结果中的对应列赋值。如果直接执行脚本pos[\'ret\'] = pos.merge(daily_rtn, on=[\'date\', \'PERMNO\'])[\'RET\'],则会在计算时进行一次join,赋值时又进行一次join,带来不必要的计算量。

步骤 4:计算投资组合的利润/损失,并绘制随着时间推移的动量策略的累积回报。

port_pnl = stock_pnl.groupby(\'date\')[\'pnl\'].sum()
cumulative_return = port_pnl.cumsum()
cumulative_return.plot()
plt.show()

注意: plot函数会将整个DataFrame所对应的DolphinDB表下载到客户端,然后对齐绘图。在使用时应当注意数据量,避免大量的网络传输带来的性能问题。参见教程。

点此查看完整的代码。关于如何用DolphinDB脚本实现这个策略,请参考官方范例。

以上是关于如何使用Orca开发量化策略?的主要内容,如果未能解决你的问题,请参考以下文章

CCXT如何获取历史行情,为后面开发策略做准备?

用Tkinter打造自己的Python IDE开发工具变量数据共享机制及小白量化策略编写

用Tkinter打造自己的Python IDE开发工具变量数据共享机制及小白量化策略编写

用Tkinter打造自己的Python IDE开发工具变量数据共享机制及小白量化策略编写

小白量化《穿云箭集群量化》量化策略编写

小白量化《穿云箭集群量化》量化策略编写