In [1]:
%matplotlib inline
%time from hikyuu.interactive import *
use_draw_engine('matplotlib')

std::cout are redirected to python::stdout
std::cerr are redirected to python::stderr
2021-02-13 00:01:01.515 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)
2021-02-13 00:01:03.001 [HKU-I] - 0.03s Loaded Data. (StockManager.cpp:150)
Wall time: 2.77 s


# 一、策略分析¶

S1和B1就这样循环

## 策略分析¶

• 买入：日线EMA1（快线）和EMA2（慢线）金叉向上
• 卖出：日线EMA1（快线）和EMA2（慢线）死叉向下

• 初次建仓：使用50%的资金
• 买入：初次建仓时持股数的50%
• 卖出：初次建仓时持股数的50%

# 二、实现系统部件¶

## 自定义系统有效性策略¶

In [2]:
def getNextWeekDateList(week):
from datetime import timedelta
py_week = week.datetime()
next_week_start = py_week + timedelta(days = 7 - py_week.weekday())
next_week_end = next_week_start + timedelta(days=5)
return get_date_range(Datetime(next_week_start), Datetime(next_week_end))
#ds = getNextWeekDateList(Datetime(201801010000))
#for d in ds:
#    print(d)

In [3]:
def DEMO_CN(self):
""" DIF > DEA 时，系统有效
参数：
fast_n：周线dif窗口
slow_n: 周线dea窗口
"""
k = self.to
if (len(k) <= 10):
return

#-----------------------------
# 周线
#-----------------------------
week_q = Query(k[0].datetime, k[-1].datetime, ktype=Query.WEEK)
week_k = k.get_stock().get_kdata(week_q)

n1 = self.get_param("week_macd_n1")
n2 = self.get_param("week_macd_n2")
n3 = self.get_param("week_macd_n3")
m = MACD(CLOSE(week_k), n1, n2, n3)
fast = m.get_result(0)
slow = m.get_result(1)

x = fast > slow
if (x[i] >= 1.0):
#需要被扩展到日线（必须是后一周）
date_list = getNextWeekDateList(week_k[i].datetime)
for d in date_list:


## 自定义信号指示器¶

In [4]:
#这个例子不需要，已经有内建的SG_Cross函数可直接使用


## 自定义资金管理策略¶

In [5]:
class DEMO_MM(MoneyManagerBase):
"""
初次建仓：使用50%的资金
买入：初次建仓时持股数的50%
卖出：初次建仓时持股数的50%
"""
def __init__(self):
super(DEMO_MM, self).__init__("MACD_MM")
self.set_param("init_position", 0.5) #自定义初始仓位参数，占用资金百分比

def _reset(self):
#pass

def _clone(self):
mm = DEMO_MM()
#return DEMO_MM()

def _get_buy_num(self, datetime, stk, price, risk, part_from):
tm = self.tm
cash = tm.current_cash

#如果信号来源于系统有效条件，建立初始仓位
if part_from == System.Part.CONDITION:
#return int((cash * 0.5 // price // stk.atom) * stk.atom)  #MoneyManagerBase其实已经保证了买入是最小交易数的整数
return int(cash * self.get_param("init_position") // price)

#非初次建仓，买入同等数量

def _getSellNumber(self, datetime, stk, price, risk, part_from):
tm = self.tm
position = tm.get_position(stk)
current_num = int(position.number * 0.5)

#记录第一次卖出时的股票数，以便下次以同等数量买入

return current_num #返回类型必须是整数


# 三、构建并运行系统¶

## 修改设定公共参数¶

In [6]:
#System参数
#delay=True #(bool) : 是否延迟到下一个bar开盘时进行交易
#delay_use_current_price=True #(bool) : 延迟操作的情况下，是使用当前交易时bar的价格计算新的止损价/止赢价/目标价还是使用上次计算的结果
#max_delay_count=3 #(int) : 连续延迟交易请求的限制次数
#tp_monotonic=True #(bool) : 止赢单调递增
#tp_delay_n=3 #(int) : 止盈延迟开始的天数，即止盈策略判断从实际交易几天后开始生效
#ignore_sell_sg=False #(bool) : 忽略卖出信号，只使用止损/止赢等其他方式卖出
#ev_open_position=False #(bool): 是否使用市场环境判定进行初始建仓

cn_open_position=True #(bool): 是否使用系统有效性条件进行初始建仓

#MoneyManager公共参数
#auto-checkin=False #(bool) : 当账户现金不足以买入资金管理策略指示的买入数量时，自动向账户中补充存入（checkin）足够的现金。
#max-stock=20000 #(int) : 最大持有的证券种类数量（即持有几只股票，而非各个股票的持仓数）
#disable_ev_force_clean_position=False #(bool) : 禁用市场环境失效时强制清仓
#disable_cn_force_clean_position=False #(bool) : 禁用系统有效条件失效时强制清仓


## 设定私有参数及待测试标的¶

In [7]:
#账户参数
init_cash = 500000 #账户初始资金
init_date = '1990-1-1' #账户建立日期

#信号指示器参数
week_n1 = 12
week_n2 = 26
week_n3 = 9

#选定标的，及测试区间
stk = sm['sz000002']

#如果是同一级别K线，可以使用索引号，使用了不同级别的K线数据，建议还是使用日期作为参数
#另外，数据量太大的话，matplotlib绘图会比较慢
start_date = Datetime('2016-01-01')
end_date = Datetime()


## 构建系统实例¶

In [8]:
#创建模拟交易账户进行回测，初始资金30万
my_tm = crtTM(date=Datetime(init_date), init_cash = init_cash)

#创建系统实例
my_sys = SYS_Simple()

my_sys.set_param("cn_open_position", cn_open_position)

my_sys.tm = my_tm
my_sys.cn = crtCN(DEMO_CN,
{'week_macd_n1': week_n1, 'week_macd_n2': week_n2, 'week_macd_n3': week_n3},
'DEMO_CN')
my_sys.sg = SG_Cross(EMA(n=week_n1), EMA(n=week_n2))
my_sys.mm = DEMO_MM()


## 运行系统¶

In [9]:
q = Query(start_date, end_date, ktype=Query.DAY)
my_sys.run(stk, q)

#将交易记录及持仓情况，保存在临时目录，可用Excel查看
#临时目录一般设置在数据所在目录下的 tmp 子目录
#如果打开了excel记录，再次运行系统前，记得先关闭excel文件，否则新的结果没法保存
my_tm.tocsv(sm.tmpdir())


# 四、查看资金曲线及绩效统计¶

In [10]:
#绘制资金收益曲线
x = my_tm.get_profit_curve(stk.get_datetime_list(q), Query.DAY)
#x = my_tm.getFundsCurve(stk.getDatetimeList(q), KQuery.DAY) #资金净值曲线
PRICELIST(x).plot()

In [11]:
#回测统计
per = Performance()
print(per.report(my_tm, Datetime.now()))

帐户初始金额: 500000.00

R乘数期望值: 0.08



# 五、或许想看下图形¶

In [12]:
#自己写吧


# 六、或许想看看所有股票的情况¶

In [13]:
import pandas as pd
def calTotal(blk, q):
per = Performance()
s_name = []
s_code = []
x = []
for stk in blk:
my_sys.run(stk, q)
per.statistics(my_tm, Datetime.now())
s_name.append(stk.name)
s_code.append(stk.market_code)
x.append(per["当前总资产"])
return pd.DataFrame({'代码': s_code, '股票': s_name, '当前总资产': x})

%time data = calTotal(blocka, q)

Wall time: 28.5 s

In [14]:
#保存到CSV文件
#data.to_csv(sm.tmpdir() + '/统计.csv')
data[:10]

Out[14]:

0 SH600770 综艺股份 567335.0
1 SZ000532 华金资本 584465.0
2 SZ300238 冠昊生物 780428.0
3 SH601777 *ST力帆 792940.0
4 SH600781 *ST辅仁 548118.0
5 SH600671 *ST目药 411071.0
6 SZ002888 惠威科技 797464.0
7 SH603599 广信股份 536307.9
8 SH600086 退市金钰 621961.0
9 SH600363 联创光电 644507.0
In [ ]: