因子表达式重构及quantstats替换:quantlab 1.3

今日计划:

1、gui框架,回测主体界面,实盘界面框架。

2、走通回测结果展示。

把回测框架搭了一下:

图片

投资理念

昨天我在星球发了一些期货大神的经验总结,一个星友发出了灵魂考问:

什么是正确的逻辑,什么是正确的理念和正确的方法?

关注我的老朋友知道,我是把投资当成科学来研究的,不会上升到玄学的领域。我在这里尝试回复一下。

逻辑,理念和方法,加总起来是一个叫“投资体系”的东西。这是认知层面的东西,不具体到某一个策略或者操作手法,这都是“术”的层面。

很喜欢塔勒布关于不确定的研究,里面的“杠铃策略”尤是。

一端代表极保守,另一个代表追求高风险高收益。

有同学说,期货风险多高呀,老师真要实战嘛。我的回应的,很多人也是这么看股票的。风险第一位是对的,但知道自己在做什么,边界在哪里更重要。

我说一个反脆弱的财富增长“体系”。比如你有500万的自由现金流,你该如何投资?或者做资产配置?股票?股票还是房产。

90%做大类资产配置,股债平衡,追求长期10%的年化收益。拿10%(约50万)可以配置高风险高收益的投资,比如期货。这个10%的50万,假如20倍的杠杆,可以分成10份,一次投资成功翻一番。

你说如果10次都输了呢?450万的本金,长期来看,下一期可以补充高风险投资本金需要,要不会带来巨大的心理压力和实际的本金损失,机会来临,就可能逆风翻盘。

这是道的层面的东西,知道自己在做什么很关键。

高手从来不是赌,是构建系统,“先胜而后求战”。

量化调研

调研了一下国内几个开源量化软件或平台。

总结下来:

1、数据平台,给普通用户做数据服务的。传统金融软件就是日K线+技术指标,或者支持一些表达式宏。想做更复杂的事情,不太方便。高级用户有自己导出,或者自己分析的需求,但金融数据库相对普通用户又太贵,因此有了一些量化数据分析平台。

2、策略开发:在线量化平台,quantopian模式,多数都转私募或者服务B端了。含数据服务,回测系统及实盘对接。——这里的难点当然还是策略,平台也做培训服务。

3、交易端,主要是期货CTA。从服务私募到服务高级用户,to B到to 大C的逻辑,外加知识付费为主。

数据平台是一个细致的体力活,数据是量化的起点,但离量化还有距离。一些数据平台也会按一些指标跑出规律,甚至给出回测的分析。

在线量化平台,数据为量化策略服务。难点在于有能力写策略的人,往往不那么需要这样一个平台,而且还是速度慢,不灵活以及策略安全性的问题。这可能也是quantopian不长久的原因吧。

交易离钱最近,直命痛点。

规划中的平台,应该这样——以实盘为导向:

1、数据,不用大而全,够用就好,准备一些有代表性的,用户可以方便的更新,导入与管理。

2、策略开发,足够高效与易用。很多平台开发策略,调试策略相当费劲,而且还需要持续担心是否出bug。内置大量结构化、模块化的算子,因子挖掘,组合优化,参数调优等等。

3、实盘。按需对接,比如相对容易的,期货。

4、有gui本地运行,交易还是需要干预的,不是所有人的都会写代码。

小结

七年之约,只是开始。

践行长期主义。

万物之中,希望至美。

人工智能与金融投资,都是长坡厚雪,且还是为数不多,可以满足个人英雄主义情结的地方。

走,一起赶路吧。

今日工作:

1、因子表达式重构

2、整合ffn代码,去掉pyfolio, quantstats。 

星球的合伙人计划,已有三名位同学入选,本周若超过10位,将组织第一次例会,探讨平台发展计划,答疑等。

最近做了一些调研,实盘对接做得好的几家,主要集中在期货量化。因为期货CTA本身就是合规,甚至是鼓励的。除了加密货币,最适合量化的领域是期货的CTA或者外汇的RA。

星球很多同学反馈,代码用起来有难度,其实是的,读别人的代码本身就有难度,何况叠加对量化、金融、人工智能的理解。

因此,后续我尽可能把易用性做出来,产品化、服务化

系统代码已经发布至星球:

图片

因子表达式重构的原因:

之前考虑到rank这个函数,需要截面排序,因此需要全体数据进行运算之后,才能rank,因此引入了group_by(‘date’),当然也引入了调试的复杂性。从帮助大家简单理解的角度,我决定去掉groupby。

Duckdbloader使用上没有变化:

在计算因子表达式时,单个dataframe循环计算:

def load(self, fields=None, names=None):
    dfs = self._load_dfs()
    # df = self._reset_index(df)

    if not fields or not names or len(fields) != len(names):
        return self._concat_dfs(dfs)
    else:
        # 截面rank需要
        dfs_expr = []
        for df in tqdm(dfs):
            for field, name in tqdm(zip(fields, names)):
                df[name] = calc_expr(df, field)
            dfs_expr.append(df)
        df = self._concat_dfs(dfs_expr)
        # todo 如果需要支持截面rank,逻辑加在这里
        return df

这里,表达式函数的实现就可以非常简单。

输入是pd.Series以及其它参数:

def shift(se: pd.Series, N):
    return se.shift(N)


def roc(se: pd.Series, N):
    return se / shift(se, N) - 1

rolling的函数实现也变得简单:

import numpy as np
import pandas as pd
from scipy.stats import percentileofscore


def rolling(se, N, func):
    ind = getattr(se.rolling(window=N), func)()
    return ind


def sum(se: pd.Series, N):
    return rolling(se, N, 'sum')


def max(se, N):
    return rolling(se, N, 'max')


def min(se, N):
    return rolling(se, N, 'min')


def std(se, N):
    return rolling(se, N, 'std')


def avg(se, N):
    se = rolling(se, N, 'mean')
    return se


def mean(se, N):
    return avg(se, N)


def idxmax(se, N):
    return rolling(se, N, 'argmax')


def idxmin(se, N):
    return rolling(se, N, 'argmin')


def ts_rank(se, N):
    return se.rolling(N).apply(lambda x: percentileofscore(x, x[-1]) / len(x))


def _zscore(se):
    return se - se.mean() / se.std()


def zscore(se: pd.Series, N):
    return se.rolling(window=N).apply(lambda x: _zscore(x))


def _slope(x):
    try:
        x = x / x.iloc[0]  # 这里做了一个归一化        slope = np.polyfit(range(len(x)), x, 1)[0]
        return slope
    except:
        return -1


def slope(se, N):
    result = se.rolling(N, min_periods=2).apply(lambda x: slope(x))
    return result


def quantile(se, N, qscore):
    return se.rolling(N, min_periods=1).quantile(qscore)


def bias(se, N):
    return se.rolling(N).apply(lambda x: x / x.mean())


def _qcut(se: pd.Series, quantiles, N):
    if len(se) < 3:
        return
    return pd.qcut(se, quantiles, labels=range(0, N), duplicates='drop').astype('float')


def qcut(se: pd.Series, N):
    quantiles = [step / 100 for step in range(0, 100, int(100 / N))]
    if len(quantiles) <= N:
        quantiles.append(1)
    labels = se.rolling(N).apply(
        lambda sub_se: _qcut(sub_se, quantiles, N))
    return labels

本次重构没有用到talib,列为todo。

验证alpha158,没有问题:

图片

借用了ffn计算指标的功能,取代了quantstats。

图片

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/103860
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 2024 年 7 月 29 日
下一篇 2024 年 7 月 29 日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注