Object Oriented Python

Object Oriented Python: Hedgefund Modeling

Due October 7th by midnight

Objectives

Practice Object Oriented Programming (OOP) with Python. Model real world entities with classes, maximizing code-reuse. Learn to make appropriate use of encapsulation, composition, polymorphism, inheritance, and mixins.

Description

A small hedgefund is currently keeping track of all its data using comma separated text files (.csv files) and some archaic Cobol programs that no unfosilized programmer understands. They want a new object oriented programming interface to their data, written in Python, that could be used as part of a web application. Your job is to design and code up the necessary classes. The hedge fund has also hired some database people to migrate their data from the csv files to a modern relational database, but that won't be ready for a few weeks so for now you'll just have to make do with the following csv files:

  • funds.csv: A fund is something in which money of a single currency can be invested. It is managed by a fund manager who works for the hedgefund business. Investments below a specified fund minimum are not accepted. Each fund guarantees that a fixed percentage of the original investment will be returned to the investor when the investment matures.
  • managers.csv: A fund manager is a person responsible for trying to maximize the return on the money invested in one or more funds. A fund manager receives a commission on the money made by each of his/her funds as a percentage of the difference bewteen the fund's value and the total amount invested in it.
  • investors.csv: An investor is someone who has invested money in one or more of the funds.
  • agents.csv: An agent is a sales-person who deals with potential investors and gets them to invest in the funds. An agent receives a commission from the hedgefund business on every investment he/she secures as a percentage of the amount invested.
  • investments.csv. An investment is a specified amount of money given by an investor to be managed as part of a fund for a specified period of time.
Download these files and look over them. Each file contains a number of records representing the entities the file is named for. Pay close attention to the first line of each file which names the columns. Each record has a record id or rid which uniquely identifies it and may be used in other files as a reference to it. For example, an investment record identifies an investor and a fund and the agent who convinced the investor to invest in that fund. (N.B. These are fake records greatly simplified for the purpose of this assignment - be thankful there are no addresses :)



Below is an example trace of EXACTLY how your classes should work when used in an interactive Python session. It might seem long, but the point is that a lot of functionality is the same for each class!



>>> from sys import stdout as o
>>> import datetime
>>> from hedgefunds import *
>>>
>>> i = Investor('Flintstone','Fred',datetime.date(1965,12,3),'male','Mr.')
>>>
>>> i
Investor('Flintstone', 'Fred', datetime.date(1965, 12, 3), 'male', 'Mr.', 1)
>>> repr(i)
"Investor('Flintstone', 'Fred', datetime.date(1965, 12, 3), 'male', 'Mr.', 1)"
>>> i.__repr__()  # THIS CONTROLS PREVIOUS TWO LINES
"Investor('Flintstone', 'Fred', datetime.date(1965, 12, 3), 'male', 'Mr.', 1)"
>>> eval(repr(i))  # OBJECT CAN BE RECREATED FROM ITS REPR STRING
Investor('Flintstone', 'Fred', datetime.date(1965, 12, 3), 'male', 'Mr.', 1)
>>>
>>> i  # NOTICE ONE ADDITIONAL BIT OF DATA WE DIDN'T SPECIFY?
Investor('Flintstone', 'Fred', datetime.date(1965, 12, 3), 'male', 'Mr.', 1)
>>> # THE '1' AT THE END IS THE RECORD ID rid. THE REPR STRING INCLUDES ALL DATA SO YOU SEE IT.
>>> # THE INVESTOR CLASS SHOULD ASSIGN A rid IF ONE IS NOT PASSED INTO THE CONSTRUTOR.
>>>
>>> print i
1, Mr. Fred Flintstone, 12/03/1965, male
>>> i.__str__()  # THIS CONTROLS PREVIOUS LINE
'1, Mr. Fred Flintstone, 12/03/1965, male'
>>>
>>> # NOTE THE NAME IS NICELY PUT TOGETHER AND AND rid IS AT THE START
>>> # NOTE datetime.date(YYYY, MM, DD) BUT WE PRINT IN  MM/DD/YYYY FORMAT
>>>
>>> # NOW LET'S LOOK AT OUR INVESTOR OBJECT'S ATTRIBUTES
>>> i.last_name
'Flintstone'
>>> i.first_name
'Fred'
>>> i.dob
datetime.date(1965, 12, 3)
>>> i.date_of_birth
'12/03/1965'
>>> i.gender
'male'
>>> i.title
'Mr.'
>>> i.name
'Mr. Fred Flintstone'
>>> i.rid
1
>>>
>>> # BUT WE'RE GONNA NEED SOMEHWERE TO STORE MULTIPLE INVESTOR OBJECTS
>>> # SO AS WELL AS CREATING INVESTOR OBJECTS, THE INVESTOR CLASS MUST ACT LIKE A RECORDSET
>>>
>>> Investor.report(o)  # o WAS SET TO STDOUT AT THE TOP OF THIS TRACE BUT COULD BE ANY FILE
>>> Investor.add(i)  # NO RECORDS SO LET'S ADD ONE
>>> Investor.report(o)  # NOW WE HAVE SOMETHING TO REPORT
1, Mr. Fred Flintstone, 12/03/1965, male
>>>
>>> Investor.add(Investor('Flintstone','Wilma',datetime.date(1968,1,15),'female','Mrs.'))
>>> Investor.report(o)
1, Mr. Fred Flintstone, 12/03/1965, male
2, Mrs. Wilma Flintstone, 01/15/1968, female
>>>
>>> Investor.add("I'm not an investor object")  # THIS HAD BETTER NOT WORK
Traceback (most recent call last):
  File "", line 1, in 
  File "hedgefunds.py", line 42, in add
    raise TypeError('Expected object of type %s, received %s' % (cls.__name__, type(record).__name__))
TypeError: Expected object of type Investor, received str
>>>
>>> # WE COULD CONTINUE ADDING INVESTORS MANUALLY BUT SINCE WE'VE GOT A FILE FULL OF THEM....
>>> Investor.load('investors.csv')
>>> Investor.report(o)
1, Mr. Charles Creed, 12/05/1928, male
2, Miss Sheila Geller, 11/12/1962, female
3, Mr. Fred Kenobi, 07/13/1957, male
4, Miss Rachel Geller, 07/11/1968, female
5, Mr. Charles Rubble, 09/23/1940, male
6, Mrs. Leah Skywalker, 07/02/1929, female
7, Mr. Bill Balboa, 03/06/1988, male
8, Dr. Sheila Barkley, 08/26/1950, female
.
.
>>> # YOU SHOULD SEE 120 RECORDS (OUR TWO MANUALLY ADDED RECORDS ARE GONE)
>>>
>>>
>>>
>>> # THE SAME USAGE PATTERN IS NOW REPEATED FOR AGENTS, MANAGERS, FUNDS AND INVESTMENTS
>>> # (FOLLOWED BY SOME EXAMPLES OF FUNCTIONALITY COMMON TO ALL RECORDSETS)
>>>
>>> # AGENTS
>>>
>>> a = Agent(2.1,'Rubble','Barney',datetime.date(1966,4,20),'male','Mr.')
>>> a
Agent(2.1000000000000001, 'Rubble', 'Barney', datetime.date(1966, 4, 20), 'male', 'Mr.', 1)
>>> repr(a)
"Agent(2.1000000000000001, 'Rubble', 'Barney', datetime.date(1966, 4, 20), 'male', 'Mr.', 1)"
>>> eval(repr(a))
Agent(2.1000000000000001, 'Rubble', 'Barney', datetime.date(1966, 4, 20), 'male', 'Mr.', 1)
>>> print a
1, Mr. Barney Rubble, 04/20/1966, male, 2.1
>>> a.last_name
'Rubble'
>>> a.first_name
'Barney'
>>> a.dob
datetime.date(1966, 4, 20)
>>> a.date_of_birth
'04/20/1966'
>>> a.gender
'male'
>>> a.title
'Mr.'
>>> a.name
'Mr. Barney Rubble'
>>> a.rid
1
>>> a.commission
2.1000000000000001
>>> Agent.add(a)
>>> Agent.report(o)
1, Mr. Barney Rubble, 04/20/1966, male, 2.1
>>> Agent.load('agents.csv')
>>> Agent.report(o)
1, Mr. Barney Flintstone, 02/13/1933, male, 4.0
2, Miss Rachel Rubble, 11/21/1982, female, 2.5
3, Dr. Ted Geller, 03/14/1963, male, 8.0
4, Miss Phoebe Creed, 11/06/1959, female, 5.5
5, Mr. Luke Kenobi, 08/24/1945, male, 2.5
6, Dr. Megan Creed, 03/26/1957, female, 5.5
7, Mr. Ted Rubble, 09/14/1931, male, 3.5
8, Mrs. Monica Balboa, 05/07/1934, female, 1.5
.
.
>>> YOU SHOULD SEE 40 RECORDS (OUR MANUALLY ADDED RECORD IS GONE)
>>>
>>>
>>>
>>> # MANAGERS
>>>
>>> m = Manager(1.2,'Creed','Apollo',datetime.date(1960,11,3),'male','Mr.')
>>> m
Manager(1.2, 'Creed', 'Apollo', datetime.date(1960, 11, 3), 'male', 'Mr.', 1)
>>> repr(m)
"Manager(1.2, 'Creed', 'Apollo', datetime.date(1960, 11, 3), 'male', 'Mr.', 1)"
>>> eval(repr(m))
Manager(1.2, 'Creed', 'Apollo', datetime.date(1960, 11, 3), 'male', 'Mr.', 1)
>>> print m
1, Mr. Apollo Creed, 11/03/1960, male, 1.2
>>> m.last_name
'Creed'
>>> m.first_name
'Apollo'
>>> m.dob
datetime.date(1960, 11, 3)
>>> m.date_of_birth
'11/03/1960'
>>> m.gender
'male'
>>> m.title
'Mr.'
>>> m.name
'Mr. Apollo Creed'
>>> m.rid
1
>>> m.commission
1.2
>>> Manager.add(m)
>>> Manager.report(o)
1, Mr. Apollo Creed, 11/03/1960, male, 1.2
>>> Manager.load('managers.csv')
>>> Manager.report(o)
1, Dr. Ben Flintstone, 03/11/1925, male, 1.5
2, Miss Jessica Geller, 05/20/1960, female, 0.5
3, Mr. Ted Balboa, 05/13/1988, male, 2.0
4, Miss Betty Solo, 01/26/1981, female, 1.5
5, Mr. Ben Barkley, 10/18/1946, male, 2.5
6, Dr Megan Balboa, 05/16/1971, female, 2.0
7, Mr. Luke Skywalker, 07/09/1971, male, 2.0
8, Mrs. Phoebe Barkley, 05/20/1963, female, 0.5
.
.
>>> YOU SHOULD SEE 40 RECORDS (OUR MANUALLY ADDED RECORD IS GONE)
>>>
>>>
>>>
>>> # FUNDS
>>>
>>> f = Fund('HUFI3',5,'USD',10000,50,123456.78)
>>> f
Fund('HUFI3', 5, 'USD', 10000, 50, 123456.78, 1)
>>> repr(f)
"Fund('HUFI3', 5, 'USD', 10000, 50, 123456.78, 1)"
>>> eval(repr(f))
Fund('HUFI3', 5, 'USD', 10000, 50, 123456.78, 1)
>>> print f
1, HUFI3, 5, USD, 10000, 50, 123456.78
>>> f.name
'HUFI3'
>>> f.manager  # THIS IS THE rid OF A MANAGER RECORD
5
>>> f.manager_name  # THIS MUST BE GOT BY FINDING THE MANAGER RECORD WITH rid=5
'Mr. Ben Barkley'
>>> f.currency
'USD'
>>> f.minimum
10000
>>> f.guarantee
50
>>> f.value
123456.78
>>> f.rid
1
>>> Fund.add(f)
>>> Fund.report(o)
1, HUFI3, 5, USD, 10000, 50, 123456.78
>>> Fund.load('funds.csv')
>>> Fund.report(o)
1, 20A2D, 13, AUD, 20000, 20, 282872.276008
2, GYE18, 30, GBP, 10000, 80, 452975.814718
3, 6IMOA, 15, AUD, 5000, 100, 209646.345482
4, XM89Z, 40, SGD, 20000, 70, 112462.549576
5, 4N7H8, 18, EUR, 5000, 90, 193206.182659
6, 419A6, 28, GBP, 20000, 40, 288830.435006
7, WNBBW, 16, AUD, 10000, 30, 342606.155466
8, 14DH9, 8, GBP, 2000, 30, 207586.916035
.
.
>>> YOU SHOULD SEE 200 RECORDS (OUR MANUALLY ADDED RECORD IS GONE)
>>>
>>>
>>>
>>> # INVESTMENTS
>>>
>>> i = Investment(2, 4, 6, 23000, datetime.date(2009,1,13), datetime.date(2012,1,13))
>>> i
Investment(2, 4, 6, 23000, datetime.date(2009, 1, 13), datetime.date(2012, 1, 13), 1)
>>> repr(i)
'Investment(2, 4, 6, 23000, datetime.date(2009, 1, 13), datetime.date(2012, 1, 13), 1)'
>>> eval(repr(i))
Investment(2, 4, 6, 23000, datetime.date(2009, 1, 13), datetime.date(2012, 1, 13), 1)
>>> print i
1, 2, 4, 6, 23000, 01/13/2009, 01/13/2012
>>> i.fund
2
>>> i.fund_name  # MUST BE GOT BY FINDING THE FUND RECORD WITH rid=2
'GYE18'
>>> i.investor
4
>>> i.investor_name  # MUST BE GOT BY FINDING THE INVESTOR RECORD WITH rid=4
'Miss Rachel Geller'
>>> i.agent
6
>>> i.agent_name  # MUST BE GOT BY FINDING THE AGENT RECORD WITH rid=6
'Dr. Megan Creed'
>>> i.amount
23000
>>> i.start
datetime.date(2009, 1, 13)
>>> i.start_date
'01/13/2009'
>>> i.end
datetime.date(2012, 1, 13)
>>> i.end_date
'01/13/2012'
>>> i.rid
1
>>> Investment.add(i)
>>> Investment.report(o)
1, 2, 4, 6, 23000, 01/13/2009, 01/13/2012
>>> Investment.load('investments.csv')
>>> Investment.report(o)
1, 46, 115, 19, 76000, 10/12/2007, 10/12/2038
2, 61, 92, 11, 60000, 07/01/2007, 07/01/2030
3, 183, 105, 30, 23000, 02/02/1985, 02/02/2020
4, 198, 2, 36, 47000, 10/02/1993, 10/02/1999
5, 25, 79, 15, 19000, 06/25/1980, 06/25/2005
6, 176, 39, 34, 32000, 04/26/1958, 04/26/1981
7, 75, 2, 19, 62000, 11/16/1996, 11/16/2029
8, 168, 75, 2, 65000, 03/09/1974, 03/09/2002
.
.
>>> YOU SHOULD SEE 2000 RECORDS (OUR MANUALLY ADDED RECORD IS GONE)
>>>
>>>
>>>
>>>
>>> # NOW FOR SOME FUNCTIONALITY COMMON TO ALL RECORDSETS
>>> # WE'LL USE MANAGER TO DEMONSTRATE
>>>
>>> [m.name for m in Manager.iterate()]  # ITERATE IS A GENERATOR FUNCTION
['Dr. Ben Flintstone', 'Miss Jessica Geller', 'Mr. Ted Balboa', 'Miss Betty Solo', 
'Mr. Ben Barkley', 'Dr Megan Balboa', 'Mr. Luke Skywalker', 'Mrs. Phoebe Barkley', 
'Mr. Ben Rubble', 'Mrs. Monica Barkley', 'Mr. Fred Balboa', 'Mrs. Wilma Rubble', 
'Mr. Fred Creed', 'Dame Jennifer Rubble', 'Mr. Ted Kenobi', 'Dr Wilma Skywalker', 
'Mr. Ted Solo', 'Dame Jessica Kenobi', 'Sir Luke Flintstone', 'Miss Rachel Solo', 
'Dr. Han Green', 'Dame Betty Skywalker', 'Mr. Bill Skywalker', 'Mrs. Jennifer Green', 
'Sir Rocky Solo', 'Mrs. Rachel Balboa', 'Mr. Apollo Flintstone', 'Dame Megan Solo', 
'Mr. Bill Geller', 'Mrs. Wilma Barkley', 'Mr. Fred Rubble', 'Miss Jennifer Kenobi', 
'Dr. Luke Green', 'Dr Sheila Solo', 'Mr. Charles Barkley', 'Mrs. Jessica Rubble', 
'Mr. Bill Flintstone', 'Dr Sheila Rubble', 'Sir Bill Green', 'Mrs. Sheila Kenobi']
>>>
>>> Manager.sort(key=lambda m:(m.last_name,m.first_name))  # MIMICS LIST.SORT()
>>> [m.name for m in Manager.iterate()]
['Mr. Fred Balboa', 'Dr Megan Balboa', 'Mrs. Rachel Balboa', 'Mr. Ted Balboa', 
'Mr. Ben Barkley', 'Mr. Charles Barkley', 'Mrs. Monica Barkley', 'Mrs. Phoebe Barkley', 
'Mrs. Wilma Barkley', 'Mr. Fred Creed', 'Mr. Apollo Flintstone', 'Dr. Ben Flintstone', 
'Mr. Bill Flintstone', 'Sir Luke Flintstone', 'Mr. Bill Geller', 'Miss Jessica Geller', 
'Sir Bill Green', 'Dr. Han Green', 'Mrs. Jennifer Green', 'Dr. Luke Green', 
'Miss Jennifer Kenobi', 'Dame Jessica Kenobi', 'Mrs. Sheila Kenobi', 'Mr. Ted Kenobi', 
'Mr. Ben Rubble', 'Mr. Fred Rubble', 'Dame Jennifer Rubble', 'Mrs. Jessica Rubble', 
'Dr Sheila Rubble', 'Mrs. Wilma Rubble', 'Dame Betty Skywalker', 'Mr. Bill Skywalker', 
'Mr. Luke Skywalker', 'Dr Wilma Skywalker', 'Miss Betty Solo', 'Dame Megan Solo', 
'Miss Rachel Solo', 'Sir Rocky Solo', 'Dr Sheila Solo', 'Mr. Ted Solo']
>>>
>>> Manager.reverse()  # MIMICS LIST.REVERSE()
>>> [m.name for m in Manager.iterate()]
['Mr. Ted Solo', 'Dr Sheila Solo', 'Sir Rocky Solo', 'Miss Rachel Solo', 
'Dame Megan Solo', 'Miss Betty Solo', 'Dr Wilma Skywalker', 'Mr. Luke Skywalker', 
'Mr. Bill Skywalker', 'Dame Betty Skywalker', 'Mrs. Wilma Rubble', 'Dr Sheila Rubble', 
'Mrs. Jessica Rubble', 'Dame Jennifer Rubble', 'Mr. Fred Rubble', 'Mr. Ben Rubble', 
'Mr. Ted Kenobi', 'Mrs. Sheila Kenobi', 'Dame Jessica Kenobi', 'Miss Jennifer Kenobi', 
'Dr. Luke Green', 'Mrs. Jennifer Green', 'Dr. Han Green', 'Sir Bill Green', 
'Miss Jessica Geller', 'Mr. Bill Geller', 'Sir Luke Flintstone', 'Mr. Bill Flintstone', 
'Dr. Ben Flintstone', 'Mr. Apollo Flintstone', 'Mr. Fred Creed', 'Mrs. Wilma Barkley', 
'Mrs. Phoebe Barkley', 'Mrs. Monica Barkley', 'Mr. Charles Barkley', 'Mr. Ben Barkley', 
'Mr. Ted Balboa', 'Mrs. Rachel Balboa', 'Dr Megan Balboa', 'Mr. Fred Balboa']
>>>
>>> Manager.find_first(rid=20)  # SHOULD RETURN ONE OBJECT OR NONE
Manager(2.0, 'Solo', 'Rachel', datetime.date(1928, 8, 3), 'female', 'Miss', 20)
>>>
>>> Manager.find_first(last_name='Solo')
Manager(0.5, 'Solo', 'Ted', datetime.date(1965, 1, 22), 'male', 'Mr.', 17)
>>>
>>> Manager.find(last_name='Solo')  # SHOULD RETURN A LIST OF OBJECTS
[Manager(0.5, 'Solo', 'Ted', datetime.date(1965, 1, 22), 'male', 'Mr.', 17),
Manager(1.0, 'Solo', 'Sheila', datetime.date(1952, 1, 18), 'female', 'Dr', 34),
Manager(3.0, 'Solo', 'Rocky', datetime.date(1984, 7, 15), 'male', 'Sir', 25),
Manager(2.0, 'Solo', 'Rachel', datetime.date(1928, 8, 3), 'female', 'Miss', 20),
Manager(3.0, 'Solo', 'Megan', datetime.date(1921, 3, 28), 'female', 'Dame', 28),
Manager(1.5, 'Solo', 'Betty', datetime.date(1981, 1, 26), 'female', 'Miss', 4)]
>>>
>>> Manager.find(last_name='Solo',gender='male')  # TWO CONDITIONS, BOTH MUST HOLD
[Manager(0.5, 'Solo', 'Ted', datetime.date(1965, 1, 22), 'male', 'Mr.', 17), 
Manager(3.0, 'Solo', 'Rocky', datetime.date(1984, 7, 15), 'male', 'Sir', 25)]
>>>
>>> # ANY NUMBER OF KEYWORD ARGUMENT CONDITIONS CAN BE SPECIFIED IN THIS WAY
>>> # AS LONG AS EACH KEYWORD NAMES A MANAGER OBJECT ATTRIBUTE
>>>
>>> Manager.find(last_name='Solo',gender='male',title='Sir')
[Manager(3.0, 'Solo', 'Rocky', datetime.date(1984, 7, 15), 'male', 'Sir', 25)]
>>>
>>> # IF NO KEYWORD ARGUMENTS ARE PASSED, FIND RETURNS ALL RECORDS
>>>
>>> # WITH FIND WORKING IT'S EASY TO ADD THE SAME FILTERING TO ITERATE AND REPORT
>>>
>>> [m.name for m in Manager.iterate(last_name='Solo',gender='male')]
['Mr. Ted Solo', 'Sir Rocky Solo']
>>>
>>> Manager.report(o, last_name='Solo',gender='male')
17, Mr. Ted Solo, 01/22/1965, male, 0.5
25, Sir Rocky Solo, 07/15/1984, male, 3.0
>>>
>>> # ALSO ADD A DELETE METHOD USING THE SAME FILTERING
>>> Manager.find(last_name='Solo')  # COMPARE WITH SAME CALL AFTER DELETE
[Manager(0.5, 'Solo', 'Ted', datetime.date(1965, 1, 22), 'male', 'Mr.', 17), 
Manager(1.0, 'Solo', 'Sheila', datetime.date(1952, 1, 18), 'female', 'Dr', 34), 
Manager(3.0, 'Solo', 'Rocky', datetime.date(1984, 7, 15), 'male', 'Sir', 25), 
Manager(2.0, 'Solo', 'Rachel', datetime.date(1928, 8, 3), 'female', 'Miss', 20), 
Manager(3.0, 'Solo', 'Megan', datetime.date(1921, 3, 28), 'female', 'Dame', 28), 
Manager(1.5, 'Solo', 'Betty', datetime.date(1981, 1, 26), 'female', 'Miss', 4)]
>>>
>>> Manager.delete(last_name='Solo',gender='male')
>>> Manager.find(last_name='Solo',gender='male')  # HAD BETTER RETURN []
[]
>>> Manager.find(last_name='Solo')  # NOTICE THE MALE SOLOS ARE GONE
[Manager(1.0, 'Solo', 'Sheila', datetime.date(1952, 1, 18), 'female', 'Dr', 34), 
Manager(2.0, 'Solo', 'Rachel', datetime.date(1928, 8, 3), 'female', 'Miss', 20), 
Manager(3.0, 'Solo', 'Megan', datetime.date(1921, 3, 28), 'female', 'Dame', 28), 
Manager(1.5, 'Solo', 'Betty', datetime.date(1981, 1, 26), 'female', 'Miss', 4)]
>>>
>>> # MAKE A DELETE_FIRST METHOD TO DELETE ONLY THE FIRST RECORD FOUND AND RETURN IT
>>> [m.name for m in Manager.iterate(last_name='Solo',gender='female')]
['Dr Sheila Solo', 'Miss Rachel Solo', 'Dame Megan Solo', 'Miss Betty Solo']
>>> Manager.delete_first(last_name='Solo',gender='female')
Manager(1.0, 'Solo', 'Sheila', datetime.date(1952, 1, 18), 'female', 'Dr', 34)
>>> [m.name for m in Manager.iterate(last_name='Solo',gender='female')]
['Miss Rachel Solo', 'Dame Megan Solo', 'Miss Betty Solo']
>>>
>>>
>>> # FINALLY, WE WANT TO SAVE CHANGES
>>> Manager.save('myfilename.csv')  # SHOULD WRITE IN THE FORMAT OF 'managers.csv'
>>>


Other Requirements

  • Comparable
    • Two instances of the same class must be comparable using all the usual comparison operators:
      <, <=, >, >=, ==, !=
      • Fund instances must be compared by value
      • Investor, Manager and Agent instances must be compared by the attribute values passed to their __init__() methods, and in the same order.
      • Investment instances must be compared first by amount, then by their duration (end-start), then by fund.
    • HINT: remember the Comparable mixin shown in class.
  • Object-Relational Functionality (do these last as they rely heavily on find)
    • Fund instance f:
      • f.manager_name: returns the name of the Manager responsible for Fund f, or "unknown" if the Manager record cannot be found.
      • f.investments: returns a list of Investment objects representing all investments made in Fund f
      • f.total_invested: returns the total amount of money currently invested in fund f
      • f.profit: returns the difference between f.value and f.total_invested (which can be negative)
    • Investor instance i:
      • i.investments: returns a list of Investment objects representing all investments made by Investor i
      • i.funds: returns a list of Fund objects representing all funds in which Investor i has invested.
    • Manager instance m:
      • m.funds: returns a list of Fund objects representing the funds managed by Manager m
      • m.get_funds('USD'): Like m.funds but filtered by the currency 'USD' (a different currency could be passed)
      • m.get_total_profit('USD'): returns the total amount of profit made by Manager m over all his/her funds for the currency passed ('USD' in this example).
      • m.get_total_commission('USD'): returns m.commission percent of the total profit made by Manager m in the currency passed ('USD' in this example)
    • Agent instance a:
      • a.deals: returns a list of Investment objects representing all investments in which Agent a was the agent.
      • a.get_deals('GBP'): like a.deals but filtered by the currency 'GBP' (a different currency could be passed)
      • a.get_total_deals('GBP'): returns the sum of all investment amounts in the currency passed for which Agent a was the agent
      • a.get_total_commission('GBP'): returns a.commission percent of the total investments for which Agent a was the agent in the currency passed ('GBP' in this example)
    • Investment instance i (these are shown in the trace):
      • i.fund_name: returns the name of the fund or "unknown" if the fund record cannot be found
      • i.investor_name: returns the name of the investor or "unknown" if the investor record cannot be found
      • i.agent_name: returns the name of the agent or "unknown" if the agent record cannot be found
  • EXTRA CREDIT
    • Enhancements to the find method.
      • As it stands, the find method accepts any number of keyword arguments which get collected into a dictionary of keyword-value pairs. If one such value is a container, its contents should treated as a disjunction of conditions. For example: Agent.find(rid=[1,2,3,4]) should return a list containing those Agent objects whose rid is in [1,2,3,4].
      • If one such value is callable, find should use it as a test function returning True for record objects whose named attribute passes the test. For example Agent.find(rid=lambda x:x<5 or x>10) should return a list of agent objects whose rid is less than 5 or greater than 10.
      • Hint: If you've written them correctly, report, iterate, delete and delete_first will get these enhancements too.
    • Make a typed list
      • Instead of using pythons list, wtite a class that inherits from it having exactly the same functionality EXCEPT that it can only contain objects of a specified type. (This is not easy at all).
  • Put your program in a file called hedgefunds.py. Testing your classes thoroughly before you submit them is extremely important and the quality of your test code will affect your score. Advice: Keep an eye on the class discussion board for helpful hints on how to do this assignment!

Grading Criteria

I have 97 unit tests which will be used to evaluate your functionality. Apart from testing code, ALL your code should be part of a class. Your classes will be graded on correctness and how well you manage to reuse code. (Hint: reusing code will require you to come up with a few additional classes beyond those named in the trace above). Pointlessly repetitive code will lose credit. You should also attempt to make your code easy to read and include appropriate comments that are helpful to the reader. Algorithmic efficiency is not a priority so linear time is always acceptable for this project.

Submission Checklist

  • hedgefunds.py