<commit_before>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import scipy.stats as ss
import statsmodels.tsa.stattools as sts

# import statsmodels.tools.tools as stt
import statsmodels.distributions.empirical_distribution as sde

# import statsmodels.stats.diagnostic as ssd

import warnings

# maxzero = lambda x: np.maximum(x, 0)
# vmax = np.vectorize(np.maximum)

# default no. of trading days in a year, 252.
trading_days = 252


def _is_pandas(d):
    return isinstance(d, pd.DataFrame) or isinstance(d, pd.Series)


def _reindex_dates(source, target):
    """
    Reindex source data with target's index

    Parameters
    ----------
    source : TYPE
        data to reindex
    target : TYPE
        target data

    Returns
    -------
    TYPE
    """
    if _is_pandas(source) and _is_pandas(target):
        result = source.reindex(target.index)
    else:
        result = source
    # assert no NaN
    nan_flag = np.isnan(result)
    nan_check = nan_flag.sum()
    assert nan_check == 0, "Unmatched dates, NaN #{}".format(nan_check)
    return result


def log_excess(rtns, bench, debug=True):
    """
    Calculate excess return given two log return series.

    Args:
        rtns (TYPE): log returns
        bench (TYPE): benchmark log returns

    Returns:
        Log excess returns
    """
    # convert to pct space then back to log
    # if isinstance(rtns, pd.Series) or isinstance(rtns, pd.DataFrame):
    #     x = np.exp(rtns).sub(np.exp(bench), axis='index')
    # else:
    #     x = np.exp(rtns) - np.exp(bench)
    # y = 1 + x

    # if debug:
    #     valid_log = y > 0
    #     invalid_log = len(y) - np.sum(valid_log)
    #     debug_test = np.allclose(invalid_log, 0)
    #     # uncomment below to print count
    #     # print('log_excess debug: less than or close to 0 total = ',
    #     #       invalid_log)

    #     assert(debug_test), 'Log(0 or -ve) count = {}'.format(invalid_log)

    # excess = np.log(y)

    # first match return dates
    matched_bench = _reindex_dates(bench, rtns)
    excess = rtns - matched_bench
    return excess


def pct_to_log_excess(returns, bench):
    """
    Convert percentage returns to log returns, then compute log excess.

    Parameters
    ----------
    returns : TYPE

    bench : TYPE


    Returns
    -------
    TYPE
    """
    rtns_log = pct_to_log_return(returns)
    bench_log = pct_to_log_return(bench)
    return log_excess(returns, bench)


def returns_gmean(returns):
    """
    Calculates geometric average returns from a given returns series.
    """
    if isinstance(returns, pd.DataFrame) or isinstance(returns, pd.Series):
        returns = returns.fillna(0)
    else:
        returns = np.nan_to_num(returns)
    return ss.gmean(1 + returns, axis=0) - 1


def log_returns(prices, n=1, fillna=False):
    """
    Log returns from prices. Preserves existing nan data when holidays are
    not aligned, i.e. return for date after an nan observation is done versus
    the last non-nan date.

    Parameters
    ----------
    prices : TYPE

    n : int, optional

    fillna : bool, optional
        If True fill first nan with 0.

    Returns
    -------
    TYPE
    """
    # keep null masks
    mask = prices.isnull()

    # ffill prices for calculating returns, one way to handle holiday calendars
    # the ffilled cells will be reset back to nan using the mask saved above.
    prices = prices.ffill()
    rtns = np.log(prices) - np.log(prices.shift(n))
    rtns.values[mask.values] = np.nan
    # print(rtns)

    if fillna:
        # rtns.fillna(0, inplace=True)
        # only fill first period nan
        if _is_pandas(rtns):
            rtns.values[0] = 0.0
        else:
            rtns[0] = 0.0
    return rtns


def pct_to_log_return(pct_returns):
    if _is_pandas(pct_returns):
        return np.log(1 + pct_returns.fillna(0))
    else:
        return np.log(1 + np.nan_to_num(pct_returns))


def log_to_pct_return(log_returns):
    return np.exp(log_returns) - 1


# def validate_mean_method(method):
#     if method not in {'gmean', 'simple'}:
#         raise AssertionError('mean_method can only be {"gmean", "simple"}')


# def validate_return_type(return_type):
#     if return_type not in {'pct', 'log'}:
#         raise AssertionError('mean_method can only be {"pct", "log"}')


def maxzero(x):
    return np.maximum(x, 0)


def LPM(returns, target_rtn, moment):
    """
    Lower partial moment.

    Parameters<commit_msg>updates<commit_after>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import scipy.stats as ss
import statsmodels.tsa.stattools as sts

# import statsmodels.tools.tools as stt
import statsmodels.distributions.empirical_distribution as sde

# import statsmodels.stats.diagnostic as ssd

import warnings

# maxzero = lambda x: np.maximum(x, 0)
# vmax = np.vectorize(np.maximum)

# default no. of trading days in a year, 252.
trading_days = 252


def _is_pandas(d):
    return isinstance(d, pd.DataFrame) or isinstance(d, pd.Series)


def _reindex_dates(source, target):
    """
    Reindex source data with target's index

    Parameters
    ----------
    source : TYPE
        data to reindex
    target : TYPE
        target data

    Returns
    -------
    TYPE
    """
    if _is_pandas(source) and _is_pandas(target):
        result = source.reindex(target.index)
    else:
        result = source
    # assert no NaN
    nan_flag = np.isnan(result)
    nan_check = nan_flag.sum()
    assert nan_check == 0, "Unmatched dates, NaN #{}".format(nan_check)
    return result


def log_excess(rtns, bench, debug=True):
    """
    Calculate excess return given two log return series.

    Args:
        rtns (TYPE): log returns
        bench (TYPE): benchmark log returns

    Returns:
        Log excess returns
    """
    # convert to pct space then back to log
    # if isinstance(rtns, pd.Series) or isinstance(rtns, pd.DataFrame):
    #     x = np.exp(rtns).sub(np.exp(bench), axis='index')
    # else:
    #     x = np.exp(rtns) - np.exp(bench)
    # y = 1 + x

    # if debug:
    #     valid_log = y > 0
    #     invalid_log = len(y) - np.sum(valid_log)
    #     debug_test = np.allclose(invalid_log, 0)
    #     # uncomment below to print count
    #     # print('log_excess debug: less than or close to 0 total = ',
    #     #       invalid_log)

    #     assert(debug_test), 'Log(0 or -ve) count = {}'.format(invalid_log)

    # excess = np.log(y)

    # first match return dates
    matched_bench = _reindex_dates(bench, rtns)
    excess = rtns - matched_bench
    return excess


def pct_to_log_excess(returns, bench):
    """
    Convert percentage returns to log returns, then compute log excess.

    Parameters
    ----------
    returns : TYPE

    bench : TYPE


    Returns
    -------
    TYPE
    """
    rtns_log = pct_to_log_return(returns)
    bench_log = pct_to_log_return(bench)
    return log_excess(returns, bench)


def returns_gmean(returns):
    """
    Calculates geometric average returns from a given returns series.
    """
    if isinstance(returns, pd.DataFrame) or isinstance(returns, pd.Series):
        returns = returns.fillna(0)
    else:
        returns = np.nan_to_num(returns)
    return ss.gmean(1 + returns, axis=0) - 1


def log_returns(prices, n=1, fillna=False):
    """
    Log returns from prices. Preserves existing nan data when holidays are
    not aligned, i.e. return for date after an nan observation is done versus
    the last non-nan date.

    Parameters
    ----------
    prices : TYPE

    n : int, optional

    fillna : bool, optional
        If True fill first nan with 0.

    Returns
    -------
    TYPE
    """
    # keep null masks
    mask = prices.isnull()

    # ffill prices for calculating returns, one way to handle holiday calendars
    # the ffilled cells will be reset back to nan using the mask saved above.
    prices = prices.ffill()
    rtns = np.log(prices) - np.log(prices.shift(n))
    rtns.values[mask.values] = np.nan
    # print(rtns)

    if fillna:
        # rtns.fillna(0, inplace=True)
        # only fill first period nan
        if _is_pandas(rtns):
            rtns.values[0] = 0.0
        else:
            rtns[0] = 0.0
    return rtns


def pct_to_log_return(pct_returns, fillna=True):
    if _is_pandas(pct_returns):
        if fillna:
            pct_returns = pct_returns.fillna(0)
        return np.log(1 + pct_returns)
    else:
        if fillna:
            pct_returns = np.nan_to_num(pct_returns)
        return np.log(1 + pct_returns)


def log_to_pct_return(log_returns):
    return np.exp(log_returns) - 1


# def validate_mean_method(method):
#     if method not in {'gmean', 'simple'}:
#         raise AssertionError('mean_method can only be {"gmean", "simple"}')


# def validate_return_type(return_type):
#     if return_type not in {'pct', 'log'}:
#         raise AssertionError('mean_method can only be {"pct", "log"}')


def maxzero(x):
    return np.maximum(x, 0)


def LPM(returns, target_rtn, moment):
    """
    Lower partial moment.

    Parameters
{'fraction_non_alphanumeric': 0.10892287380349873, 'fraction_numerical': 0.0068214325008251735, 'mean_word_length': 2.4854294478527605, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '771956', 'n_tokens_mistral': 3049, 'n_tokens_neox': 2938, 'n_words': 850}