<jupyter_start><jupyter_text>ToDo- Clean datasets:    - If opening price jump too much (like 100 times) intra-day, remove data point.<jupyter_code>from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import empyrical
import datetime
import pandas as pd
from backtrader.utils.py3 import iteritems
import pdb
import pyfolio as pf
import decimal
import numpy as np
import math

import matplotlib
%matplotlib inline
def get_data_feed_by_name(data_feed_name, start, end):
    return bt.feeds.YahooFinanceData(dataname=data_feed_name,period='d', fromdate=start,
                                todate=end)

def get_local_data_feed_by_name(data_filename,start,end):
    return bt.feeds.YahooFinanceCSVData(
        dataname=data_filename,
        # Do not pass values before this date
        fromdate=start,
        # Do not pass values after this date
        todate=end,
        reverse=False)
start=datetime.datetime(2019,10,1)
end=datetime.datetime(2020,7,30)

data_vusa_csv = get_local_data_feed_by_name('VUSA.DE.csv',start,end)
data_stoxx_csv = get_local_data_feed_by_name('EXSA.DE.csv',start,end)
data_em_csv = get_local_data_feed_by_name('IS3N.DE.csv',start,end)
data_tech_csv = get_local_data_feed_by_name('QDVE.DE.csv',start,end)
data_japan_csv = get_local_data_feed_by_name('EUNN.DE.csv',start,end)
data_bonds_csv = get_local_data_feed_by_name('AGGH.MI.csv',start,end)
data_pacific_csv = get_local_data_feed_by_name('VGEK.DE.csv',start,end)
data_china_csv = get_local_data_feed_by_name('ICGA.DE.csv',start,end)
data_nasdaq100_csv = get_local_data_feed_by_name('EXXT.DE.csv',start,end)
etf_allocation_csv = {'stoxx':(data_stoxx_csv,0.24),
                     'nasdaq100':(data_nasdaq100_csv,0.24),
                     'emerging_markets':(data_em_csv,0.24),
                     'china':(data_china_csv,0.04),
                     'pacific':(data_pacific_csv,0.035),
                     'bonds':(data_bonds_csv,0.05),
                     'japan':(data_japan_csv,0.04), 
                     'tech':(data_tech_csv,0.10)
                     }
                 
print (sum([v[1] for k,v in etf_allocation_csv.items()]))
class BuyAndHold_More_Fund_2Stocks(bt.Strategy):
    #params = dict(monthly_cash=1000.0)
    
    params = (
        ('etf_allocation',{}),
        ('monthly_cash',1000),
        )
    
    def __init__(self):
        self.etf_allocation = self.params.etf_allocation
        self.log('etf alloc {}'.format(self.etf_allocation))
        self.target_value = 0.
    
    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    
    def start(self):
        # Activate the fund mode and set the default value at 100
        self.broker.set_fundmode(fundmode=True, fundstartval=100.00)
        self.cash_start = self.broker.get_cash()
        self.val_start = 100.0
        self.add_timer(
            bt.timer.SESSION_END, monthdays=[1], monthcarry=True)
        
    def next_open(self):
        # order to buy at open price
        if self.execute_order:
        
            for data_name,(data_feed,percent_allocation) in self.etf_allocation.items():
                #print ('data_name {}'.format(data_name))
                next_open_price = data_feed.open[0]+1e-6
                size = self.target_value*percent_allocation/next_open_price
                #self.log('order executed by next_open, open {}'.format(self.datas[0].open[0]))
                order = self.buy(data=data_feed,size=size)
            
            self.execute_order = False
        
    def notify_timer(self, timer, when, *args, **kwargs):
        # Add the influx of monthly cash to the broker
        self.broker.add_cash(self.p.monthly_cash)                
        target_value = self.broker.getcash() + self.p.monthly_cash
        
        # Order will be executed at next_open
        self.execute_order = True
        self.target_value = target_value
            
    def stop(self):
        # calculate the actual returns
        self.roi = (self.broker.get_value() / self.cash_start) - 1.0
        self.froi = self.broker.get_fundvalue() - self.val_start
        print('ROI:        {:.2f}%'.format(100.0 * self.roi))
        print('Fund Value: {:.2f}%'.format(self.froi))
# Running
cerebro = bt.Cerebro(cheat_on_open=True)
cerebro.broker.set_cash(0.000001)
cerebro.broker.set_fundmode(True)

#cerebro.adddata(data_vusa_csv)
#cerebro.adddata(data_stoxx_csv)

allocation = etf_allocation_csv

for data_name,(data_feed,percent_allocation) in allocation.items():
    cerebro.adddata(data_feed, name=data_name)


#cerebro.addstrategy(BuyAndHold_More_Fund_2Stocks, period=14)
cerebro.addstrategy(BuyAndHold_More_Fund_2Stocks,etf_allocation=allocation, monthly_cash=700)

# Observers
cerebro.addobserver(bt.observers.TimeReturn, timeframe=bt.TimeFrame.Days)

# Analyzers
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio', timeframe=bt.TimeFrame.Days)
cerebro.addanalyzer(bt.analyzers.PositionsValue, headers=True, cash=True, _name='mypositions')
cerebro.addanalyzer(bt.analyzers.TimeReturn,_name='timereturn')
    
# Execute

results = cerebro.run()

# ToDo - sizer?
strat = results[0]
pyfoliozer = strat.analyzers.getbyname('pyfolio',)
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items() #<--positions only holds data for one symbol here
transactions.head(8)<jupyter_output><empty_output><jupyter_text>Analysis<jupyter_code>strat = results[0]
timereturn_analyzer = strat.analyzers.getbyname('timereturn')
timereturn = timereturn_analyzer.get_analysis()

p = strat.analyzers.getbyname('mypositions').get_analysis()
mypositions = [[k] + v for k, v in iteritems(p)] #<-- Now positions will hold data for all symbols
cols = mypositions.pop(0)  # headers are in the first entry
mypositions = pd.DataFrame.from_records(mypositions, index=cols[0], columns=cols)
mypositions.index = pd.DatetimeIndex(mypositions.index) 
mypositions['total'] = mypositions.sum(axis=1)
myreturns = mypositions['total'].pct_change()
myreturns.index = pd.to_datetime(myreturns.index).tz_localize('US/Eastern')
timereturn_df = pd.DataFrame([(k,v) for k,v in timereturn.items()],columns=['date','return'])
timereturn_df.set_index('date',inplace=True)
timereturn.index = pd.to_datetime(timereturn_df.index)
pf.create_interesting_times_tear_sheet(timereturn_df['return'])
timereturn_df[(timereturn_df.index>'2020-02-07') & (timereturn_df.index<'2020-03-01')].plot()
timereturn_df.loc['2020-04-14']
mypositions[(mypositions.index>'2020-02-05') & (mypositions.index<'2020-03-01')].head(10)
transactions[(transactions.index>'2020-04-01') & (transactions.index<'2020-05-01')].head(30)
# plotting
figs=pf.create_returns_tear_sheet(timereturn_df['return'],return_fig=True)
timereturn_df.groupby(timereturn_df.index.month).head(10)
#ou can directly df.groupby(df.index.month)
#timereturn_df.groupby(pd.Grouper(freq='M')).cumsum()
timereturn_df.resample('M').sum()
timereturn_df.plot()
mypositions[(mypositions.index>'2020-04-01') & (mypositions.index<'2020-05-01')]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1252606700959266, 'fraction_numerical': 0.02655359377172251, 'mean_word_length': 3.8938775510204082, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, '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': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '422333', 'license': 'MIT', 'n_tokens_mistral': 2682, 'n_tokens_neox': 2512, 'n_words': 382, 'path': 'notebooks/Buy_with_target_value.ipynb', 'repo_name': 'gabrielfior/play-pyfolio', 'size': '7193'}