Skip to content

Project 2: Crypto Portfolio

Crypto coins

Introduction

In this project you will develop some classes for a cryptocurrency portfolio management system. A portfolio is the set of all cryptocurrencies (also known as crypto-coins), that a person owns. If a person bought one type of coin for a low price, and now that cryptocurrency has a higher price, then the person's portfolio is worth more, and they would make a profit if they sold the higher-priced coins.

The system you develop will keep a user aware of the value of their portfolio and the individual coins within it—which coins are doing well, which coins are doing poorly, and how the portfolio is doing altogether. A UML class diagram of the system is shown below:

Crypto Portfolio UML Diagram

The Market class will hold information about the top 50 coins for sale. The Coin class will hold information about a cryptocoin in the market. The Market class will be updated with current data from Coinranking's API. An API is web resource for data and other services that any program can access live.

To manage a portfolio, which is a group of coins owned by a user, there is the Portfolio class and the PortfolioCoin class. The Portfolio class contains all of the user's PortfolioCoins together. The PortfolioCoin class holds information about one of the user's particular coins—how much they own of that coin, and, importantly, how much they paid for that coin.

The Market class will be given to you completed (except for one line; see next section). You will create and submit the Coin, Portfolio, and PortfolioCoin classes.

Setup tasks

Many web APIs require an API key, as does coinranking.com. API keys are used for controlling access to an API's data and services, some of which may be paid. Coinranking allows some limited accesses without a key, and then it has a free API key which allows quite good service. More data and faster service is a paid service. You will need to register for a free API key on coinranking.com and add it to the provided market.py file.

  1. Go to developers.coinranking.com and click the Get API key button: coinranking API key registration
  2. Register for an account on the site: coinranking API key registration
  3. Sign in, go to your dashboard, and copy your API key (on the right): coinranking API key registration
  4. Paste the key into the provided market.py file, as the string value of the global constant:
    YOUR_API_KEY = "Paste your API key here"
    
  5. In order to use a Web API, you must install the requests module in Thonny, using the Tools > Manage Packages menu.

market.py

The Market class is defined in market.py (lined in step 4 above). The file won't work until after you have pasted your own API key into the code. The Market class also requires the Coin class described in the next section.

The first time you instantiate the Market class, the constructor will load the top 50 coins from the API and create a file named savecoins.json in the current folder. This file prevents further accesses to the API while you are writing and testing your code—the loading of the coins is slow due to rate-limiting of the API. You may access the list of top 50 coins using the get_coins() method of the Market class. You may download the most current data at any time using the fetch_coins_from_API() method.

Requirements

No test files or drivers are given for this assignment, and you don't have to submit any unit tests. However, only 10 submissions to Gradescope are allowed for Part A and Part B. So you will need to do extensive testing before submitting your files. We recommend you implement and test your files in the following order.

coin.py

You must define the Coin class in a file named coin.py. This class should be written first. It contains only a constructor method and some getter and setter methods. The Market class will need your Coin class in order to load its list of coins.

  • __eq__(): The equality method should return True if the arguments are both Coin objects with the same symbol and name.
  • __str__() method: Return a string that contains the symbol followed by a colon, the name followed by "is trading at", the price, and then the description on its own line, formatted as shown, with the price limited to 10 decimal places:
    SHIB: Shiba Inu is trading at 0.0000106722
    Shiba Inu is a decentralized dog-themed cryptocurrency, with a loyal following from the ShibArmy, that aims to revolutionize against centralization.
    

Documentation: The Coin class does not require docstring comments for its methods. Only the module and class docstrings are required. See docstring requirements for examples.

portfolio_coin.py

The PortfolioCoin class stores the information about a coin in a portfolio. It holds a reference to the actual Coin object, as well as the price paid (per coin) for the currency, and the number of coins of this type that are held. The required methods are simply the constructor and getter and setter methods, and:

  • get_total_price_paid(): Return the total price paid for the amount owned of this currency.
  • get_current_value(): Return the total current value of the amount owned of this currency, considering the current price listed in the Coin object.
  • get_current_gain_loss(): Return the current monetary gain or loss on this currency, given the price paid versus the current price.
  • __str__() method: Return a string that contains the amount owned followed by the coin symbol, followed by "coins were purchased at", followed by the price paid, followed by "per coin.". The price_paid_per_coin should be limited to 10 decimal places, and the amount_owned should be limited to 4 decimal places. Then the full Coin string should follow on the next line, as shown:
    18.67 SHIB coins were purchased at 0.0000098000 per coin.
    SHIB: Shiba Inu is trading at 0.0000106722
    Shiba Inu is a decentralized dog-themed cryptocurrency, with a loyal following from the ShibArmy, that aims to revolutionize against centralization.
    
  • __eq__(): The equality method should return True if the arguments are PortfolioCoin objects that refer to the same Coin and have the same amount_owned.

Documentation: The PortfolioCoin class requires docstring comments only for the first 3 methods described above. The module and class docstrings are also required. See docstring requirements for examples.

portfolio.py

The Portfolio class holds a list of currencies owned, each represented by a PortfolioCoin object. Its methods are described as follows:

  • add_coin(): This method takes a parameter which is a tuple that contains 3 values—a coin symbol (str), the price per coin paid for that coin (float), and the amount of that coin owned (float). If the symbol is not one of the top 50 coins in the market (as listed in the Market object), then the function should return False. If the price paid is None, not positive, or greater than 250,000, return False. If the amount owned is None, not positive or greater than 1,000,000, then return False. Otherwise, instantiate a PortfolioCoin object for this currency, and add it to the Portfolio object's coins list, then return True.
  • add_coins(): This method takes a parameter which is a list of tuples that are the same as described in add_coin() above. For each of the tuples that is correct (as described above), a PortfolioCoin should be instantiated and added to the Portfolio object's coins list.
  • get_original_value(): Return the total amount spent on currencies in this portfolio.
  • get_current_value(): Return the total current value of the currencies in this portfolio.
  • get_gain_loss(): Return the total gained or lost on the whole portfolio, considering the current values of the currencies being held versus the amount paid for them.
  • get_best_earner(): Return the PortfolioCoin that is currently showing the highest monetary gain. If the Portfolio is empty, return None.
  • get_worst_earner(): Return the PortfolioCoin that is currently showing the lowest monetary gain. If the Portfolio is empty, return None.
  • get_coin(): Takes a str parameter representing a currency symbol. If a currency with that symbol is in the portfolio, return the PortfolioCoin object, otherwise return None.
  • __str__(): Return a string with the following format. The first line should say, "Portfolio contains {number of coins} cryptocurrencies.". The second line should say, "The purchase cost of the portfolio was ${original value} and the current value is ${current total value}." The dollar amounts should be displayed with exactly 2 decimal places. The following lines should be the PortfolioCoin strings. You must add a newline between each PortfolioCoin string. An example of a string for a Portfolio that contains SHIB and BTC coins is shown below.
    Portfolio contains 2 cryptocurrencies.
    The purchase cost of the portfolio was $11000.00 and the current value is $28521.05.
    1000000.0000 SHIB coins were purchased at 0.0010000000 per coin.
    SHIB: Shiba Inu is trading at 0.0000113214
    Shiba Inu is a decentralized dog-themed cryptocurrency, with a loyal following from the ShibArmy, that aims to revolutionize against centralization.
    1.0000 BTC coins were purchased at 10000.0000000000 per coin.
    BTC: Bitcoin is trading at 28509.7259022293
    Bitcoin is a digital currency with a finite supply, allowing users to send/receive money without a central bank/government, often nicknamed "Digital Gold".
    
  • find_new_coin(): Takes a str parameter and searches the descriptions of all the Coins in the Market for this string. For example, the user may be searching for a new stable coin to buy. This function should return a single string according to the following:
    • For every Coin whose description contains the (case-insensitive) search term, a line containing a substring of the description should appended to the return string.
    • The substring should contain the 3 words before and the 3 words after the first word in the description which contains the search term.
    • The line should begin with the Coin's symbol, followed by a colon and space, followed by the substring, followed by a newline.
    • The description should be divided into words as it is separated by spaces. You do not have to consider punctuation.
    • If the the word that contains the search term is near the beginning or end of the description (so there are less than 3 words before or after it), just return as many words as there are.
    • If there are multiple matches for the search term in a single Coin description, only return the substring for the first match.
    • In order to be case-insensitive, convert all the text to lowercase, and leave it in lowercase in the search results.
    • The block below contains the results of searching for 'Binance':
      BNB: binance coin (bnb) is
      BUSD: this stablecoin are binance and paxos.
      TWT: that belongs to binance exchange.
      CAKE: built on the binance smart chain (bsc)
      
    • The block below contains the results of searching for 'stable':
      USDT: (usdt) is a stablecoin that uses blockchain
      USDC: a us dollar-backed stable coin developed by
      BUSD: busd is a stablecoin that is pegged
      DAI: is a decentralized, stable currency backed by
      FXS: open-source, permissionless, algorithmic stablecoin protocol implemented on
      FRAX: open-source, permissionless, algorithmic stablecoin protocol implemented on
      

Documentation: The Portfolio class does not require docstring comments for its methods. Only the module and class docstrings are required. See docstring requirements for examples.

Readiness Quiz (10 points)

You should read this document carefully, and look at the starter code and UML diagram provided. Once you have a clear understanding of the expectations for this assignment, complete the readiness quiz (10 points). The grading for this quiz will be all or nothing: your score on the quiz will be 0 if you miss any questions.

If you do not successfully complete the readiness quiz, you will have a hard time completing PA2 (Part A and Part B). We strongly recommend you complete the quiz at least one day early to give yourself plenty of time to work with understanding.

Part A - the Coin and the PortfolioCoin classes (40 points):

Complete the coin.py and portfolio_coin.py files and submit them to Gradescope by the deadline for your section.

Be sure to test your Coin and PortfolioCoin classes carefully for correctness AND style before submitting to Gradescope. The Market class uses all of the methods of the Coin class and prints Coin objects, so running Market (as the main program) can help you test.

You are limited to a maximum of 10 submissions to Gradescope for part A.

Part B - the Portfolio class (40 points)

Before uploading your submission to Gradescope, be sure to complete the following steps:

  • Test your solution carefully.
  • Run flake8 and eliminate all warnings.
  • Review and update comments as needed.
  • Make sure you have all required docstrings.

You are limited to a maximum of 10 submissions to Gradescope for part B.

Style and correctness (10 points)

Your instructor is allotted 10 points to check your submissions for style—proper comments, good variable names, readable program structure, minimal repetition, etc.

Your instructor may also find incorrect solutions that passed the Gradescope tests—for example, hard-coded results. Additional points may be deducted in that case.

Honor Code

This assignment must be completed individually. Your submission must conform to the Honor Code. Authorized help is limited to general discussion on Piazza, the lab assistants assigned to CS 149, and the instructor. Copying work from another student, person, or the Internet is an honor code violation and will be grounds for a reduced or failing grade in the course.

Acknowledgments