Example #1
0
 def __init__(self, funds, portfolio, logger = None):
     self._portfolio = portfolio
     self._funds = funds
     self._logger = logger
     self._buy_orders = []
     self._sell_orders = []
     self._fund_ops = SebFundOperations()
Example #2
0
 def __init__(self, default_allocation):
     self._default = default_allocation
     self._initial_investing_done = False
     self._fund_op = SebFundOperations()
     self.name = "Relative Momentum"
     self._lookback_days = 30
     self._nbr_funds = 4
     self._use_sma10 = True
Example #3
0
 def _forced_sell_funds(self, date):
     s = SebFundOperations()
     fund_names = list(self._portfolio.get_all_fund_names())
     for fund_name in fund_names:
         (_, end) = s.get_interval(self._funds[fund_name])
         delta = end - date
         # Check if fund is closing
         if delta.days is 0:
             # Force selling of fund at last day
             order = self.add_reallocation(date, fund_name, 0.0)
             self._process_reallocations.execute(date)
Example #4
0
    def calc_value(self, date, funds):
        value = {}
        value['cash'] = self.current_cash()
        all_shares = self.get_all_fund_shares()
        s = SebFundOperations()

        try:
            for fund_name, shares in all_shares:
                fund = funds[fund_name]
                value[fund_name] = s.calc_value(fund, date, shares)
        except KeyError as e:
            raise Portfolio.CalcValueException(
                "Not able to calculate any value {0}".format(date))

        return value
Example #5
0
class ProcessOrders:
    class Order:
        def __init__(self, fund_name, percentage):
            self.fund_name = fund_name
            self.percentage = percentage

    def __init__(self, funds, portfolio, logger = None):
        self._portfolio = portfolio
        self._funds = funds
        self._logger = logger
        self._buy_orders = []
        self._sell_orders = []
        self._fund_ops = SebFundOperations()

    def add_buy_order(self, fund_name, percentage):
        order = ProcessOrders.Order(fund_name, percentage)
        self._buy_orders.append(order)
    
    def add_sell_order(self, fund_name, percentage):
        order = ProcessOrders.Order(fund_name, percentage)
        self._sell_orders.append(order)

    def add_sell_all_order(self, fund_name):
        order = ProcessOrders.Order(fund_name, float('nan'))
        self._sell_orders.append(order)

    def _log_warning(self, date, fund_name, initial_amount, amount):
        if self._logger is None:
            return

        self._logger.warning("%s Warning: Decreased amount in %s from %.1f to %.1f because of cash shortage",
                             date.strftime('%Y-%m-%d'),
                             fund_name, 
                             initial_amount, 
                             amount)

    def _log_closed_order(self, date, text, fund_name, amount):
        if self._logger is None:
            return
        
        self._logger.info("%s %s %s %d Skr", 
                          date.strftime('%Y-%m-%d'), 
                          text, 
                          fund_name, 
                          amount)

    def _calc_value_of_portfolio(self, date, percentage):
        value = self._portfolio.calc_value(date, self._funds)
        total_value = sum(value.values())
        
        self._logger.debug("{}".format(value))

        amount = percentage * total_value

        return round(amount, 2)

    def _decrease_cash(self, value):
        # Since we are buying, first decrease cash to get money
        try:
            self._portfolio.decrease_cash(value)
        except Portfolio.OverdrawOfCash:
            # Ok, not enough left, might be rounding error
            # Withdraw what we have in cash
            cash = self._portfolio.take_all_cash()
            value = cash
        
        return value

    class ValueArgError(Exception):
        pass

    def _buy_fund(self, date, fund_name, value):
        self._logger.debug("{0} buy fund {1} {2}".format(date, fund_name, value))

        if value == 0:
            raise ProcessOrders.ValueArgError("Trying to buy for 0 Skr") 

        fund = self._funds[fund_name]
        # Calc nbr of shares
        shares = self._fund_ops.calc_shares(fund, date, value)
        # then "buy" and increase the shares of the fund
        self._portfolio.increase_fund(fund_name, shares)

        self._log_closed_order(date, "Bought", fund_name, value)

    def _execute_buy_order(self, date, order):
        value = self._calc_value_of_portfolio(date, order.percentage)
        value = self._decrease_cash(value)        
        self._buy_fund(date, order.fund_name, value)

    def _sell_fund(self, date, fund_name, fund):
        shares = self._portfolio.close_fund(fund_name)
        value = self._fund_ops.calc_value(fund, date, shares)
        return value

    def _sell_part_of_fund(self, date, fund_name, fund, percentage):
        value = self._calc_value_of_portfolio(date, percentage)

        # First calc nbr of shares
        shares = self._fund_ops.calc_shares(fund, date, value)

        # Since we are selling, first "sell" and decrease the shares of the fund
        try:
            self._portfolio.decrease_fund(fund_name, shares)
        except:
            # Ok, not enough shares left, might be rounding error
            # Take all shares left in that fund
            value = self._sell_fund(date, fund_name, fund)

        return value

    def _execute_sell_order(self, date, order):
        fund = self._funds[order.fund_name]

        if math.isnan(order.percentage):
            value = self._sell_fund(date, order.fund_name, fund)
        else:
            value = self._sell_part_of_fund(date, order.fund_name, fund, order.percentage)
            
        # then increase cash
        self._portfolio.increase_cash(value)

        self._log_closed_order(date, "Sold", order.fund_name, value)

    def execute_sell_orders(self, date):
        for order in self._sell_orders:
            self._execute_sell_order(date, order)
        self._sell_orders = []

    def execute_buy_orders(self, date):
        self._logger.debug("{0} execute_buy_orders".format(date.strftime('%Y-%m-%d')))

        for order in self._buy_orders:
            self._execute_buy_order(date, order)
        self._buy_orders = []
Example #6
0
 def create_fund_operations(self):
     return SebFundOperations()
Example #7
0
class RelativeMomentum:
    def __init__(self, default_allocation):
        self._default = default_allocation
        self._initial_investing_done = False
        self._fund_op = SebFundOperations()
        self.name = "Relative Momentum"
        self._lookback_days = 30
        self._nbr_funds = 4
        self._use_sma10 = True

    def set_nbr_funds_in_selection(self, nbr_funds):
        self._nbr_funds = nbr_funds

    def set_sma10(self, sma10):
        self._use_sma10 = sma10

    def set_logger(self, logger):
        self._logger = logger

    def _initial_investment(self, date, market):
        # Do inital investment
        market.update_portfolio(date, self._default)
        self._initial_investing_done = True

    def _calc_average(self, fund_name, fund_returns):
        sum = 0
        for fund_return in fund_returns:
            sum += fund_return[fund_name]

        avg_return = sum / len(fund_returns)
        return avg_return

    def _get_best_funds_average(self, date, avail_funds):

        self._logger.debug("avail_funds: {}".format(len(avail_funds)))

        ret_funds1 = self._fund_op.current_return_funds(date, avail_funds, 30)
        ret_funds3 = self._fund_op.current_return_funds(date, avail_funds, 90)
        ret_funds6 = self._fund_op.current_return_funds(date, avail_funds, 180)
        ret_funds12 = self._fund_op.current_return_funds(
            date, avail_funds, 360)

        self._logger.debug("ret_funds {} {} {} {}".format(
            len(ret_funds1.keys()), len(ret_funds3.keys()),
            len(ret_funds6.keys()), len(ret_funds12.keys())))

        fund_returns = [ret_funds1, ret_funds3, ret_funds6, ret_funds12]

        fund_averages = {}

        for name in avail_funds:
            try:
                avg = self._calc_average(name, fund_returns)
                fund_averages[name] = avg
            except KeyError:
                pass

        return fund_averages

    def _pos_sma10_trend(self, fund, date):
        try:
            idx = fund.index.get_loc(date, method='ffill')
            sma10 = fund.iloc[idx].sma10
            quote = fund.iloc[idx].quote
            if not numpy.isnan(sma10):
                if quote > sma10:
                    return True
        except KeyError:
            pass

        return False

    def execute(self, date, market):
        avail_funds = market.get_available_funds(date)

        if self._initial_investing_done is False:
            self._initial_investment(date, market)
            return

        self._logger.debug("**** Rel Mom ****")

        best_funds = self._get_best_funds_average(date, avail_funds)

        if self._use_sma10:
            sma10 = lambda n: self._pos_sma10_trend(avail_funds[n], date)
            tmp = {
                name: best_funds[name]
                for name in best_funds if sma10(name)
            }
            best_funds = tmp

        new_alloc = {}
        # Sort the return, best return first
        sorted_funds = sorted(best_funds.items(),
                              key=lambda x: x[1],
                              reverse=True)
        self._logger.debug("len: {}".format(len(best_funds.keys())))

        for fund_name, _ in sorted_funds[0:self._nbr_funds]:
            new_alloc[fund_name] = 0.20

        self._logger.info("{0} {1}".format(date, new_alloc))

        market.update_portfolio(date, new_alloc)
Example #8
0
 def get_available_funds(self, date):
     s = SebFundOperations()
     avail = s.get_available_funds(date, self._funds)
     return avail