Advertisement
dravitch

crypto_portfolio_optimizer

Dec 2nd, 2024
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.27 KB | Cryptocurrency | 0 0
  1. # Cryptocurrency Portfolio Optimizer
  2. # Version: 1.0
  3. # Filename: crypto_portfolio_optimizer_en_v1.0.py
  4. # Author: Sunu Xaliss
  5. # Date: December 2024
  6. # Description: Cryptocurrency Portfolio Optimization Tool
  7.  
  8. """
  9. Cryptocurrency Portfolio Allocation and Analysis Tool
  10.  
  11. DISCLAIMER: THIS SCRIPT IS PROVIDED FOR EDUCATIONAL PURPOSES ONLY.
  12. IT IS NOT FINANCIAL ADVICE. CRYPTOCURRENCY INVESTMENTS ARE HIGHLY
  13. VOLATILE AND CARRY SIGNIFICANT RISK. ALWAYS CONDUCT YOUR OWN RESEARCH
  14. AND CONSULT WITH A QUALIFIED FINANCIAL ADVISOR BEFORE MAKING ANY
  15. INVESTMENT DECISIONS.
  16.  
  17. Description:
  18. This script allows users to:
  19. - Retrieve historical cryptocurrency market data
  20. - Optimize a cryptocurrency portfolio using Modern Portfolio Theory
  21. - Analyze portfolio performance metrics
  22. - Visualize the efficient frontier of cryptocurrency investments
  23.  
  24. Key Features:
  25. - Fetches historical price data for multiple cryptocurrencies
  26. - Performs Monte Carlo simulation for portfolio optimization
  27. - Calculates portfolio return, volatility, and Sharpe ratio
  28. - Generates an efficient frontier visualization
  29. - Provides transparent ethical disclosure of methodology
  30.  
  31. Dependencies:
  32. - numpy
  33. - pandas
  34. - yfinance
  35. - matplotlib
  36. - seaborn
  37. - scipy
  38. """
  39.  
  40. # Required imports
  41. !pip install numpy pandas yfinance matplotlib seaborn scipy
  42. import numpy as np
  43. import pandas as pd
  44. import yfinance as yf
  45. from datetime import datetime, timedelta
  46. import logging
  47. import matplotlib.pyplot as plt
  48. import seaborn as sns
  49. from scipy.optimize import minimize
  50.  
  51. # Configuration
  52. CRYPTOS_TO_ANALYZE = [
  53.     'BTC-USD',   # Bitcoin
  54.     'ETH-USD',   # Ethereum
  55.     'XRP-USD',   # Ripple
  56.     'DASH-USD',  # Dash
  57.     'DOGE-USD',  # Dogecoin
  58.     'XLM-USD',   # Stellar
  59.     'LTC-USD',   # Litecoin
  60.     'ETC-USD',   # Ethereum Classic
  61.     'BCH-USD'    # Bitcoin Cash
  62. ]
  63.  
  64. # Logging configuration
  65. logging.basicConfig(
  66.     level=logging.INFO,
  67.     format='%(asctime)s - %(levelname)s - %(message)s'
  68. )
  69. logger = logging.getLogger(__name__)
  70.  
  71. class CryptoPortfolioOptimizer:
  72.     def __init__(
  73.         self,
  74.         cryptos=CRYPTOS_TO_ANALYZE,
  75.         start_date='2014-01-01',
  76.         end_date=datetime.now().strftime('%Y-%m-%d'),
  77.         initial_capital=10000
  78.     ):
  79.         """
  80.        Initialize the cryptocurrency portfolio optimizer
  81.        
  82.        Args:
  83.            cryptos (list): List of cryptocurrency symbols
  84.            start_date (str): Start date for historical analysis
  85.            end_date (str): End date for historical analysis
  86.            initial_capital (float): Initial investment capital
  87.        """
  88.         self.cryptos = cryptos
  89.         self.start_date = start_date
  90.         self.end_date = end_date
  91.         self.initial_capital = initial_capital
  92.        
  93.         # Initialization of attributes
  94.         self.historical_data = None
  95.         self.returns = None
  96.         self.covariance_matrix = None
  97.        
  98.     def fetch_historical_data(self):
  99.         """
  100.        Retrieve historical data for cryptocurrencies
  101.        
  102.        Returns:
  103.            pd.DataFrame: Consolidated historical data
  104.        """
  105.         all_crypto_data = []
  106.        
  107.         for crypto in self.cryptos:
  108.             try:
  109.                 ticker = yf.Ticker(crypto)
  110.                 data = ticker.history(start=self.start_date, end=self.end_date)
  111.                
  112.                 if not data.empty:
  113.                     data['Symbol'] = crypto.split('-')[0]
  114.                     data = data.reset_index().rename(columns={
  115.                         'Date': 'date',
  116.                         'Close': 'close',
  117.                         'Volume': 'volume'
  118.                     })
  119.                    
  120.                     all_crypto_data.append(data[['date', 'Symbol', 'close', 'volume']])
  121.                 else:
  122.                     logger.warning(f"No data found for {crypto}")
  123.            
  124.             except Exception as e:
  125.                 logger.error(f"Error retrieving data for {crypto}: {e}")
  126.        
  127.         self.historical_data = pd.concat(all_crypto_data, ignore_index=True)
  128.         return self.historical_data
  129.    
  130.     def preprocess_data(self):
  131.         """
  132.        Preprocess historical data
  133.        - Clean missing data
  134.        - Calculate daily returns
  135.        - Normalize data
  136.        """
  137.         if self.historical_data is None:
  138.             raise ValueError("No historical data loaded. Run fetch_historical_data() first.")
  139.        
  140.         # Pivot data to create price matrix
  141.         prices_pivot = self.historical_data.pivot(
  142.             index='date',
  143.             columns='Symbol',
  144.             values='close'
  145.         )
  146.        
  147.         # Clean missing data
  148.         prices_pivot.dropna(inplace=True)
  149.        
  150.         # Calculate daily returns
  151.         self.returns = prices_pivot.pct_change().dropna()
  152.        
  153.         # Calculate covariance matrix
  154.         self.covariance_matrix = self.returns.cov()
  155.        
  156.         return self.returns
  157.    
  158.     def calculate_portfolio_metrics(self, weights):
  159.         """
  160.        Calculate portfolio metrics
  161.        
  162.        Args:
  163.            weights (np.array): Asset weights
  164.        
  165.        Returns:
  166.            tuple: Portfolio return, volatility, Sharpe ratio
  167.        """
  168.         # Annualized portfolio return
  169.         portfolio_return = np.sum(self.returns.mean() * weights) * 252
  170.        
  171.         # Annualized portfolio volatility
  172.         portfolio_volatility = np.sqrt(
  173.             np.dot(weights.T, np.dot(self.covariance_matrix * 252, weights))
  174.         )
  175.        
  176.         # Sharpe ratio (assuming a risk-free rate of 2%)
  177.         sharpe_ratio = (portfolio_return - 0.02) / portfolio_volatility
  178.        
  179.         return portfolio_return, portfolio_volatility, sharpe_ratio
  180.    
  181.     def optimize_portfolio(self, num_portfolios=100000):
  182.         """
  183.        Portfolio optimization using Monte Carlo simulation
  184.        
  185.        Args:
  186.            num_portfolios (int): Number of portfolios to simulate
  187.        
  188.        Returns:
  189.            dict: Optimization results
  190.        """
  191.         # Ensure data is preprocessed
  192.         if self.returns is None:
  193.             self.preprocess_data()
  194.        
  195.         # Number of assets
  196.         num_assets = len(self.cryptos)
  197.        
  198.         # Initialize results storage
  199.         results = np.zeros((3, num_portfolios))
  200.         weights_record = np.zeros((num_portfolios, num_assets))
  201.        
  202.         for i in range(num_portfolios):
  203.             # Generate random weights with constraint sum = 1
  204.             weights = np.random.random(num_assets)
  205.             weights /= np.sum(weights)
  206.            
  207.             # Calculate metrics
  208.             portfolio_return, portfolio_volatility, sharpe_ratio = self.calculate_portfolio_metrics(weights)
  209.            
  210.             # Store results
  211.             results[0, i] = portfolio_volatility
  212.             results[1, i] = portfolio_return
  213.             results[2, i] = sharpe_ratio
  214.             weights_record[i, :] = weights
  215.        
  216.         # Identify optimal portfolio (max Sharpe)
  217.         max_sharpe_idx = np.argmax(results[2, :])
  218.         optimal_weights = weights_record[max_sharpe_idx, :]
  219.        
  220.         return {
  221.             'volatilities': results[0, :],
  222.             'returns': results[1, :],
  223.             'sharpe_ratios': results[2, :],
  224.             'optimal_weights': dict(zip(self.cryptos, optimal_weights)),
  225.             'optimal_metrics': {
  226.                 'volatility': results[0, max_sharpe_idx],
  227.                 'return': results[1, max_sharpe_idx],
  228.                 'sharpe_ratio': results[2, max_sharpe_idx]
  229.             }
  230.         }
  231.    
  232.     def visualize_efficient_frontier(self, optimization_results):
  233.         """
  234.        Visualize the efficient frontier
  235.        
  236.        Args:
  237.            optimization_results (dict): Optimization results
  238.        """
  239.         plt.figure(figsize=(12, 8))
  240.         plt.scatter(
  241.             optimization_results['volatilities'],
  242.             optimization_results['returns'],
  243.             c=optimization_results['sharpe_ratios'],
  244.             cmap='viridis'
  245.         )
  246.         plt.colorbar(label='Sharpe Ratio')
  247.         plt.xlabel('Volatility (Risk)')
  248.         plt.ylabel('Expected Return')
  249.         plt.title('Efficient Frontier - Crypto Portfolio')
  250.         plt.show()
  251.    
  252.     def ethical_disclosure(self):
  253.         """
  254.        Transparent disclosure of assumptions and limitations
  255.        """
  256.         disclosure = f"""
  257.        Transparency Disclosure for Cryptocurrency Portfolio Optimization
  258.        
  259.        Analysis Period: {self.start_date} - {self.end_date}
  260.        Cryptocurrencies Analyzed: {', '.join(self.cryptos)}
  261.        
  262.        Assumptions:
  263.        1. Use of historical price data
  264.        2. Assumption of normal distribution of returns
  265.        3. Reinvestment of returns
  266.        4. Risk-free rate approximated at 2%
  267.        5. No transaction fees included
  268.        
  269.        Limitations:
  270.        - Past performance does not guarantee future results
  271.        - Extreme volatility of cryptocurrency market
  272.        - Regulatory risk not accounted for
  273.        - Variable asset liquidity
  274.        """
  275.         print(disclosure)
  276.         return disclosure
  277.  
  278. def main():
  279.     # Initialize optimizer
  280.     optimizer = CryptoPortfolioOptimizer()
  281.    
  282.     try:
  283.         # Load historical data
  284.         optimizer.fetch_historical_data()
  285.        
  286.         # Preprocess data
  287.         optimizer.preprocess_data()
  288.        
  289.         # Optimize portfolio
  290.         optimization_results = optimizer.optimize_portfolio()
  291.        
  292.         # Visualize efficient frontier
  293.         optimizer.visualize_efficient_frontier(optimization_results)
  294.        
  295.         # Display results
  296.         print("\nOptimal Portfolio:")
  297.         for crypto, weight in optimization_results['optimal_weights'].items():
  298.             print(f"{crypto}: {weight*100:.2f}%")
  299.        
  300.         print("\nOptimal Portfolio Metrics:")
  301.         print(f"Return: {optimization_results['optimal_metrics']['return']*100:.2f}%")
  302.         print(f"Volatility: {optimization_results['optimal_metrics']['volatility']*100:.2f}%")
  303.         print(f"Sharpe Ratio: {optimization_results['optimal_metrics']['sharpe_ratio']:.2f}")
  304.        
  305.         # Ethical disclosure
  306.         optimizer.ethical_disclosure()
  307.    
  308.     except Exception as e:
  309.         logger.error(f"Execution error: {e}")
  310.        
  311. if __name__ == "__main__":
  312.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement