import pandas as pd
from datetime import datetime
import backtrader as bt
import matplotlib.pyplot as plt
import tushare as ts

plt.rcParams['font.sans-serif'] = ['SimHei']        # 设置画图时的中文显示
plt.rcParams['axes.unicode_minus'] = False          # 设置画图时的负号显示

# 1.数据加载
def get_data(code='600519',startTime='2017-01-01',endTime='2020-01-01'):
    df = ts.get_k_data(code,start=startTime,end = endTime)
    ## 设置为日期格式
    df.index = pd.to_datetime(df.date)
    # df['op']
    # print(df)
    df['openinterest'] = 0
    # print(df)   顺序保持一致
    df = df[['open','high','low','close','volume','openinterest']]
    # print(df)
    return df

stock_df = get_data()

# 加载并读取数据源 dataname:数据来源 fromdate(date格式):开始时间 todate:截至时间
fromdate = datetime(2018,1,1)
todate = datetime(2020,1,1)
data = bt.feeds.PandasData(dataname = stock_df,fromdate=fromdate,todate=todate)

# print(data)
# 2、构建自己的策略
# 上穿20日均线买入,跌穿20日均线卖出
class MyStrategy(bt.Strategy):
    def __init__(self):
        self.dataclose=self.datas[0].close
        self.order = None
        self.buyprice = None
        self.buycomm = None

        self.sma15 = bt.indicators.SimpleMovingAverage(self.datas[0],period=15)

    # 每个bar都会执行一次,回测的每个日期都会执行一次
    def next(self):
        if self.order:
            # 如果有交易正在进行就返回
            return
        '''
        close[0]  是当天的日期收盘价
        close[-1] 是昨天的日期收盘价
        close[-2] 是前天的日期收盘价
        :return:
        '''
        # 如果大于20日均线就买入
        if self.dataclose[0] > self.sma15[0]:
            self.order = self.buy()
        else:
            if self.dataclose[0] <self.sma15[0]:
                self.order = self.close()

    def notify_order(self,order):
        '''
        submitted[提交]:已发送给经纪人,等待确认。
        accepted[接受]:被经纪人接受
        patrial[部分执行]:部分执行。
        completed[完成]:完全执行。
        canceled/cancelled[取消/取消]:被用户取消。
        expried[已过期]:已过期。
        margin[保证金]:没有足够的现金来执行订单。
        rejected[拒绝]:被经纪人拒绝
        :param order:
        :return:
        '''
        if order.status in [order.Submitted,order.Accepted]:
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'Buy Execute, Price:%.2f,Cost:%.2f,Comm%.2f'%(
                        order.executed.price,
                        order.executed.value,
                        order.executed.comm
                    )
                )
                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:
                self.log(
                    'Sell Execute, Price:%.2f,Cost:%.2f,Comm%.2f' % (
                        order.executed.price,
                        order.executed.value,
                        order.executed.comm
                    )
                )
            self.bar_executed=len(self)
        elif order.status in [order.Canceled,order.Margin,order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

    def notify_trade(self, trade):
        if not trade.isclosed:
            return
        self.log(
            'Operation Profit, Gross %.2f, Net %.2f'%
            (trade.pnl,trade.pnlcomm)
        )
    def log(self,txt,dt=None,doprint=True):
        if doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print('%s,%s'%(dt.isoformat(),txt))


if __name__ == '__main__':
    # 3.策略设置
    cerebro = bt.Cerebro()      # 创建大脑
    # 将数据加入回测系统
    cerebro.adddata(data)
    # cerebro.adddata(data1)        # 可以加很多个不同的品种
    # cerebro.adddata(data2)
    # cerebro.adddata(data3)

    # 加入自己的策略
    cerebro.addstrategy(MyStrategy)
    cerebro.addanalyzer(bt.analyzers.SharpeRatio,_name='SharpeRatio')

    # 我想查看最大回撤
    cerebro.addanalyzer(bt.analyzers.DrawDown,_name='DrawDown')

    # 添加经纪人 初始化资金为 100000
    start_cash = 100000
    cerebro.broker.setcash(start_cash)
    # 设置手续费 万分之2
    cerebro.broker.setcommission(0.0002)
    # 每次买80%
    cerebro.addsizer(bt.sizers.PercentSizer,percents=20)

    # 执行回测
    s = fromdate.strftime("%Y-%m-%d")
    t = todate.strftime("%Y-%m-%d")
    print(f"初始资金:{start_cash}\n回测时间:{s}   {t}")
    result = cerebro.run()
    portval = cerebro.broker.getvalue()
    print(f"策略执行完之后的资金:{portval}\n回测时间:{s}   {t}")

    print('夏普比率:\t',result[0].analyzers.SharpeRatio.get_analysis()['sharperatio'])
    # print('最大回撤:\t',result[0].analyzers.DrawDown.get_analysis()['max']['drawdrown'])
    # print('最大回撤:\t',result[0].analyzers.DrawDown.get_analysis()['drawdrown'])
    cerebro.plot()

backtrader为什么交易只进行一次呢_数据