# -*- coding: utf-8 -*- import math import numpy as np import pandas as pd from functools import reduce from operator import mul from sys import float_info as sflt TRADING_DAYS_IN_YEAR = 250 TRADING_HOURS_IN_DAY = 6.5 MINUTES_IN_HOUR = 60 def combination(**kwargs): """https://stackoverflow.com/questions/4941753/is-there-a-math-ncr-function-in-python""" n = int(math.fabs(kwargs.pop('n', 1))) r = int(math.fabs(kwargs.pop('r', 0))) if kwargs.pop('repetition', False) or kwargs.pop('multichoose', False): n = n + r - 1 # if r < 0: return None r = min(n, n - r) if r == 0: return 1 numerator = reduce(mul, range(n, n - r, -1), 1) denominator = reduce(mul, range(1, r + 1), 1) return numerator // denominator def cross(series_a:pd.Series, series_b:pd.Series, above:bool =True, asint:bool =True, offset:int =None, **kwargs): series_a = verify_series(series_a) series_b = verify_series(series_b) offset = get_offset(offset) series_a.apply(zero) series_b.apply(zero) # Calculate Result current = series_a > series_b # current is above previous = series_a.shift(1) < series_b.shift(1) # previous is below # above if both are true, below if both are false cross = current & previous if above else ~current & ~previous if asint: cross = cross.astype(int) # Offset if offset != 0: cross = cross.shift(offset) # Name & Category cross.name = f"{series_a.name}_{'XA' if above else 'XB'}_{series_b.name}" cross.category = 'utility' return cross def df_error_analysis(dfA:pd.DataFrame, dfB:pd.DataFrame, **kwargs): """ """ col = kwargs.pop('col', None) corr_method = kwargs.pop('corr_method', 'pearson') # Find their differences diff = dfA - dfB df = pd.DataFrame({'diff': diff.describe()}) extra = pd.DataFrame([diff.var(), diff.mad(), diff.sem(), dfA.corr(dfB, method=corr_method)], index=['var', 'mad', 'sem', 'corr']) # Append the differences to the DataFrame df = df['diff'].append(extra, ignore_index=False)[0] # For plotting # diff.hist() # if diff[diff > 0].any(): # diff.plot(kind='kde') if col is not None: return df[col] else: return df def fibonacci(**kwargs): """Fibonacci Sequence as a numpy array""" n = int(math.fabs(kwargs.pop('n', 2))) zero = kwargs.pop('zero', False) weighted = kwargs.pop('weighted', False) if zero: a, b = 0, 1 else: n -= 1 a, b = 1, 1 result = np.array([a]) for i in range(0, n): a, b = b, a + b result = np.append(result, a) if weighted: fib_sum = np.sum(result) if fib_sum > 0: return result / fib_sum else: return result else: return result def get_drift(x:int): """Returns an int if not zero, otherwise defaults to one.""" return int(x) if x and x != 0 else 1 def get_offset(x:int): """Returns an int, otherwise defaults to zero.""" return int(x) if x else 0 def pascals_triangle(n:int =None, **kwargs): """Pascal's Triangle Returns a numpy array of the nth row of Pascal's Triangle. n=4 => triangle: [1, 4, 6, 4, 1] => weighted: [0.0625, 0.25, 0.375, 0.25, 0.0625 => inverse weighted: [0.9375, 0.75, 0.625, 0.75, 0.9375] """ n = int(math.fabs(n)) if n is not None else 0 weighted = kwargs.pop('weighted', False) inverse = kwargs.pop('inverse', False) # Calculation triangle = np.array([combination(n=n, r=i) for i in range(0, n + 1)]) triangle_sum = np.sum(triangle) triangle_weights = triangle / triangle_sum inverse_weights = 1 - triangle_weights if weighted and inverse: return inverse_weights if weighted: return triangle_weights if inverse: return None return triangle def signed_series(series:pd.Series, initial:int =None): """Returns a Signed Series with or without an initial value""" series = verify_series(series) sign = series.diff(1) sign[sign > 0] = 1 sign[sign < 0] = -1 sign.iloc[0] = initial return sign def symmetric_triangle(n:int =None, **kwargs): n = int(math.fabs(n)) if n is not None else 2 weighted = kwargs.pop('weighted', False) if n == 2: triangle = [1, 1] if n > 2: if n % 2 == 0: front = [i + 1 for i in range(0, math.floor(n/2))] triangle = front + front[::-1] else: front = [i + 1 for i in range(0, math.floor(0.5 * (n + 1)))] triangle = front.copy() front.pop() triangle += front[::-1] if weighted: triangle_sum = np.sum(triangle) triangle_weights = triangle / triangle_sum return triangle_weights return triangle def verify_series(series:pd.Series): """If a Pandas Series return it.""" if series is not None and isinstance(series, pd.core.series.Series): return series def weights(w): def _dot(x): return np.dot(w, x) return _dot def zero(x): """If the value is close to zero, then return zero. Otherwise return the value.""" return 0 if -sflt.epsilon < x and x < sflt.epsilon else x