import backtrader as bt
import os
from pathlib import Path
import datetime


class TestSTrategy(bt.Strategy):
    #set parameter, e.g. setting moving averge period, sell order executed 5 time intervals after buying the asset
    params = (
        ('maperiod', 15), ('exitbars', 5)
    )
    def __init__(self): #define initial varibles
        self.dataclose = self.datas[0].close #save data to datas[0] (data on the first date set)
        self.order = None
        #initialize the MA indicator
        self.sma = bt.indicators.MovingAverageSimple(self.datas[0], period=self.params.maperiod)


    #Show order status
    def notifty_order(self, order):

        # if order is only submitted/accepted but not executed, return nth
        if order.status in [order.Submitted, order.Accepted]:
            return

        if order.status in [order.Completed]: #when buy order is executed
            #record order completed time
            if order.isbuy():
                self.log("Buy Executed {}".format(order.executed.price)) #show buy execute value
            elif order.issell():
                self.log("Sell Executed {}".format(order.executed.price))  #show sell execute value

            self.bar_executed = len(self) #store the length of time at the execution

        elif order.status in[order.Canceled, order.Margin, order.Rejected]: #when buy order is NOT executed
            self.log("Order Canceled/Margin/Rejected")

        self.order = None #if order completed/cancelled, set back to None


    #strategy: determind the buy/sell logic each interval
    def next(self):
        self.log("Close {}".format(self.dataclose[0]))

        if self.order: #self.order <> None, return nth
            return
        #Example 1:
        # #run the following if self.order = None
        #determine asset value we have
        if not self.position: #if not holding that asset: determine buy strategy
            #buy if price decreases 3 cont days
            if self.dataclose[0] < self.dataclose[-1]:
                if self.dataclose[-1] < self.dataclose[-2]:
                    self.log("Buy Create {}".format(self.dataclose[0])) #craete buy order at market value
                    self.order = self.buy() #buy order
        else: #if holding that asset: determine sell strategy
            #determine if the length of time now is >= execution length of time + x time intervals
            if len(self) >= (self.bar_executed + self.params.exitbars):
                self.log("Sell Create{}".format(self.dataclose[0])) #Create sell order at market value
                self.order = self.sell() #sell order

        #Example 2
        if not self.position:  # if not holding that asset: determine buy strategy
            #determine if the closing price is larger than the MA indicator
            if self.dataclose[0] > self.sma[0]:
                self.log("Buy Create {}".format(self.dataclose[0]))
                self.order = self.buy()  # buy order
        else:
            if self.dataclose[0] < self.sma[0]:
                self.log("Sell Create{}".format(self.dataclose[0]))  # Create sell order at market value
                self.order = self.sell()  # sell order


    def log(self, txt):
        dt = self.datas[0].datatime.date(0)
        #print("{} {}".format(dt.isoformat(),txt))

    def stop(self): #showing the tested the parameters
        print(self.params.maperiod, self.broker.getvalue())


if __name__ == '__main__':
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(10000000) #principal

    #trading costs
    cerebro.broker.setcommission(commission=0.001)

    #add strategy
    cerebro.addstrategy(TestSTrategy)

    #or optimize
    cerebro.optstrategy(TestSTrategy, maperiod=(10,31))


    ###########
    #gat data
    data_path = Path(os.getcwd())/'_______'
    data = bt.feeds.YahooFinanceCSVData(dataname=data_path, fromdate=datetime.datetime(2019,1,1), todate=datetime.datetime(2019,12,31))
    ############

    cerebro.adddata(data) #add data to the strategy
    print("Start Portfolio {}". format(cerebro.broker.getvalue())) #Initial portfolio value



    cerebro.run() #run the strategy
    print("Final Portfolio {}".format(cerebro.broker.getvalue()))

    #plotting graphs
    cerebro.plot()
