Advertisement
dravitch

DCA vs Buy & Hold Analyzer

Dec 15th, 2024
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.78 KB | Cryptocurrency | 0 0
  1. """
  2. Comparative and Interactive Investment Performance Analysis Tool:
  3. Comparison Between Buy-and-Hold vs Dollar Cost Averaging (DCA)
  4.  
  5. WARNING: THIS SCRIPT IS PROVIDED FOR EDUCATIONAL PURPOSES ONLY.
  6. THIS IS NOT FINANCIAL ADVICE. CRYPTOCURRENCY INVESTMENTS ARE HIGHLY
  7. VOLATILE AND INVOLVE SIGNIFICANT RISKS. ALWAYS CONDUCT YOUR OWN
  8. RESEARCH AND CONSULT A QUALIFIED FINANCIAL ADVISOR BEFORE MAKING
  9. ANY INVESTMENT DECISIONS.
  10.  
  11. Description:
  12. This script enables users to:
  13. - analyze and compare cryptocurrency investment strategies, specifically focusing on Dollar Cost Averaging (DCA) and Buy and Hold approaches.
  14. - to simulate and evaluate investment performance for various cryptocurrencies over a specified time period.
  15.  
  16. Key Features:
  17. - Interactive User Input: Allows users to:
  18. - Select from a list of popular cryptocurrencies
  19. - Choose monthly investment amount
  20. - Set investment frequency (monthly, quarterly, semi-annual, annual)
  21. - Define investment duration (1-10 years)
  22.  
  23. Performance Analysis Capabilities:
  24.  
  25. - Calculates detailed investment performance metrics
  26. - Compares Dollar Cost Averaging (DCA) against Buy and Hold strategy
  27. - Generates comprehensive performance reports
  28. - Calculates key financial indicators like Sharpe Ratio
  29. - Visualizes portfolio value over time
  30.  
  31. Dependencies:
  32. - yfinance
  33. - pandas
  34. - numpy
  35. - matplotlib
  36. """
  37.  
  38. import yfinance as yf
  39. import pandas as pd
  40. import numpy as np
  41. import matplotlib.pyplot as plt
  42. from datetime import datetime, timedelta
  43.  
  44. class InvestmentPerformanceAnalyzer:
  45.     def __init__(self, ticker, start_date, end_date, monthly_investment, investment_frequency='monthly'):
  46.         """
  47.        Initialize performance analyzer for a cryptocurrency investment strategy.
  48.        
  49.        :param ticker: Cryptocurrency symbol (e.g., 'BTC-USD')
  50.        :param start_date: Start date of investment period
  51.        :param end_date: End date of investment period
  52.        :param monthly_investment: Fixed amount to invest each period
  53.        :param investment_frequency: Frequency of investment (default: 'monthly')
  54.        """
  55.         # Configure pandas display options for better alignment
  56.         pd.set_option('display.max_columns', None)
  57.         pd.set_option('display.width', 1000)
  58.         pd.set_option('display.float_format', '{:.6f}'.format)
  59.        
  60.         self.ticker = ticker
  61.         self.start_date = pd.to_datetime(start_date)
  62.         self.end_date = pd.to_datetime(end_date)
  63.         self.monthly_investment = monthly_investment
  64.         self.investment_frequency = investment_frequency
  65.        
  66.         # Retrieve historical price data
  67.         self._fetch_price_data()
  68.        
  69.     def _fetch_price_data(self):
  70.         """
  71.        Retrieve historical price data for the specified cryptocurrency.
  72.        """
  73.         # Extend end date slightly to ensure capturing last month's data
  74.         self.price_data = yf.download(self.ticker, start=self.start_date, end=self.end_date + pd.Timedelta(days=30), interval="1mo")
  75.        
  76.         # Handle multi-index columns and select closing prices
  77.         if isinstance(self.price_data.columns, pd.MultiIndex):
  78.             self.price_data = self.price_data[('Close', self.ticker)]
  79.        
  80.         # Reset index and rename columns
  81.         self.price_data = self.price_data.reset_index()
  82.         self.price_data.columns = ['Investment_Date', 'Asset_Price']
  83.        
  84.     def calculate_dca_performance(self):
  85.         """
  86.        Simulate Dollar Cost Averaging (DCA) investment strategy and calculate performance metrics.
  87.        """
  88.         # Initialize tracking variables
  89.         cumulative_investment = 0
  90.         cumulative_assets_purchased = 0
  91.         performance_data = []
  92.        
  93.         # Simulate investment for each period
  94.         for index, row in self.price_data.iterrows():
  95.             current_price = row['Asset_Price']
  96.            
  97.             # Ensure valid price
  98.             if pd.notna(current_price) and current_price > 0:
  99.                 # Calculate assets purchased
  100.                 assets_purchased = self.monthly_investment / current_price
  101.                 cumulative_assets_purchased += assets_purchased
  102.                 cumulative_investment += self.monthly_investment
  103.                
  104.                 # Store performance snapshot
  105.                 performance_data.append({
  106.                     'Date': row['Investment_Date'],
  107.                     'Asset_Price': current_price,
  108.                     'Cumulative_Assets': cumulative_assets_purchased,
  109.                     'Total_Invested': cumulative_investment,
  110.                     'Current_Value': cumulative_assets_purchased * current_price
  111.                 })
  112.        
  113.         # Convert to DataFrame with appropriate formatting
  114.         self.performance_df = pd.DataFrame(performance_data)
  115.         return self.performance_df
  116.  
  117.     def generate_performance_report(self):
  118.         """
  119.        Generate a comprehensive performance report.
  120.        """
  121.         # Ensure performance calculation is done
  122.         if not hasattr(self, 'performance_df'):
  123.             self.calculate_dca_performance()
  124.        
  125.         # Final metrics
  126.         last_row = self.performance_df.iloc[-1]
  127.         initial_price = round(self.performance_df['Asset_Price'].iloc[0], 2)
  128.         final_price = round(self.performance_df['Asset_Price'].iloc[-1], 2)
  129.  
  130.         # Calculate returns
  131.         total_invested = last_row['Total_Invested']
  132.         final_value = round(last_row['Current_Value'], 2)
  133.         total_return_percentage = ((final_value - total_invested) / total_invested) * 100
  134.        
  135.         # Calculate Sharpe Ratio (simplified version)
  136.         monthly_returns = self.performance_df['Asset_Price'].pct_change()
  137.         sharpe_ratio = monthly_returns.mean() / monthly_returns.std() * np.sqrt(12)  # Annualized
  138.        
  139.         # Prepare report
  140.         report = {
  141.             'Performance Indicators': {
  142.                 'Annualized Sharpe Ratio': f'{sharpe_ratio:.2f}',
  143.                 'Asset': self.ticker,
  144.                 'Investment Period': f'{self.start_date.strftime("%Y-%m-%d")} to {self.end_date.strftime("%Y-%m-%d")}',
  145.                 'Investment Frequency': self.investment_frequency,
  146.                 'Monthly Investment': f'{self.monthly_investment} €'
  147.             },
  148.             'DCA Investment Summary': {
  149.                 'Initial Asset Price': f'{initial_price:.2f} €',
  150.                 'Final Asset Price': f'{final_price:.2f} €',
  151.                 'Total Invested': f'{total_invested:.2f} €',
  152.                 'Final Portfolio Value': f'{final_value:.2f} €',
  153.                 'Total Return (%)': f'{total_return_percentage:.2f}%'
  154.             }
  155.         }
  156.        
  157.         return report
  158.    
  159.     def compare_with_buy_and_hold(self):
  160.         """
  161.        Compare DCA strategy with Buy and Hold strategy.
  162.        """
  163.         # Buy and hold investment would be total amount invested at the beginning
  164.         buy_hold_data = yf.download(self.ticker, start=self.start_date, end=self.end_date, interval="1mo")
  165.        
  166.         if isinstance(buy_hold_data.columns, pd.MultiIndex):
  167.             initial_price = buy_hold_data[('Close', self.ticker)].iloc[0]
  168.             final_price = buy_hold_data[('Close', self.ticker)].iloc[-1]
  169.         else:
  170.             initial_price = buy_hold_data['Close'].iloc[0]
  171.             final_price = buy_hold_data['Close'].iloc[-1]
  172.        
  173.         total_invested = self.monthly_investment * len(self.performance_df)
  174.         buy_hold_assets = total_invested / initial_price
  175.         final_buy_hold_value = buy_hold_assets * final_price
  176.         buy_hold_return = ((final_buy_hold_value - total_invested) / total_invested) * 100
  177.        
  178.         return {
  179.             'Buy and Hold Approach': {
  180.                 'Initial Asset Price': f'{initial_price:.2f} €',
  181.                 'Final Asset Price': f'{final_price:.2f} €',
  182.                 'Total Invested': f'{total_invested:.2f} €',
  183.                 'Final Portfolio Value': f'{final_buy_hold_value:.2f} €',
  184.                 'Total Return (%)': f'{buy_hold_return:.2f}%'
  185.             }
  186.         }
  187.  
  188.     def generate_performance_graph(self):
  189.         """
  190.        Generate a line graph of portfolio value over time.
  191.        """
  192.         plt.figure(figsize=(14, 7))
  193.        
  194.         # Plot DCA performance
  195.         plt.plot(self.performance_df['Date'], self.performance_df['Current_Value'],
  196.                  label='DCA Portfolio Value', color='blue')
  197.        
  198.         # Calculate and plot Buy and Hold performance
  199.         total_invested = self.monthly_investment * len(self.performance_df)
  200.         buy_hold_data = yf.download(self.ticker, start=self.start_date, end=self.end_date, interval="1mo")
  201.        
  202.         if isinstance(buy_hold_data.columns, pd.MultiIndex):
  203.             initial_price = buy_hold_data[('Close', self.ticker)].iloc[0]
  204.             purchased_assets = total_invested / initial_price
  205.             buy_hold_values = purchased_assets * buy_hold_data[('Close', self.ticker)]
  206.         else:
  207.             initial_price = buy_hold_data['Close'].iloc[0]
  208.             purchased_assets = total_invested / initial_price
  209.             buy_hold_values = purchased_assets * buy_hold_data['Close']
  210.        
  211.         plt.plot(buy_hold_data.index, buy_hold_values,
  212.                  label='Buy and Hold Portfolio Value', color='red', linestyle='--')
  213.        
  214.         plt.xlabel('Date')
  215.         plt.ylabel('Value (€)')
  216.         plt.title(f'Comparative Performance for {self.ticker}')
  217.         plt.grid(True)
  218.         plt.legend()
  219.         plt.tight_layout()
  220.         plt.show()
  221.  
  222. def get_crypto_list():
  223.     """
  224.    Returns a predefined list of popular cryptocurrencies.
  225.    """
  226.     return [
  227.         'BTC-USD',   # Bitcoin
  228.         'ETH-USD',   # Ethereum
  229.         'BNB-USD',   # Binance Coin
  230.         'ADA-USD',   # Cardano
  231.         'DOT-USD',   # Polkadot
  232.         'LINK-USD',  # Chainlink
  233.         'XRP-USD',   # Ripple
  234.         'SOL-USD',   # Solana
  235.         'DOGE-USD'   # Dogecoin
  236.     ]
  237.  
  238. def get_investment_frequencies():
  239.     """
  240.    Returns available investment frequencies.
  241.    """
  242.     return [
  243.         'monthly',
  244.         'quarterly',
  245.         'semi-annual',
  246.         'annual'
  247.     ]
  248.  
  249. def user_input():
  250.     """
  251.    Interactive function to obtain investment parameters from the user.
  252.    """
  253.     print("\n--- Crypto Investment Performance Analysis Tool ---")
  254.    
  255.     # Cryptocurrency selection
  256.     cryptos = get_crypto_list()
  257.     print("\nSelect a cryptocurrency:")
  258.     for i, crypto in enumerate(cryptos, 1):
  259.         print(f"{i}. {crypto}")
  260.    
  261.     while True:
  262.         try:
  263.             crypto_choice = int(input("\nEnter the cryptocurrency number: "))
  264.             if 1 <= crypto_choice <= len(cryptos):
  265.                 ticker = cryptos[crypto_choice - 1]
  266.                 break
  267.             else:
  268.                 print("Invalid choice. Try again.")
  269.         except ValueError:
  270.             print("Please enter a valid number.")
  271.    
  272.     # Investment amount
  273.     while True:
  274.         try:
  275.             monthly_investment = float(input("\nEnter monthly investment amount in euros: "))
  276.             if monthly_investment > 0:
  277.                 break
  278.             else:
  279.                 print("Amount must be positive.")
  280.         except ValueError:
  281.             print("Please enter a valid amount.")
  282.    
  283.     # Investment frequency
  284.     frequencies = get_investment_frequencies()
  285.     print("\nSelect investment frequency:")
  286.     for i, freq in enumerate(frequencies, 1):
  287.         print(f"{i}. {freq}")
  288.    
  289.     while True:
  290.         try:
  291.             frequency_choice = int(input("\nEnter frequency number: "))
  292.             if 1 <= frequency_choice <= len(frequencies):
  293.                 frequency = frequencies[frequency_choice - 1]
  294.                 break
  295.             else:
  296.                 print("Invalid choice. Try again.")
  297.         except ValueError:
  298.             print("Please enter a valid number.")
  299.    
  300.     # Investment period
  301.     while True:
  302.         try:
  303.             years = int(input("\nEnter number of investment years (1-10): "))
  304.             if 1 <= years <= 10:
  305.                 break
  306.             else:
  307.                 print("Please choose between 1 and 10 years.")
  308.         except ValueError:
  309.             print("Please enter a valid number.")
  310.    
  311.     # Calculate dates
  312.     end_date = datetime.now().date()
  313.     start_date = end_date - timedelta(days=365 * years)
  314.    
  315.     return {
  316.         'ticker': ticker,
  317.         'start_date': start_date.strftime('%Y-%m-%d'),
  318.         'end_date': end_date.strftime('%Y-%m-%d'),
  319.         'monthly_investment': monthly_investment,
  320.         'investment_frequency': frequency
  321.     }
  322.  
  323. def main():
  324.     """
  325.    Main function to execute investment performance analysis.
  326.    """
  327.     # Retrieve investment parameters
  328.     parameters = user_input()
  329.    
  330.     # Create InvestmentPerformanceAnalyzer object
  331.     analyzer = InvestmentPerformanceAnalyzer(
  332.         parameters['ticker'],
  333.         parameters['start_date'],
  334.         parameters['end_date'],
  335.         parameters['monthly_investment'],
  336.         parameters['investment_frequency']
  337.     )
  338.    
  339.     # Calculate DCA performance
  340.     dca_performance_df = analyzer.calculate_dca_performance()
  341.     performance_report = analyzer.generate_performance_report()
  342.     print("\n--- DCA Performance Report ---")
  343.     for section, values in performance_report.items():
  344.         print(f"\n{section}:")
  345.         for key, value in values.items():
  346.             print(f"{key}: {value}")
  347.    
  348.     # Compare with Buy and Hold strategy
  349.     buy_hold_report = analyzer.compare_with_buy_and_hold()
  350.     print("\n--- Buy and Hold Report ---")
  351.     for section, values in buy_hold_report.items():
  352.         print(f"\n{section}:")
  353.         for key, value in values.items():
  354.             print(f"{key}: {value}")
  355.    
  356.     # Generate and display performance graph
  357.     analyzer.generate_performance_graph()
  358.  
  359. if __name__ == "__main__":
  360.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement