Пример #1
0
def calculate_xirr(fund_pd, tx_pd):
    xirr_total = []
    xirr_eq = []
    xirr_db = []
    xirr_hy = []
    for fund in fund_pd.columns:
        x = fund_pd[fund]
        f_data = tx_pd[tx_pd['AMC_Scheme_Name'] == fund]
        xirr_data = f_data[['Transaction_Date',
                            'Amount(Credits/Debits)']].values.tolist()
        xirr_data.append([x['NAV date'], x['Current Value']])
        if x['Fund Type'] == 'Equity':
            xirr_eq.extend(xirr_data)
        elif x['Fund Type'] == 'Debt':
            xirr_db.extend(xirr_data)
        elif x['Fund Type'] == 'Hybrid':
            xirr_hy.extend(xirr_data)
        xirr_total.extend(xirr_data)
        x['XIRR'] = xirr(xirr_data) * 100
    fund_pd['Total'] = None
    fund_pd['Total']['XIRR'] = xirr(xirr_total) * 100
    if len(xirr_eq) > 0:
        fund_pd['Equity'] = None
        fund_pd['Equity']['XIRR'] = xirr(xirr_eq) * 100
    if len(xirr_db) > 0:
        fund_pd['Debt'] = None
        fund_pd['Debt']['XIRR'] = xirr(xirr_db) * 100
    if len(xirr_hy) > 0:
        fund_pd['Hybrid'] = None
        fund_pd['Hybrid']['XIRR'] = xirr(xirr_hy) * 100
Пример #2
0
def main(argv=None):

    if argv:
        cashflows.append(' '.join(argv))
    else:
        read_flows()
    if not parse_flows():
        return 1

    print('\n------ Cashflow ------')
    fmt = '%s %0.2f' if any(not f == int(f) for d, f in cashflows) else '%s %d'
    for d, f in cashflows:
        print(fmt % (d, f))
    print('----------------------')

    print('xIRR: %0.2f%%' % (100 * xirr(cashflows), ))

    return 0
Пример #3
0
 def xirrCashflow(self, df_, df_trans):
     """This function will calculate the xirr of a cashflow with the following transactions:
         Dataframe:
         date (index)
         isin
         numstocks
         """
     logger = logging.getLogger(__name__)
     ret = None
     
     try:
         msg = f"Starting calculate xirr of cashflow"
         logger.debug(msg)
         self.gc.writeJobStatus("Running", statusMessage=msg)
                 
         # convert transactions to tupel-list
         l = {}
         # loop through transactions and get stock value 
         for i, row in df_trans.iterrows():
             # get stock value for isin at time x
             sv = self.getStockVal(i, row['isin'], 'close')
             v = sv * row['numstocks']
             if i in l:
                 l[i] = l[i] + v
             else:
                 l[i] = v
     
         return xirr.xirr(l)
     
         self.gc.writeJobStatus("Running", statusMessage=msg + " - DONE")
         logger.debug(msg + " - DONE")
     except Exception as e:
         logger.exception('Crash!', exc_info=e)
         self.gc.numErrors += 1
         self.gc.errMsg += "Crash xirrCashflow; "
     
     return ret
Пример #4
0
def fund_summary(i):
    tab = []
    head = [
        "Fund Name", "NAV date", "Total Units", "Total Inv", "Cur NAV",
        "Current Value", "P/L", "XIRR", "Duration", "Frequency"
    ]
    f = i.replace("Franklin_Templeton_Franklin", "Franklin_Templeton")
    f = f.replace("Aditya_Birla_Sun_Life", "ABSL")
    f = f.replace("Franklin_Templeton_India", "Franklin")
    #print(i, txn_summary.keys())
    tab.append([
        f, cur_data[i]["date"], txn_summary[i]["tot_units"],
        txn_summary[i]["tot_inv"], cur_data[i]["nav"],
        txn_summary[i]["tot_val"],
        round(txn_summary[i]["tot_val"] - txn_summary[i]["tot_inv"], 4),
        round(100 * xirr(txn_summary[i]["xirr"]),
              2), txn_summary[i]["duration"], txn_summary[i]["frequency"]
    ])
    print(
        tabulate(tab,
                 headers=head,
                 numalign="left",
                 tablefmt="grid",
                 floatfmt='.8g'))
Пример #5
0
def create_return_xirr(cashflows, method=['twrr', 'mwrr']):
    """
    Метод расчитывает доходность портфеля с учетом пополнений и изъятий денежных средств портфеля.
    Расчет производится на основе DataFrame с ежедневными данными о стоимости портфеля и денежного потока
    на дату. Для расчета используется два метода:
    - взвешенный по деньгам (money-weighted return)
    - взвешенный по времени (time-weighted return)


    :param cashflows: pandas DataFrame с столбцами ['total', 'cashflow'], имя столбца не важно, важна последовательность.
     index - дата в формает timestamp или datetime64, в которую был произведегн денежный поток и определена стоимость портфеля
     total - стоимость портфеля на соответствующую дату в формате float
     cashflow - размер денежного потока на соответствующую дату в формате float

    :param method: список методов для расчета доходности, определяет метод по которому будет считаться доходность:
     'twrr' - time-weighted rate of return. Доходность, взвешенная по времени
     'mwrr' -  money-weighted rate of return. Долходность, взвешенная по деньгам
     может принимать значения:
     ['twrr']
     ['twrr', 'mwrr']
     ['mwrr']

    :return: словарь {'twrr';{'return': float, 'data': DataFrame}, 'mwrr':{'return': float, 'data': DataFrame}}
     return - значение годовой доходности посчитанной соответствующим методом в формате float
     data - массив значений доходности в каждый момент времени
    """
    def twrr(cf):
        """
        Расчет взвешенной по времени доходности, при неравномерных на не равномерных периодах внесения и изъятия денег в формате DataFrame


        :param cashflows: pandas DataFrame с столбцами ['total', 'cashflow'], имя столбца не важно, важна последовательность.
         index - дата в формает timestamp или datetime64, в которую был произведегн денежный поток и определена стоимость портфеля
         total - стоимость портфеля на соответствующую дату в формате float
         cashflow - размер денежного потока на соответствующую дату в формате float

         каждая строка равна дневным значениям.

        :return: [twrr, annual return, revenue]
        *twrr - взвешенная по времени среднегодовая доходность за весь период. формат: float
        *annual return - годовая доходность на каждый день
        *total_return - доходность накопительным итогом за весь период
        *
        *значения взвешенной по времени доходности на каждый период в входном датафрейме
        """

        start_date = cf.index[0]
        cf = cf.copy()

        # Формируем периоды расчета изменения стоимости портфеля между пополнениями / изъятиями
        # Формируем столбец с накопительным количеством строк с не нулевым cashflow и смещаем на один, что бы в интервал
        # попадала строка с следующим casflow для расчета общей стоимоти до изменения

        cf.loc[:, 'interval'] = np.cumsum(cf[cf.columns[1]] != 0).shift(1)

        # Формируем столбец с расчетом изменения общей стоимости портфеля за каждый период по сравнению
        # с стоимостью в предыдущим периодом убирая из расчета cashflow за текущий период.
        cf['change'] = ((cf['total'] + cf['cashflow']) /
                        cf['total'].shift(periods=1)).fillna(1)

        # Для каждой строки считаем накопительное произведение изменение общей стоимости портфеля в рамках периода
        cf['prod_interval'] = cf.groupby('interval').change.cumprod().fillna(1)

        # Готовим вспомогательный столбец для хранения полного изменения по каждому периоду
        cf['prod_previous_period'] = cf['prod_interval'][cf['cashflow'] != 0]
        cf['prod_previous_period'].fillna(1, inplace=True)

        # Расчитываем накопленную доходность умножая изменение за текущий период на общие изменения за прошлые периоды
        cf['revenue'] = cf.prod_previous_period.cumprod()

        # Рассчитываем годовую доходность, умножая на приведенный к году текущий срок с даты старта портфеля
        cf['revenue'][cf['cashflow'] == 0] = cf.revenue * cf.prod_interval

        cf['annual return'] = cf.revenue**(365. /
                                           (cf.index - start_date).days) - 1

        return [cf['annual return'][-1], cf['annual return']]

    result = {}
    if 'twrr' in method:
        twrr, data = twrr(cashflows)
        result['twrr'] = twrr
        cashflows['twrr'] = data

    if 'mwrr' in method:
        #переводим DataFrame в numpy для скорости выполнения оптимизации
        cf_np = cashflows[cashflows.columns[1]].reset_index().to_numpy()

        dict_res = []

        # для каждого значения стоимости портфеля, считаем mwrr
        for i in range(1, len(cf_np)):
            arr_cf = cf_np[:i + 1].copy()
            #прибавляем положительный итоговый денежный поток на дату, равный стоимости портфеля
            arr_cf[i, 1] += cashflows[cashflows.columns[0]][i]
            dict_t = {k: v for k, v in arr_cf[np.abs(arr_cf[:, 1]) > 1e-10]}
            dict_res += [xirr.xirr(dict_t)]

        cashflows.loc[:, 'mwrr'] = [0] + dict_res

        result['mwrr'] = cashflows['mwrr'][-1]
    result['data'] = cashflows

    return result
Пример #6
0
def calcXIRR(gc,
             df_full,
             myDepot,
             days,
             valColumn='Value-total',
             investColumn='Invested-total',
             save=True):
    """calculates the XIRR of the depot for the last x days"""

    loc = locals()
    logger = logging.getLogger(__name__)

    try:
        msg = f"Starting calcXIRR with {loc}"
        logger.debug(msg)
        gc.writeJobStatus("Running", statusMessage=msg)

        # get value at start date
        startDate = datetime.datetime.now() - datetime.timedelta(days=days)
        df = df_full.loc[(df_full.index >= startDate)]
        df = df[[valColumn, investColumn]]

        l_xirr = {}

        startVal = 0
        if (len(df.index) > 0):
            startDate = df.index[0]
            startVal = df.iloc[0][valColumn]

        l_xirr[startDate] = startVal

        # get transactions
        df_diff = df.diff().dropna()
        df_diff = df_diff[df_diff[investColumn] != 0]

        for i, row in df_diff.iterrows():
            l_xirr[i] = row[investColumn]

        # get value at end
        l_xirr[df.index[-1]] = -df.iloc[-1][valColumn]

        x = -1

        if ((df.index[-1] - startDate).days > 30):

            x = xirr.xirr(l_xirr)

            logger.debug(f"XIRR of depot {myDepot}: {x}")

            # Save
            now = pd.Timestamp(datetime.datetime.now(UTC)).replace(
                hour=0, minute=0, second=0, microsecond=0)

            df_res = pd.DataFrame(index=[now],
                                  columns=["KPI-Value", "KPI", "Depot"],
                                  data=[[x, f"XIRR-{days}", myDepot]])
            #print(df_res)
            if ((x != float("inf")) and (x != float("-inf"))
                    and (save == True)):
                gc.influx_write_api.write(
                    gc.influx_db,
                    gc.influx_org,
                    record=df_res,
                    data_frame_measurement_name="KPIs",
                    data_frame_tag_columns=['Depot', 'KPI'])
                gc.influx_write_api.close()
        else:
            logger.warn(
                f"Not calculating xirr due to short timespan of {(df.index[-1] - startDate).days} days"
            )

        gc.writeJobStatus("Running", statusMessage=msg + " - DONE")
        logger.debug(msg + " - DONE")

        if (x == float("inf")):
            x = 9999

        if (x == float("-inf")):
            x = -9999

        return x

    except Exception as e:
        logger.exception(f'Crash calcXIRR with {loc}!', exc_info=e)
        gc.numErrors += 1
        gc.errMsg += f"Crash calcXIRR with {loc}; "
Пример #7
0
def print_summary(write_to_file):
    tab = []
    head = [
        "Fund Name", "NAV date", "Total Units", "Total Inv", "Cur NAV",
        "Current Value", "P/L", "XIRR", "Day P/L", "Duration", "Frequency"
    ]
    tot_inv = 0
    tot_val = 0
    tot_xirr = []
    cat_xirr = []
    cat_inv = 0
    cat_tot = 0
    fund_hist = [0, 0, 0]
    cat_hist = [0, 0, 0]
    summary = {}
    nav_date = []

    eq_total = 0
    hy_total = 0
    db_total = 0
    inv_total = 0

    for f in get_funds("Equity"):
        if f in txn_summary.keys():
            eq_total += txn_summary[f]["tot_val"]

    for f in get_funds("Hybrid"):
        if f in txn_summary.keys():
            hy_total += txn_summary[f]["tot_val"]

    for f in get_funds("Debt"):
        if f in txn_summary.keys():
            db_total += txn_summary[f]["tot_val"]

    inv_total = eq_total + hy_total + db_total

    #print("E", eq_total, "H", hy_total, "D", db_total, "T", inv_total);

    for i in txn_summary.keys():
        summary[i] = [
            shorten_fund(i), cur_data[i]["date"], txn_summary[i]["tot_units"],
            txn_summary[i]["tot_inv"], cur_data[i]["nav"],
            get_wt(i, txn_summary[i]["tot_val"], eq_total, hy_total, db_total),
            round(txn_summary[i]["tot_val"] - txn_summary[i]["tot_inv"], 4),
            str(round(100 * xirr(txn_summary[i]["xirr"]), 2)) + "%",
            pl_pct(txn_summary[i]["day_chg"], txn_summary[i]["tot_val"]),
            txn_summary[i]["duration"], txn_summary[i]["frequency"]
        ]

        tot_xirr.extend(txn_summary[i]["xirr"])
    for t in "Equity", "Debt", "Hybrid":
        cat_inv = 0
        cat_val = 0
        cat_xirr = []
        cat_hist = [0, 0, 0]
        #tab.append([t])
        for i in get_funds(t):
            if i not in txn_summary.keys():
                continue
            if t == "Equity" or t == "Hybrid":
                nav_date.append(cur_data[i]["date"])
            tab.append(summary[i])
            tot_inv += txn_summary[i]["tot_inv"]
            tot_val += txn_summary[i]["tot_val"]
            cat_inv += txn_summary[i]["tot_inv"]
            cat_val += txn_summary[i]["tot_val"]
            cat_hist[0] += txn_summary[i]["day_chg"]
            cat_hist[1] += txn_summary[i]["week_chg"]
            cat_hist[2] += txn_summary[i]["month_chg"]
            cat_xirr.extend(txn_summary[i]["xirr"])
            #print(cat_xirr)

        tab.append([
            t + " ========>", "========", "========",
            round(cat_inv, 4), "========",
            str(round(cat_val, 2)) + " (" +
            str(round((cat_val / inv_total) * 100, 1)) + "%)",
            round(cat_val - cat_inv, 4),
            str(round(100 * xirr(cat_xirr), 2)) + "%",
            "D: " + pl_pct(cat_hist[0], cat_val),
            "W: " + pl_pct(cat_hist[1], cat_val),
            "M: " + pl_pct(cat_hist[2], cat_val)
        ])

        fund_hist[0] += cat_hist[0]
        fund_hist[1] += cat_hist[1]
        fund_hist[2] += cat_hist[2]


#	pdb.set_trace()
#tab.append(["Total", cur_data[i]["date"], "", tot_inv, "", tot_val, tot_val - tot_inv, round(100 * xirr(tot_xirr), 2), round(fund_hist[0], 2), round(fund_hist[1], 2)])
    tab.append([
        "Total" + " ========>", cur_data[i]["date"], "",
        round(tot_inv, 2), "",
        round(tot_val, 2),
        round(tot_val - tot_inv, 2),
        str(round(100 * xirr(tot_xirr), 2)) + "%",
        "D: " + pl_pct(fund_hist[0], tot_val),
        "W: " + pl_pct(fund_hist[1], tot_val),
        "M: " + pl_pct(fund_hist[2], tot_val)
    ])
    print(
        tabulate(tab,
                 headers=head,
                 numalign="left",
                 tablefmt="grid",
                 floatfmt='.8g'))
    file_name = max(set(nav_date), key=nav_date.count)
    #print(file_name, nav_date)

    if write_to_file:
        report_name = os.path.abspath(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         os.pardir)) + report_dir + time.strftime(
                             file_name, time.localtime())
        with open(report_name, "w") as r:
            r.write(
                tabulate(tab,
                         headers=head,
                         numalign="left",
                         tablefmt="grid",
                         floatfmt='.8g'))
            r.write("\n")