年初至今47.4%,年化77.1%的ETF动量轮动,量化系统升级至v1.1(代码+数据)

今天聊聊期货。

在AI量化人眼中,ETF,指数,可转债,股票,甚至外汇,加密货币,都差不多,反正就是OHLCV量化数据处理,指标化,建模等。当然,股票的基本面背后是公司,可以玩的维度多一些罢了。

但期货有点不同。

期货本质是合约,合约是会到期的,一个合约存续可能就几个月。

这就引出一个“期货主力连续合约”的概念。成交量最大的合约的K线连成,主力合约指的是持仓量最大的合约。一般情况下,持仓量最大的合约,其成交量也是最大的。因为它是市场上最活跃的合约,也是最容易成交的合约,所以投机者基本上都在参与这个合约。

按我们的习惯,代码说话:

import akshare as ak

df = ak.futures_display_main_sina()
df['_id'] = df['symbol']
print(df)
mongo_utils.write_df('future_basic', df, drop_tb_if_exist=True)

图片

一共75个连续合约,覆盖商品期货和股指期货。

从量化的角度,至少在准备数据这一块,期货要简单得多,ETF少多1000多支,还要复权,有估值数据,还要区分lof。可转债少一点,500多支,但转债要与正股关联看基本面;股票有基本面财务数据等。

使用akshare读取期货连续合约的历史价格:

# 期货连续合约
def load_futures(symbol, start_date='20050101', end_date=datetime.now().strftime("%Y%m%d")):
    df = ak.futures_main_sina(symbol=symbol, start_date=start_date, end_date=end_date)
    cols = {'日期': 'date', '开盘价': 'open', '收盘价': 'close', '最高价': 'high', '最低价': 'low', '成交量': 'volume',
            '换手率': 'turn_over', '成交额': 'amount','动态结算价':'vwap','持仓量':'Open Interest'}
    df.rename(columns=cols, inplace=True)
    return df

这里与股票不同在于两个:“动态成交价”,就是按交易量加权的成交价,其实港美股也用,就是vwap。持仓量,这个就是“未平合约”,英文名叫“Open Interest”。

熟悉Backtrader的同学可能恍然大悟,看来Backtrader的作者看来是为期货定制的,data里必须有一项叫Open Interest,这就是这东东。

图片

图片

画出来看看,看着就很刺激,这里没有人聊“价值投资”,会聊基本面和宏观,除了部分套取保值,对冲风险之外,就是投机,合法投机。

数据已经打包上传至星球,请大家前往下载:

图片

CTA也是量化发源地了吧,与外汇的RA,不知道谁更早,没有考证过。

有杠杆,可多空,可高频,T+0,怎么看都是为AI量化而来的。

星球同学表示担心, 以及我的观点:做投资,安全是第一的,这个永远正确,欢迎这样的讨论与分享。

图片

我发现,大家对AI量化的热情还是很高的。昨天线上会议,一个同学问我打算做多久,我当时脱口而出是“永远”。

持续更新内容并不容易,尤其还得有点东西。每行代码都是敲出来,充分去调试,去测试。但大家的热情还是让我觉得,这是值得做下去很有意义的事情。

大家第一次交流,如何更好的学习,更好可持续发展,更优质产出代码和策略,更好的服务大家,也给了我很多启发,后续这样的讨论要持续做下去。

框架升级为v1.1:

2023-09-20

quantlab 1.1
1、优化了Rebalance函数,未选中的标的,先清仓。
2、增加一过滤,如果当前持仓只有一项,且就是当前选中的这一支,这时跳过调仓,避免 weight: 1.0这样不停调仓。
3SelectTopK 如果前置Algo没有selected,则不进行排序选择。
4toml格式写策略迁移过来了,在根目录proj_parser.py里。

我们先把toml模板以及几个策略迁移过来。

若使用ETF的趋势交易:代码在如下位置:

Rebalance进行了优化,未选中的symbol,要选清仓。然后按权重买卖标的。


class Rebalance(Algo):

def __init__(self):
super(Rebalance, self).__init__()

def __call__(self, target):
if "weights" not in target.temp: return True targets = target.temp["weights"] # print(targets) if type(targets) is pd.Series: targets = targets.to_dict() symbols = target.get_current_holding_symbols() for s in symbols: if s not in targets.keys(): # logger.info('清空:{}'.format(s)) target.close(s) # 如果调仓就1个,且当前有持仓就1个,二者还相等,那不执行 if len(targets.keys()) == 1 and list(targets.keys())[0] in symbols and len(symbols) == 1: return True for data, w in targets.items(): #logger.info('调仓:{},{}'.format(data,w)) target.order_target_percent(data, w*0.99) return True

 

图片

代码已经更新至星球:

图片

name = 'ETF趋势交易_动量轮动'
id = 'E9B913DF-6C4E-6058-20DC-FD0D83777A60'
desc = '使用rsrs20日动量两个指标,对特定ETF池进行轮动。'

[data]
start_date = '20230101'
end_date = ''
symbols = [
        '159915.SZ', # 创业板ETF
        '510300.SH', # 沪深300ETF
        '518880.SH', # 黄金ETF
        '513110.SH', # 纳指100指数
        '513520.SH', # 日经ETF
]
fields = ['roc(close,20)','roc_20>0.02','roc_20<-0.02','slope_pair(high,low,18)','rsrs>1.0','rsrs<0.8']
names = ['roc_20','buy','sell','rsrs','rsrs_gte','rsrs_lte']
data_folder = 'etfs' # 数据在data下的目录

[benchmark]
symbols=['000300.SH']
data_folder = 'index' # 数据在data下的目录

#[[algos]]
#name = 'RunWeekly'

[[algos]]
name = 'SelectBySignal'
rules=['buy']
at_least_count=1

[[algos]]
name = 'SelectHolding'

[[algos]]
name = 'SelectBySignal'
rules=['sell']
at_least_count=1
exclude= true

[[algos]]
name = 'SelectTopK'
factor_name='roc_20'
K=1

[[algos]]
name = 'WeightEqually'

[[algos]]
name = 'Rebalance'

图片

这个尖角是日经指数造成的。

图片

图片

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

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

相关推荐

发表回复

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