Esempio n. 1
1
class Portfolio(object):
    """
    Portfolio class
    """
    def __init__(self, capital = 0, buy = "BUY", sell = "SELL",
                 limit = 0, allow_short = True, TS = datetime.datetime.now(),
                 validate_mode = False):
        """
        kwargs
        ------
        buy
            buy code
        sell
            sell code
        limit
            the amount beyond which cash shouldn't fall
        allow_short
            allow for short trades
            If True, negative positions are included
        """
        self._cash = Cash(capital, TS = TS)
        self._trades = []
        self._default_columns = ['TS', 'S', 'Q', 'P', 'M', 'V']
        self._buy = buy
        self._sell = sell
        self._limit = limit
        self.metrics = Metrics(self)

    def __repr__(self):
        df = DataFrame(self._trades).set_index("TS")
        return "Balance: {B}, Transactions: {T}".format \
                (B = self.cash_balance, T = len(df))

    def __add__(self, pf):
        """
        Adds two portfolios
        """
        return self.summary + pf.summary

    def __sub__(self, pf):
        """
        Shows the difference between two portfolios
        """
        return self.summary - pf.summary

    def validate_trades(self, replace = False):
        """
        Validates all the trades
        replace
            If True, replace the existing trades with validated trades
            Use this option with caution since it reconstructs the entire
            portfolio
        """
        return self.cash_ledger()[self.cash_ledger().balance < self._limit]

    def validate_positions(self):
        """
        Validates for positions
        """
        trades = self.trades.groupby("S")["Q"].cumsum()
        return self.trades[trades < 0]   #TO DO: A better algorithm

    def add_funds(self, A, TS = None, I = "Capital", **kwargs):
        """
        Add funds to this portfolio

        Parameters
        ----------
        A : Amount of funds
        TS: Date/time in specified format
        """
        return self._cash.add(A, TS, I = I, **kwargs)

    def withdraw_funds(self, A, TS = None, **kwargs):
        """
        withdraw funds from this portfolio

        Parameters
        ----------
        A: Amount of cash to be withdrawn
        TS: Date/time in specified format
        """
        return self._cash.wd(A, TS, **kwargs)

    def expense(self, A, TS = None, **kwargs):
        """
        Add an expense
        """
        return self._cash.wd(A, TS, I = "Expense", **kwargs)

    def cash_ledger(self, from_period = None, to_period = None, freq = None):
        """
        Display the cash ledger
        """
        return self._cash.ledger(from_period, to_period, freq)

    @property
    def cash_balance(self):
        """
        Gets the current funds position
        """
        return self._cash.balance

    def add_trades(self,S,Q,P,M, I = "Trade", TS=None,**kwargs):
        """
        Add trades to the existing portfolio

        Parameters
        ----------
        S: Symbol. string
            Ticker or stock symbol
        Q: Qty. integer
            The number of shares traded
        P: Price. float
            price at the which the shares are traded
        M: Mode. string
            Whether the stocks are bought or sold
            B for bought and S for sold.
        TS: timestamp. date/time in YY-MM-DD HH:MM:SS format
            or any valid python datetime
            Time of order execution
            By default, the present time is taken

        **kwargs
        --------
        You could use any number of arguments. Each keyword is stored as a
        separate column of information. To get best reports, try to be
        consistent with keywords and value

        """
        dct = kwargs.copy()
        dct.update([('S', S), ('Q', Q if M == self._buy else -Q), ('P', P),
                    ('M', M), ('TS', TS)])
        self._trades.append(dct)
        if M == self._buy:
            self.withdraw_funds(P * Q, TS = TS, I = I)
        else:
            self.add_funds(P * Q, TS = TS, I = I)
        return self._trades


    def add_trades_from_file(self, filename, mappings = None, **options):
        """
        Add trades from a csv file

        filename: filename with path
            The header names must match with the default column names.
            If not, a corresponding mapping must be provided.
            Columns not in the existing dataframe are considered new columns
        mappings: dict
            dictionary mapping headers in the file to default dataframe columns
        **options
            Any of the options that could be passed to the pandas read_csv function
        """
        df = read_csv(filename, **options)
        if mappings is not None:
            df.columns = _map_columns(df.columns, mappings)
        self._trades = concat([self._trades, df])
        self._set_Q_V()
        return self._trades['TS', 'S', 'Q', 'P', 'M']


    def weights(self, method = "transaction", S = None, **kwargs):
        """
        Get the current weights of the all the stocks in portfolio
        By default, weights for all the stocks are returned
        Use S to restrict the stocks
        method
            method to calculate weights
            In case of valuation, a price dataframe to be given
            'transaction' - based on BUY and SELL
            'valuation' - based on present price

        S: Symbol. string or list.
            Symbol or list of symbols for which weight is required

        """
        df = self.summary[self.summary.Q != 0]
        return n(df.V)

    def weight_history(self, S, freq = "M"):
        """
        Get the weight history for a symbol
        S
            Symbol
        freq
            frequency to calculate history
        """
        pass

    @property
    def trades(self):
        """
        Gets the list of trades
        """
        return DataFrame(self._trades).set_index("TS")

    @property
    def summary(self):
        """
        Provides a summary of the portfolio by symbols
        """
        df = DataFrame(self._trades)
        df["V"] = df.P * -df.Q
        return df.groupby("S").agg({"Q": sum, "V": sum})

    def clear_trades(self):
        """
        Clear all trades
        """
        self._trades = []

    @property
    def holdings(self):
        """
        Gets the list of positions
        """
        return DataFrame(self._trades).groupby(['S']).agg({'Q': np.sum})

    def valuation(self, price):
        """
        Get the current valuation
        price
            current price of the stocks as series
        """
        df = DataFrame(self._trades).groupby("S").agg({"Q": sum})
        df["P"] = price.ix[df.index]
        df["V"] = df.Q * df.P
        return df

    def price(self, method = 'average'):
        """
        Average price of the shares
        """
        df = DataFrame(self._trades)
        df["V"] = df.Q * df.P + 0.0
        df2 = df.groupby(["S", "M"]).agg({"Q": sum, "V": sum}).unstack()
        return df2.V/df2.Q

    def infer(self, what, FROM, relation, function):
        """
        Infer required columns from existing columns
        """
        pass

    def f(self, f):
        """
        Filter trades based on conditions
        f
            Any valid pandas dataframe query
        """
        df = DataFrame(self._trades)
        return df.query(f)
Esempio n. 2
0
def test_transactions():
    cash = Cash(10000)
    cash.add(10000)
    cash.add(-2000)  # The negative sign has no effect
    assert cash.balance == 22000
    cash.wd(5000)
    cash.wd(-5000)  # Again, this has no effect
    assert cash.balance == 12000
Esempio n. 3
0
def test_transactions():
    cash = Cash(10000)
    cash.add(10000)
    cash.add(-2000) # The negative sign has no effect
    assert cash.balance == 22000
    cash.wd(5000)
    cash.wd(-5000) # Again, this has no effect
    assert cash.balance == 12000
Esempio n. 4
0
 def __init__(self, capital = 0, buy = "BUY", sell = "SELL",
              limit = 0, allow_short = True, TS = datetime.datetime.now(),
              validate_mode = False):
     """
     kwargs
     ------
     buy
         buy code
     sell
         sell code
     limit
         the amount beyond which cash shouldn't fall
     allow_short
         allow for short trades
         If True, negative positions are included
     """
     self._cash = Cash(capital, TS = TS)
     self._trades = []
     self._default_columns = ['TS', 'S', 'Q', 'P', 'M', 'V']
     self._buy = buy
     self._sell = sell
     self._limit = limit
     self.metrics = Metrics(self)
Esempio n. 5
0
class TestCash(unittest.TestCase):
    cash = Cash(10000, TS="2014-01-01")
    assert cash.balance == 10000

    def add_cash(self):
        receipts = [(5000, "2014-01-01", "Salary"),
                    (6000, "2014-02-06", "Lottery"),
                    (5000, "2014-02-10", "Salary"),
                    (5000, "2014-04-10", "Salary"),
                    (3000, "2014-05-06", "Debt")]
        [self.cash.add(A=a, TS=t, I=i) for (a, t, i) in receipts]

    def withdraw_cash(self):
        payments = [
            (1000, "2014-01-10", "Grocery"), (1000, "2014-01-17", "Party"),
            (5000, "2014-01-20", "Rent"), (1200, "2014-02-10", "Party"),
            (850, "2014-02-17", "Grocery"), (3100, "2014-03-01", "Medicine"),
            (600, "2014-03-10", "Grocery"), (600, "2014-03-18", "Party"),
            (3000, "2014-04-01", "Medicine"), (5000, "2014-04-10", "Rent"),
            (1000, "2014-04-25", "Grocery"), (800, "2014-04-10", "Grocery")
        ]
        [self.cash.wd(A=a, TS=t, I=i) for (a, t, i) in payments]
        assert self.cash.balance == 7850
Esempio n. 6
0
def test_initialize():
    cash = Cash()
    assert cash.balance == 0
    cash = Cash(10000)
    assert cash.balance == 10000