量化双均线策略:(二)判断买入卖出信号

上篇已经介绍了data的获取,此篇介绍ma5与ma10的双均线策略具体实现。双均线策略是一个趋势策略,基本思路是金叉买入,死叉卖出,也就是当ma5向上穿过ma10时,则买入,向下穿过ma10时,则卖出。

主要实现在Strategy类中,输入的变量格式如下:code_list = ['002415', '002416', '000333'],init_cash = 100000,starttime = '2014-01-01',endtime = '2017-11-30',其他几个重要成员变量如下:cash为还剩余的现金;capital_market_value为持仓的市值,按每天的收盘价计算;limit_cash为每只股票分得的仓位,双均线策略中,codelist中的股票平分持仓;position_list为持仓的情况,类型为dict,股票的code为key,持仓的数量为value;data_range为一个回测的日期list,pd.period_range的freq参数选择为B,即工作日;benchmark赋值为sh,即上证指数,以便比对策略结果。

#coding=utf-8
from trade import Trade
from data_repository import DataRepository
import tushare as ts
import pandas as pd
import numpy as np
import math

class Strategy(object):
    def __init__(self, code_list, init_cash, start_time, end_time):
        self.start_time = start_time
        self.end_time = end_time
        self.data_repository = DataRepository.get_instance(code_list, self.start_time, self.end_time)
        self.code_list = code_list
        self.benchmark_code = 'sh'

        self.init_cash = init_cash
        self.cash = init_cash
        self.limited_cash = init_cash/len(code_list)
        self.position_list = {}
        for code in self.code_list:
            self.position_list[code] = 0

        #存储trade对象
        self.trade = Trade()

        d = list(pd.period_range(start=start_time, end=end_time, freq='B'))
        self.date_range = list(map(str, d))
        self.res_df = pd.DataFrame()
        self.res_df['date'] = self.date_range
        self.capital_market_value = []
实际的回测定义在run_simulation函数中,主循环框架为按天循环,通过每天的开盘价判断是否买入或者卖出( 先判断卖出信号,再判断买入信号,具体判断在后面介绍),如果卖出成功,那么将卖出的所得加入到cash变量,对应的position变量调整为0(这个策略为初级策略,都是在每只股票的额度内全买全卖,后续策略会加入仓位控制),并将操作记录下到trade变量中。买入的操作也是类似的。每天买卖判断完成后,再按收盘价计算下持仓的市值,记录到capital_market_value中,以便统计每天的涨跌,计算sharp比率等统计指标。

所有天数回测结束后,将结果记录到res_df中,该变量为pandas类型,以便分析结果。res_df中数据有每天capital_market_value,每天的涨跌幅,每天对应的benchmark的值(因为回测的date_range已经剔除了周末,节假日等情况会存在停市,故capital_market_value与benchmark的值均使用之前第一个值填充),benchmark采用先bfill,然后还可能存在最开头的值是空的情况,在ffill填充,而capital_market_value则是使用df[df['date'] <= date].tail(1)['close']获得。

    def run_simulation(self):
        #按天循环
        for date in self.date_range:
            for code in self.code_list:
                sell_signal, sell_open_price = self.get_sell_signal(code, date)
                direction = -1
                if sell_signal == 1:
                    amount = self.get_sell_amount(code)
                    if amount > 0:
                        commission = self.cal_cost_function(sell_open_price, amount)
                        #更改现金
                        self.cash += sell_open_price*amount
                        self.cash -= commission
                        #更改持仓
                        self.position_list[code] -= amount
                        #加入trade记录
                        self.trade.add_trade(code, sell_open_price, amount, date, direction, commission)

            for code in self.code_list:
                buy_signal, buy_open_price = self.get_buy_signal(code, date)
                direction = 1
                if buy_signal == 1:
                    amount = self.get_buy_amount(code, buy_open_price)
                    if amount > 0:
                        commission = self.cal_cost_function(buy_open_price, amount)
                        #更改现金
                        self.cash -= buy_open_price*amount
                        self.cash -= commission
                        #更改持仓
                        self.position_list[code] += amount
                        #加入trade记录
                        self.trade.add_trade(code, buy_open_price, amount, date, direction, commission)

            #计算每天的市值
            self.capital_market_value.append(self.get_market_value(date))

        #所有天循环结束后,加入到res_df
        self.res_df['capital_market_value'] = pd.Series(self.capital_market_value)
        self.res_df['profolio_daily_return'] = round((self.res_df['capital_market_value']/\
                                                self.res_df['capital_market_value'].shift(1)-1),4)

        self.res_df['benchmark'] = self.get_benchmark_index()
        self.res_df['benchmark'].fillna(method='bfill', inplace=True)
        self.res_df['benchmark'].fillna(method='ffill', inplace=True)
        self.res_df.to_csv('./datares.csv')
benchmark与captial_market_value的取得具体如下。

    def get_benchmark_index(self):
        df = ts.get_k_data(self.benchmark_code, start=self.start_time, end=self.end_time)
        benchmark_list = []
        for date in self.date_range:
            if df[df['date'] == date].empty:
                benchmark_list.append(np.nan)
            else:
                benchmark_list.append(float(df[df['date'] == date]['close']))
        return benchmark_list
    
    #得到某天的持仓股票的市值,加上cash,一并返回
    def get_market_value(self, date):
        market_value = 0
        for code in self.position_list:
            df = self.data_repository.get_onecode_df(code)
            if self.position_list[code] != 0:
                close_price = df[df['date'] <= date].tail(1)['close']
                market_value += self.position_list[code]*float(close_price)
        return round(market_value+self.cash, 2)
最重要的判断买入卖出信号如下。有个小地方需要注意下,给定一个date日期,df = df[df['date'] <= date].tail(3),获取了三天的价格,即今天的价格,昨天的价格,前天的价格,通过前天与昨天的均线价格去判断买入卖出。这是因为每天的均线价格是收盘价的平均(包含当天),策略的思路是每天通过开盘价买入或者卖出,但是当天的均线价格需要等收盘才能得到,也就是当买入或者卖出时候,只能使用昨天与前天的均线价格,否则就使用了未来函数。

    def get_sell_signal(self, code, date):
        df = self.data_repository.get_onecode_df(code)
        sell_signal = 0
        sell_open_price = 0


        if df[df['date'] == date].empty:
            return sell_signal, sell_open_price
        df = df[df['date'] <= date].tail(3)
        if len(df) == 3 and df.iloc[0]['ma5'] > df.iloc[0]['ma10'] and df.iloc[1]['ma5'] < df.iloc[1]['ma10']:
            sell_signal = 1
            sell_open_price = df.iloc[1]['open']
        return sell_signal, sell_open_price


        #以后还要加入判断止盈的方法


    def get_buy_signal(self, code, date):
        df = self.data_repository.get_onecode_df(code)
        buy_signal = 0
        buy_open_price = 0
        if df[df['date'] == date].empty:
            return buy_signal, buy_open_price
        df = df[df['date'] <= date].tail(3)
        if len(df) == 3 and df.iloc[0]['ma5'] < df.iloc[0]['ma10'] and df.iloc[1]['ma5'] > df.iloc[1]['ma10']:
            buy_signal = 1
            buy_open_price = df.iloc[1]['open']
        return buy_signal, buy_open_pric
剩下的几个就比较简单了,判断持仓数量,计算交易手续费。

    def get_sell_amount(self, code):
        return self.position_list[code]


    def get_buy_amount(self, code, price):
        if self.position_list[code] == 0:
            amount = math.floor(self.limited_cash/(price*100))*100
            return amount
        else:
            return 0

    def cal_cost_function(self, price, amount):
        commission = price*amount*0.0003
        #最低5元手续费
        if commission > 5:
            return commission
        else:
            return 5

你可能感兴趣的:(量化双均线策略:(二)判断买入卖出信号)