我们就用 数字货币交易策略大全! 中的套利来举例吧。
# 基础库导入
from __future__ import print_function
from __future__ import division
import warnings
# warnings.filterwarnings('ignore')
# warnings.simplefilter('ignore')
import time
import moment
import random
import pandas as pd
import numpy as np
import mplfinance as mpf
import cufflinks as cf
cf.set_config_file(theme='pearl',sharing='public',offline=True)
from tqdm.autonotebook import tqdm
tqdm.pandas(desc="progress")
import matplotlib.pyplot as plt
import ipywidgets
%matplotlib inline
import qrcode
from IPython.display import Image
import psutil
g_cpu_cnt = psutil.cpu_count(logical=True) * 1
import os
import sys
import logging
# 使用insert 0即只使用github,避免交叉使用了pip安装的abupy,导致的版本不一致问题
sys.path.insert(0, os.path.abspath('../'))
import abupy
# from abupy import env
from abupy.CoreBu import ABuEnv
# from abupy.AlphaBu import ABuPickTimeExecute
from abupy import AbuFactorAtrNStop, AbuFactorPreAtrNStop, AbuFactorCloseAtrNStop, AbuFactorBuyBreak
from abupy import AbuFactorSellBreak
from abupy import abu, EMarketTargetType, AbuMetricsBase, ABuMarketDrawing, ABuProgress, ABuSymbolPd
from abupy import EMarketTargetType, EDataCacheType, EMarketSourceType, EMarketDataFetchMode, EStoreAbu, AbuUmpMainMul
from abupy import AbuUmpMainDeg, AbuUmpMainJump, AbuUmpMainPrice, AbuUmpMainWave, feature, AbuFeatureDegExtend
from abupy import AbuUmpEdgeDeg, AbuUmpEdgePrice, AbuUmpEdgeWave, AbuUmpEdgeFull, AbuUmpEdgeMul, AbuUmpEegeDegExtend
from abupy import AbuUmpMainDegExtend, ump, Parallel, delayed, AbuMulPidProgress
from abupy import ABuFileUtil
pd.options.display.max_rows = 222
from IPython.display import display
from IPython.core.display import HTML
from pprint import pprint, pformat
# 关闭沙盒数据
abupy.env.disable_example_env_ipython()
# abupy.feature.g_price_rank_keys = [12, 25, 50, 100, 200]
# abupy.feature.g_deg_keys = [12, 25, 50, 100, 200]
#os.path.join(ABuEnv.g_project_cache_dir, 'abu_socket_progress')
import numpy as np
import pandas as pd
import mplfinance as mpf
from datetime import datetime as dt
import mplfinance as mpf
import talib
from talib import MA_Type
from talib.abstract import *
pd.options.display.max_rows = 6
K_DEFAULT_DT_FMT = "%Y-%m-%d"
# 开高低收
def ohlc(x):
if len(x):
ohlc={ "open":x["open"][0],
"high":max(x["high"]),
"low":min(x["low"]),
"close":x["close"][-1],
"volume":sum(x["volume"])}
return pd.Series(ohlc)
def fix_date(date_str):
"""
修复日期不规范的写法:
eg. 2016-1-1 fix 2016-01-01
eg. 2016:01-01 fix 2016-01-01
eg. 2016,01 01 fix 2016-01-01
eg. 2016/01-01 fix 2016-01-01
eg. 2016/01/01 fix 2016-01-01
eg. 2016/1/1 fix 2016-01-01
eg. 2016:1:1 fix 2016-01-01
eg. 2016 1 1 fix 2016-01-01
eg. 2016 01 01 fix 2016-01-01
.............................
不使用时间api,直接进行字符串解析,执行效率高,注意fix_date内部会使用fmt_date
:param date_str: 检测需要修复的日期str对象或者int对象
:return: 修复了的日期str对象
"""
if date_str is not None:
# 如果是字符串先统一把除了数字之外的都干掉,变成干净的数字串
if isinstance(date_str, six.string_types):
# eg, 2016:01-01, 201601-01, 2016,01 01, 2016/01-01 -> 20160101
date_str = ''.join(list(filter(lambda c: c.isdigit(), date_str)))
# 再统一确定%Y-%m-%d形式
date_str = fmt_date(date_str)
y, m, d = date_str.split('-')
if len(m) == 1:
# 月上补0
m = '0{}'.format(m)
if len(d) == 1:
# 日上补0
d = '0{}'.format(d)
date_str = "%s-%s-%s" % (y, m, d)
return date_str
def week_of_date(date_str, fmt=K_DEFAULT_DT_FMT, fix=True):
"""
输入'2016-01-01' 转换为星期几,返回int 0-6分别代表周一到周日
:param date_str: 式时间日期str对象
:param fmt: 如date_str不是%Y-%m-%d形式,对应的格式str对象
:param fix: 是否修复日期不规范的写法,eg. 2016-1-1 fix 2016-01-01
:return: 返回int 0-6分别代表周一到周日
"""
if fix and fmt == K_DEFAULT_DT_FMT:
# 只针对%Y-%m-%d形式格式标准化日期格式
date_str = fix_date(date_str)
return dt.strptime(date_str, fmt).weekday()
def to_abu_df(df):
""" 转换为Abu/Bbu常用的格式"""
df['date'] = df.index
df['date_time'] = df.index
# df_d.date = pd.to_datetime(df_d['date'],format='%Y%m%d')
# df_d['date'] = pd.to_datetime(df_d['date'], format="%m/%d/%Y")
"""
Changing the format but not changing the type:
df['date'] = pd.to_datetime(df["date"].dt.strftime('%Y-%m'))
"""
df['date'] = df['date'].dt.strftime('%Y%m%d')
# df['date_week'] = df['date'].apply(lambda x: week_of_date(str(x), '%Y%m%d'))
df['date_week'] = df['date'].apply(lambda x: week_of_date(str(x), '%Y%m%d'))
df['date_time'] = df['date_time'].dt.strftime('%Y%m%d%H%M%S').astype(int)
df['pre_close'] = df['close'].shift(1)
df['pre_close'].fillna(df['open'], axis=0, inplace=True)
df['p_change'] = np.where(df['pre_close'] == 0, 0,
(df['close'] - df['pre_close']) / df['pre_close'] * 100)
df['p_change'] = df['p_change'].apply(lambda x: round(x, 3))
df = df[['open','close','high','low','volume','date','date_time','pre_close','date_week','p_change']]
return df
上面易大堆代码是常用库导入和定义一些下面常用到的 function,不用详细了解。
下面是某数字货币远/近期合约的原始行情数据和K线图。
raw_data = pd.read_csv('/opt/notebook/data/HUOBI_ETH_CQ.csv', index_col='datetime')
raw_data['date_time'] = pd.to_datetime(raw_data.index)
raw_data.index = raw_data['date_time']
raw_data = raw_data.resample('2H').apply(ohlc)
# raw_data = raw_data[raw_data.index>='2020-06-15 06:00:00']
raw_data
tmp_df = raw_data[['volume', 'open', 'close', 'high', 'low']]
tmp_df['date_time'] = tmp_df.index
tmp_df.columns = [ 'Volume', 'Open', 'Close', 'High', 'Low', 'Date']
# qf=cf.QuantFig(tmp_df, title='ETH 当季', legend='top',name='GS')
# qf.add_bollinger_bands(boll_std=2.0)
# qf.iplot()
tmp_df
raw_data_next = pd.read_csv('/opt/notebooks/data/HUOBI_ETH_NQ.csv', index_col='datetime')
raw_data_next['date_time'] = pd.to_datetime(raw_data_next.index)
raw_data_next.index = raw_data_next['date_time']
raw_data_next = raw_data_next.resample('2H').apply(ohlc)
tmp_df = raw_data_next[['volume', 'open', 'close', 'high', 'low']]
tmp_df['date_time'] = tmp_df.index
tmp_df.columns = [ 'Volume', 'Open', 'Close', 'High', 'Low', 'Date']
qf=cf.QuantFig(tmp_df, title='ETH 下季', legend='top',name='GS')
qf.add_bollinger_bands(boll_std=2.0)
qf.iplot()
raw_data_next
就一些行情数据/ohlc数据,什么也看不出来啊。 但是如果我们:
kl_cur = raw_data[raw_data.index>='2020-06-15 06:00:00']
kl_next = raw_data_next[raw_data_next.index<='2021-01-22 04:00:00']
print('看看长度是否一致:' , len(kl_cur), len(kl_next))
kl = kl_cur.copy()
kl = kl_cur.copy()
kl.loc[:,'close'] = (kl_next.loc[:,'close'] - kl_cur.loc[:,'close']) / kl_cur.loc[:,'low']
kl['bollUPPER'], kl['bollMIDDLE'], kl['bollLOWER'] = talib.BBANDS(
# close narray
kl['close'],
# time default 20
timeperiod=200,
# number of non-biased standard deviations from the mean
nbdevup=2, # 2,
nbdevdn=2, # 2,
# Moving average type: simple moving average here
matype=0)
kl[['close', 'bollUPPER', 'bollMIDDLE', 'bollLOWER']].plot( figsize=(16,8))
上面有一行关键的代码
kl.loc[:,'close'] = (kl_next.loc[:,'close'] - kl_cur.loc[:,'close']) / kl_cur.loc[:,'low']
它的关键作用就是拿下期合约收盘价减去当期合约收盘价,再除以当期合约的收盘价 ,最后计算出来的就是上图中的蓝线-close, 它围绕着绿色线上下波动。
我们还看到, 蓝色收盘价线80% - 90%的时间都是在橙色线和红色线圈出来的范围内波动的,很少出轨,即便偶尔出轨,也很大机率回到绿线附近 —— —— 就好比男人偶尔出轨,最终还是要回归家庭的。当然跟小三私奔可能小概率发生。
那么每次价格出轨时,我们可不可以赌它稍晚的时候会回归到绿线附近呢? 听起来是个好主意。 在回答“可不可以”之前,我想先回答另一问题。
“为什么搞量化交易这么复杂! 想炒个币还要会编程吗?!!”
回答是要的, 下面表格中的 1、2、3、4、5、6…… 都是需要会编程的, 上面的例子仅是下表的第3点。或者读完这篇文章后你可以想一下:刚才上图我们称之为“跟踪远近期合约的价格变化的衍生物” 这个需要自己写代码计算出来和画图的,在流行的炒币app找不到这种图,不会编程你怎么玩?
而且交易所也没有“跟踪远近期合约的价格变化的衍生物”这种衍生物产品可以直接交易,需要“逆向工程”为买卖远近期合约来达到交易这种衍生物的效果。
要解决的问题 \各派的做法 | 量化交易的做法 | 传统主观交易的做法 |
---|---|---|
1.下海交易前如何知道即将使用的手法是否能获利? | 回测(下文会讲到) | 直接拿真金白银下海,生死由命 |
2.如何应对7 X 24不间断是市场? | 都是计算机程序在执行 | 不吃不喝不睡呗 |
3.有些想知道数据和想看的图表在已有交易app中找不到怎么办? | 自己写点代码呗 | 啊!这个真没办法。。。 |
4.币市价格变化太大,垃圾币0.x就能从黄金变成废纸怎么应对? | 计算机cpu够快就行 | 啊!破产,玩完,本剧终。。 |
5.如何扩大规模(管理更多资金/账户)? | 增加计算机/服务器 | |
【表1:量化交易 vs 传统主观交易】
上面例子只是量化交易在的小儿科了,在做更深度的分析的时候,还需要掌握人工智能:
当然这又是另外一个比较深入的话题了,笔者有另外一个 notebook 专门讲这个的。
编程+机器学习这两个知识点就够做量化金融了吗? 在上面的小例子中,怎么画出橙色线和红色线 把80% - 90%时间的价格都圈在它们的范围内? 这就需要数学了。
那么随便找到一个会编程的程序员就能搞量化交易了吗? 这个图是量化交易员的面试/机试题 能回答这个问题。
ok, 回归正题: 那么每次价格出轨我们可不可以赌它稍晚的时候会回归到绿线附近呢? 这就回到 表1中第1项 —— 回测。回测也称作历史回测,就是按照某些交易逻辑(选股/标的、择时、仓位管理等)在历史数据上再走一次,输出收益曲线各项度量指标。 同样的交易手法在未来的交易中不一定和回测结果保持高度一致,但是往往很类似————基于人性不变、历史重演的基本事实。
给程序员和数据分析师的5分钟极速入坑量化交易课程 介绍了一个简单的回测过程。
以下就是根据上面“那么每次价格出轨我们可不可以赌它稍晚的时候会回归到绿线附近呢?” 设计的一个交易策略的回测结果 (因子代码和详细研究过程不透露哈):
ABuEnv.project_name = '_CQvsNQ_'
_g_project_id = ABuEnv.project_name
def fixed_commission(trade_cnt, price):
global trade_cnts, prices, commissions
trade_cnts.append(trade_cnt)
prices.append(price)
commission = trade_cnt * price * 0.002
commissions.append(commission)
return 40
# return trade_cnt * price * 0.002 * 6
# 手续费
commission_dict = {'buy_commission_func': fixed_commission,
'sell_commission_func': fixed_commission}
def run_load_train():
global abu_result_tuple
abu_result_tuple = abu.load_abu_result_tuple(n_folds=6.2, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name= _g_project_id + 'btcCQvsNQ-1H')
AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=False).plot_max_draw_down()
def select(select):
if select == 'run loop back':
run_loop_back()
else:
run_load_train()
# _ = ipywidgets.interact_manual(select, select=[ 'load train data', 'run loop back'])
run_load_train()
以上就是一个历史回测输出的信息。
这就完了吗? 就可以下海交易了吗?上面只是介绍了从idea到概念化和回测研究阶段。下表是策略从idea 到实盘运营所要经历的流程:
阶段\产品线 | 稳定套利策略 | 日内短线策略 | 波段策略 | 长线策略 |
---|---|---|---|---|
从idea到概念化 | ||||
回测研究阶段 | ||||
实盘代码开发 | 1. --季度合约套利-- | |||
试验盘在跑 | ||||
实盘运作中 | ||||
业绩归因分析 |
总结:
试验盘/实盘时要做的事请看 这个图
看起来很有意思,想学习吗?欢迎参阅 量化 | 给初学者的一些量化建议
长按识别下方二维码在浏览器中阅读本文。
data = 'https://s3-cdn1.litup.me/blog-post/quant-from-idea-to/00.html'
img = qrcode.make(data)
img.save('MyQRCode1.png')
pil_img = Image(filename='MyQRCode1.png')
display(pil_img)