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 __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 _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)
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
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 = []
def create_fund_operations(self): return SebFundOperations()
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)
def get_available_funds(self, date): s = SebFundOperations() avail = s.get_available_funds(date, self._funds) return avail