|
| 1 | +from Trading.model.history import History |
| 2 | +from abc import ABC, abstractmethod |
| 3 | +from typing import Dict, List |
| 4 | + |
| 5 | +class ScoreCalculator(ABC): |
| 6 | + ''' |
| 7 | + ScoreCalculator class is used to quantify a set of traits that is desirable |
| 8 | + in stocks, based on their historical data. It takes a history object and a window size |
| 9 | + and scores the stocks based on the historical data in the window. |
| 10 | +
|
| 11 | + This class can be used in order to determine which stocks are |
| 12 | + the best to trade based on their historical data. |
| 13 | +
|
| 14 | + The ScoreCalculator class is used in the PerfectRange class to rank |
| 15 | + stocks ranging behavior based on their historical data. |
| 16 | + ''' |
| 17 | + def __init__(self, window: int, is_bigger_better=False) -> None: |
| 18 | + self._window = window |
| 19 | + self._is_bigger_better = is_bigger_better |
| 20 | + |
| 21 | + @abstractmethod |
| 22 | + def calculate(self, history: History) -> float: |
| 23 | + pass |
| 24 | + |
| 25 | +class Ordering: |
| 26 | + ''' |
| 27 | + Ordering class is used to keep track of the ordering of stocks |
| 28 | + based on their historical data. It takes a list of stocks and |
| 29 | + orders them based on their historical data. |
| 30 | + ''' |
| 31 | + def __init__(self, top_n: int, score_calculator: ScoreCalculator) -> None: |
| 32 | + self._top_n = top_n |
| 33 | + self._scores: Dict[str, float] = dict() |
| 34 | + self._score_calculator = score_calculator |
| 35 | + |
| 36 | + def add_history(self, history: History): |
| 37 | + score = self._score_calculator.calculate(history) |
| 38 | + score = round(score, 2) |
| 39 | + self._scores[history.symbol] = score |
| 40 | + |
| 41 | + is_bigger_better = self._score_calculator._is_bigger_better |
| 42 | + if is_bigger_better: |
| 43 | + self._scores = dict(sorted(self._scores.items(), key=lambda item: item[1], |
| 44 | + reverse=True)[:self._top_n]) |
| 45 | + else: |
| 46 | + self._scores = dict(sorted(self._scores.items(), key=lambda item: item[1])[:self._top_n]) |
| 47 | + |
| 48 | +class RangeScorer(ScoreCalculator): |
| 49 | + ''' |
| 50 | + RangeScorer class is used to score stocks based on their historical data. |
| 51 | + It takes a history object and a window size and scores the stocks based on |
| 52 | + their historical data in the window. |
| 53 | +
|
| 54 | + It finds the price level above which x % of the highs are and the price level |
| 55 | + below which x % of the lows are. It then calculates the ratio of these two price |
| 56 | + levels and returns the ratio as the score. |
| 57 | + ''' |
| 58 | + def __init__(self, window: int, x_percent: float = 10) -> None: |
| 59 | + super().__init__(window, is_bigger_better=True) |
| 60 | + self._x_percent = x_percent |
| 61 | + |
| 62 | + def calculate(self, history: History): |
| 63 | + # order the highs and lows of the last periods |
| 64 | + ordered_highs = sorted(history.high) |
| 65 | + ordered_lows = sorted(history.low) |
| 66 | + |
| 67 | + # get top lowest x% highs and top x% highest lows |
| 68 | + length = len(ordered_highs) |
| 69 | + top_highs = ordered_highs[int(length/self._x_percent)] |
| 70 | + top_lows = ordered_lows[-int(length/self._x_percent)] |
| 71 | + |
| 72 | + # this represents the diminished range height |
| 73 | + ratio = top_highs / top_lows |
| 74 | + |
| 75 | + # minimize the standard deviation of highs and lows |
| 76 | + from Trading.utils.calculations import calculate_standard_deviation |
| 77 | + |
| 78 | + highs_std = calculate_standard_deviation(ordered_highs) |
| 79 | + lows_std = calculate_standard_deviation(ordered_lows) |
| 80 | + |
| 81 | + return ratio / (highs_std + lows_std) |
0 commit comments