def set_readonly_props(self) -> None: if not self._transactions: return self._start_capital = self._capital.get( ModelUtils.get_first_key(self._capital)) self._end_capital = self._capital.get( ModelUtils.get_last_key(self._capital)) self._hold_length_days_stats = StatUtils.get_descriptive_stats( [t.hold_length_days for t in self._transactions]) self._change_in_capital_stats = StatUtils.get_descriptive_stats( [t.change_in_capital for t in self._transactions]) self._has_profit_stats = StatUtils.get_descriptive_stats( [NumberUtils.to_int(t.has_profit) for t in self._transactions]) self._pct_return = NumberUtils.get_change(self._end_capital, self._start_capital) self._best_transactions = [ t for t in sorted(self._transactions, key=lambda x: x.change_in_capital, reverse=True) if t.has_profit ][:20] self._worst_transactions = [ t for t in sorted(self._transactions, key=lambda x: x.change_in_capital) if not t.has_profit ][:20] symbol_grouped: Dict = {} for t in self._transactions: if not t.symbol_master.symbol in symbol_grouped: symbol_grouped[t.symbol_master.symbol]: Dict = { 'symbol_master': t.symbol_master, 'change_in_capital': 0, 'no_of_transactions': 0 } symbol_grouped[t.symbol_master. symbol]['change_in_capital'] += t.change_in_capital symbol_grouped[t.symbol_master.symbol]['no_of_transactions'] += 1 symbol_grouped_list: List[BackTestResultItemPerSymbol] = [] for k, v in symbol_grouped.items(): item: BackTestResultItemPerSymbol = BackTestResultItemPerSymbol() item.symbol_master = symbol_grouped[k]['symbol_master'] item.change_in_capital = NumberUtils.round( symbol_grouped[k]['change_in_capital']) item.no_of_transactions = symbol_grouped[k]['no_of_transactions'] symbol_grouped_list.append(item) self._best_symbols = [ i for i in sorted(symbol_grouped_list, key=lambda x: x.change_in_capital, reverse=True) if i.change_in_capital > 0 ][:20] self._worst_symbols = [ i for i in sorted(symbol_grouped_list, key=lambda x: x.change_in_capital) if i.change_in_capital < 0 ][:20]
def append_exponential_smoothing_max_min( self, prices: DataFrame, index: Any, exponential_smoothing_max_min_diff: float) -> DataFrame: if not (isinstance(prices, DataFrame) and AppConsts.PRICE_COL_SYMBOL_ID in prices.index.names and AppConsts.PRICE_COL_DATE in prices.index.names and AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE in prices.columns and AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE_PREV in prices.columns and AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE_NEXT in prices.columns): return None if not AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MAX in prices.columns: prices[AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MAX] = 0 if not AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MIN in prices.columns: prices[AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MIN] = 0 curr_exp_smoothed_maxmin: float = 0.1 for i, row in prices.loc[index].iterrows(): symbol_id: int = i[0] curr_date: date = i[1] curr_exp_smoothed_p: float = row[ AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE] prev_exp_smoothed_p: float = row[ AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE_PREV] next_exp_smoothed_p: float = row[ AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_PRICE_NEXT] maxmin_diff: float = NumberUtils.get_change( curr_exp_smoothed_p, curr_exp_smoothed_maxmin) is_max: bool = ( not np.isnan(prev_exp_smoothed_p) and not prev_exp_smoothed_p == 0 and not np.isnan(next_exp_smoothed_p) and not next_exp_smoothed_p == 0 and maxmin_diff is not None and curr_exp_smoothed_p > prev_exp_smoothed_p and curr_exp_smoothed_p >= next_exp_smoothed_p and abs(maxmin_diff) > exponential_smoothing_max_min_diff) is_min: bool = ( not np.isnan(prev_exp_smoothed_p) and not prev_exp_smoothed_p == 0 and not np.isnan(next_exp_smoothed_p) and not next_exp_smoothed_p == 0 and maxmin_diff is not None and curr_exp_smoothed_p < prev_exp_smoothed_p and curr_exp_smoothed_p <= next_exp_smoothed_p and abs(maxmin_diff) > exponential_smoothing_max_min_diff) prices[AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MAX].loc[ symbol_id, curr_date] = 1 if is_max else 0 prices[AppConsts.CUSTOM_COL_EXPONENTIAL_SMOOTHING_MIN].loc[ symbol_id, curr_date] = 1 if is_min else 0 if is_max or is_min: curr_exp_smoothed_maxmin = curr_exp_smoothed_p return prices
def append_double_tops(self, prices: DataFrame, index: Any, price_column: str, smooth_price_column: str, max_column: str, min_column: str, double_tops_diff: float) -> DataFrame: if not (isinstance(prices, DataFrame) and AppConsts.PRICE_COL_SYMBOL_ID in prices.index.names and AppConsts.PRICE_COL_DATE in prices.index.names and price_column in prices.columns and smooth_price_column in prices.columns and max_column in prices.columns and min_column in prices.columns): return None if not AppConsts.CUSTOM_COL_DOUBLE_TOPS in prices.columns: prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS] = 0 if not AppConsts.CUSTOM_COL_DOUBLE_TOPS_TARGET_PRICE in prices.columns: prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS_TARGET_PRICE] = 0 if not AppConsts.CUSTOM_COL_DOUBLE_TOPS_STOP_LOSS in prices.columns: prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS_STOP_LOSS] = 0 last_four_maxmins: List[float] = [] for i, row in prices.loc[index].iterrows(): symbol_id: int = i[0] curr_date: date = i[1] is_max: bool = row[max_column] == 1 is_min: bool = row[min_column] == 1 if is_max or is_min: if len(last_four_maxmins) >= 4: last_four_maxmins.pop(0) last_four_maxmins.append(row[smooth_price_column]) if len(last_four_maxmins) < 4: continue (point_a, point_b, point_c, point_d) = last_four_maxmins point_b_d_diff: float = NumberUtils.get_change(point_b, point_d) target_price: float = point_c - (point_d - point_c) has_double_tops: bool = (point_a < point_b and point_a < point_c and point_a < point_d and point_b > point_c and point_c < point_d and abs(point_b_d_diff) < double_tops_diff and row[price_column] < point_c) # and row[price_column] > target_price) # Actual Double Tops condition, but get better results without it. if has_double_tops: prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS].loc[symbol_id, curr_date] = 1 prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS_TARGET_PRICE].loc[ symbol_id, curr_date] = target_price prices[AppConsts.CUSTOM_COL_DOUBLE_TOPS_STOP_LOSS].loc[ symbol_id, curr_date] = point_d return prices
def append_inverted_head_and_shoulders(self, prices: DataFrame, index: Any, price_column: str, smooth_price_column: str, max_column: str, min_column: str, shoulder_diff: float, neck_diff: float) -> DataFrame: if not (isinstance(prices, DataFrame) and AppConsts.PRICE_COL_SYMBOL_ID in prices.index.names and AppConsts.PRICE_COL_DATE in prices.index.names and price_column in prices.columns and smooth_price_column in prices.columns and max_column in prices.columns and min_column in prices.columns): return None if not AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS in prices.columns: prices[AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS] = 0 if not AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_TARGET_PRICE in prices.columns: prices[AppConsts. CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_TARGET_PRICE] = 0 if not AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_STOP_LOSS in prices.columns: prices[ AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_STOP_LOSS] = 0 last_six_maxmins: List[float] = [] for i, row in prices.loc[index].iterrows(): symbol_id: int = i[0] curr_date: date = i[1] is_max: bool = row[max_column] == 1 is_min: bool = row[min_column] == 1 if is_max or is_min: if len(last_six_maxmins) >= 6: last_six_maxmins.pop(0) last_six_maxmins.append(row[smooth_price_column]) if len(last_six_maxmins) < 6: continue (point_a, point_b, point_c, point_d, point_e, point_f) = last_six_maxmins point_b_f_diff: float = NumberUtils.get_change(point_b, point_f) point_c_e_diff: float = NumberUtils.get_change(point_c, point_e) neck_point: float = point_c if point_c > point_e else point_e target_price: float = neck_point + (neck_point - point_d) has_inverted_head_and_shoulders: bool = ( point_a > point_b and point_a > point_c and point_a > point_d and point_a > point_e and point_a > point_f and point_b < point_c and point_b > point_d and point_b < point_e and point_c > point_d and point_c > point_f and point_d < point_e and point_d < point_f and abs(point_b_f_diff) < shoulder_diff and abs(point_c_e_diff) < neck_point and row[price_column] > neck_point and row[price_column] < target_price) if has_inverted_head_and_shoulders: prices[AppConsts.CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS].loc[ symbol_id, curr_date] = 1 prices[ AppConsts. CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_TARGET_PRICE].loc[ symbol_id, curr_date] = target_price prices[AppConsts. CUSTOM_COL_INVERTED_HEAD_AND_SHOULDERS_STOP_LOSS].loc[ symbol_id, curr_date] = point_f return prices