<jupyter_start><jupyter_text>一、策略分析 原始描述建仓条件：expma周线exp1跟exp2金叉向上使用使用 B=50% 的资金买入股票，建仓成功后，卖出条件才能起作用卖出条件S1：expma日线exp1和exp2死叉向下时卖出持仓股 S=50%买入条件B1：expma日线exp1和exp2金叉向上时买入股票数为S（卖出条件S1卖出股数）S1和B1就这样循环清仓条件为：expma周线exp1和exp2死叉时 策略分析市场环境：无系统有效性：周线EMA1（快线）和EMA2（慢线）金叉向上直到两者死叉，系统有效时建立初始仓位信号指示器：- 买入：日线EMA1（快线）和EMA2（慢线）金叉向上- 卖出：日线EMA1（快线）和EMA2（慢线）死叉向下止损/止盈：无资金管理：- 初次建仓：使用50%的资金- 买入：初次建仓时持股数的50%- 卖出：初次建仓时持股数的50%盈利目标：无  二、实现系统部件 自定义系统有效性策略<jupyter_code>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)
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
    for i in range(x.discard, len(x)-1):
        if (x[i] >= 1.0):
            #需要被扩展到日线（必须是后一周）
            date_list = getNextWeekDateList(week_k[i].datetime)
            for d in date_list:
                self._add_valid(d)<jupyter_output><empty_output><jupyter_text>自定义信号指示器<jupyter_code>#这个例子不需要，已经有内建的SG_Cross函数可直接使用<jupyter_output><empty_output><jupyter_text>自定义资金管理策略<jupyter_code>class DEMO_MM(MoneyManagerBase):
    """
    初次建仓：使用50%的资金
    买入：初次建仓时持股数的50%
    卖出：初次建仓时持股数的50%
    """
    def __init__(self):
        super(DEMO_MM, self).__init__("MACD_MM")
        self.set_param("init_position", 0.5) #自定义初始仓位参数，占用资金百分比
        self.next_buy_num = 0
        
    def _reset(self):
        self.next_buy_num = 0
        #pass
        
    def _clone(self):
        mm = DEMO_MM()
        mm.next_buy_num = self.next_buy_num
        #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其实已经保证了买入是最小交易数的整数
            self.next_buy_num = 0 #清理掉上一周期建仓期间滚动买卖的股票数
            return int(cash * self.get_param("init_position") // price)
        
        #非初次建仓，买入同等数量
        return self.next_buy_num
    
    def _getSellNumber(self, datetime, stk, price, risk, part_from):
        tm = self.tm
        position = tm.get_position(stk)
        current_num = int(position.number * 0.5)
        
        #记录第一次卖出时的股票数，以便下次以同等数量买入
        if self.next_buy_num == 0:
            self.next_buy_num = current_num 
            
        return current_num #返回类型必须是整数<jupyter_output><empty_output><jupyter_text>三、构建并运行系统 修改设定公共参数每个系统部件以及TradeManager都有自己的公共参数会影响系统运行，具体可以查看帮助及试验。比如：这个例子当前使用系统有效条件进行初始建仓，那么必须设置系统公共参数cn_open_position为True。否则，没有建立初始仓位的话，后续没有卖出，不会有任何交易。<jupyter_code>#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) : 禁用系统有效条件失效时强制清仓<jupyter_output><empty_output><jupyter_text>设定私有参数及待测试标的<jupyter_code>#账户参数
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()<jupyter_output><empty_output><jupyter_text>构建系统实例<jupyter_code>#创建模拟交易账户进行回测，初始资金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()<jupyter_output><empty_output><jupyter_text>运行系统<jupyter_code>q = Query(start_date, end_date, ktype=Query.DAY)
my_sys.run(stk, q)

#将交易记录及持仓情况，保存在临时目录，可用Excel查看
#临时目录一般设置在数据所在目录下的 tmp 子目录
#如果打开了excel记录，再次运行系统前，记得先关闭excel文件，否则新的结果没法保存
my_tm.tocsv(sm.tmpdir())<jupyter_output><empty_output><jupyter_text>四、查看资金曲线及绩效统计<jupyter_code>#绘制资金收益曲线
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()
#回测统计
per = Performance()
print(per.report(my_tm, Datetime.now()))<jupyter_output>帐户初始金额: 500000.00
累计投入本金: 500000.00
累计投入资产: 0.00
累计借入现金: 0.00
累计借入资产: 0.00
累计红利: 0.00
现金余额: 635819.00
未平仓头寸净值: 0.00
当前总资产: 635819.00
已平仓交易总成本: 0.00
已平仓净利润总额: 135819.00
单笔交易最大占用现金比例%: 49.99
交易平均占用现金比例%: 49.78
已平仓帐户收益率%: 27.16
帐户年复合收益率%: 5.39
帐户平均年收益率%: 5.94
赢利交易赢利总额: 194799.00
亏损交易亏损总额: -58980.00
已平仓交易总数: 7.00
赢利交易数: 5.00
亏损交易数: 2.00
赢利交易比例%: 71.43
赢利期望值: 19402.71
赢利交易平均赢利: 38959.80
亏损交易平均亏损: -29490.00
平均赢利/平均亏损比例: 1.32
净赢利/亏损比例: 3.30
最大单笔赢利: 130416.00
最大单笔亏损: -50616.00
赢利交易平均持仓时间: 34.80
赢利交易最大持仓时间: 71.00
亏损交易平均持仓时间: 66.50
亏损交易最大持仓时间: 94.00
空仓总时间: 1364.00
空仓时间/总时间%: 81.00
平均空仓时间: 194.00
最长空仓时间: 431.00
最大连续赢利笔数: 3.00
最大连续亏损笔数: 1.00
最大连续赢利金额: 132099.00
最大连续亏损金额: -50616.00
R乘数期望值: 0.08
交易机会频率/年: 1.53
年度期望R乘数: 0.12
赢利交易平均R乘数: 0.15
亏损交易平均R乘数: -0.10
最大单笔赢利R乘数: 0.52
最大单笔亏损R乘数: -0.16
最大连续赢利R乘数: 0.18
最大连续亏损R乘数: -0.16<jupyter_text>五、或许想看下图形<jupyter_code>#自己写吧<jupyter_output><empty_output><jupyter_text>六、或许想看看所有股票的情况<jupyter_code>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)
#保存到CSV文件
#data.to_csv(sm.tmpdir() + '/统计.csv')
data[:10]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1349862258953168, 'fraction_numerical': 0.05219660722053066, 'mean_word_length': 4.140089418777944, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '500761', 'license': 'MIT', 'n_tokens_mistral': 4182, 'n_tokens_neox': 4401, 'n_words': 420, 'path': 'hikyuu/examples/notebook/Demo/Demo2.ipynb', 'repo_name': 'lianchaoyun/hikyuu', 'size': '6897'}