Skip to content

Commit 94e1dd3

Browse files
committed
Make processor fully storable and retrievable from file
1 parent b54bada commit 94e1dd3

File tree

2 files changed

+45
-40
lines changed

2 files changed

+45
-40
lines changed

Trading/algo/ranker/ranker.py

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from Trading.model.history import History
22
from abc import ABC, abstractmethod
3-
from typing import Dict, List
3+
from typing import Dict, List, Optional
4+
from pydantic import BaseModel, ConfigDict
45

5-
class ScoreCalculator(ABC):
6+
class ScoreCalculator(BaseModel):
67
'''
78
ScoreCalculator class is used to quantify a set of traits that is desirable
89
in stocks, based on their historical data. It takes a history object and a window size
@@ -14,39 +15,14 @@ class ScoreCalculator(ABC):
1415
The ScoreCalculator class is used in the PerfectRange class to rank
1516
stocks ranging behavior based on their historical data.
1617
'''
17-
def __init__(self, window: int, is_bigger_better=False) -> None:
18-
self._window = window
19-
self._is_bigger_better = is_bigger_better
18+
window: int
19+
is_bigger_better: bool = False
20+
2021

2122
@abstractmethod
2223
def calculate(self, history: History) -> float:
2324
pass
2425

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-
def scores(self):
49-
return self._scores
5026

5127
class RangeScorer(ScoreCalculator):
5228
'''
@@ -58,9 +34,8 @@ class RangeScorer(ScoreCalculator):
5834
below which x % of the lows are. It then calculates the ratio of these two price
5935
levels and returns the ratio as the score.
6036
'''
61-
def __init__(self, window: int, x_percent: float = 10) -> None:
62-
super().__init__(window, is_bigger_better=True)
63-
self._x_percent = x_percent
37+
x_percent: float = 10.0
38+
is_bigger_better: bool = False
6439

6540
def calculate(self, history: History):
6641
# order the highs and lows of the last periods
@@ -69,8 +44,8 @@ def calculate(self, history: History):
6944

7045
# get top lowest x% highs and top x% highest lows
7146
length = len(ordered_highs)
72-
top_highs = ordered_highs[int(length/self._x_percent)]
73-
top_lows = ordered_lows[-int(length/self._x_percent)]
47+
top_highs = ordered_highs[int(length/self.x_percent)]
48+
top_lows = ordered_lows[-int(length/self.x_percent)]
7449

7550
# this represents the diminished range height
7651
ratio = top_highs / top_lows
@@ -87,3 +62,28 @@ def calculate(self, history: History):
8762
lows_std = lows_std / lows_mean
8863

8964
return ratio / (highs_std + lows_std)
65+
66+
67+
class Ordering(BaseModel):
68+
'''
69+
Ordering class is used to keep track of the ordering of stocks
70+
based on their historical data. It takes a list of stocks and
71+
orders them based on their historical data.
72+
'''
73+
top_n: int
74+
score_calculator: RangeScorer
75+
scores: Optional[Dict[str, float]] = dict()
76+
77+
model_config = ConfigDict(arbitrary_types_allowed=True)
78+
79+
def add_history(self, history: History):
80+
score = self.score_calculator.calculate(history)
81+
score = round(score, 2)
82+
self.scores[history.symbol] = score
83+
84+
is_bigger_better = self.score_calculator.is_bigger_better
85+
if is_bigger_better:
86+
self.scores = dict(sorted(self.scores.items(), key=lambda item: item[1],
87+
reverse=True)[:self.top_n])
88+
else:
89+
self.scores = dict(sorted(self.scores.items(), key=lambda item: item[1])[:self.top_n])

Trading/live/range/filter_ranging_stocks.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,18 @@ def exit():
3434
TIMEFRAME = '1M'
3535
perfect_range = PerfectRange(RANGE_WIDTH, TOP, TOLERANCE)
3636

37-
range_scorer = RangeScorer(RANGE_WIDTH)
38-
range_ordering = Ordering(TOP, range_scorer)
37+
range_scorer = RangeScorer(window=RANGE_WIDTH)
38+
range_ordering = Ordering(top_n=TOP, score_calculator=range_scorer)
3939

4040
class StockRangeProcessor(StatefulDataProcessor):
4141

4242
def __init__(self, json_file_rw, logger):
43+
global range_ordering
4344
super().__init__(json_file_rw, logger)
44-
self.data = {}
45+
# Load range ordering from file
46+
if self.data is not None and self.data.get('range_ordering') is not None:
47+
# load pydantic
48+
range_ordering = Ordering(**self.data['range_ordering'])
4549

4650
def process_item(self, item, iteration_index, client):
4751
global perfect_range, N_MONTHS
@@ -70,9 +74,10 @@ def process_item(self, item, iteration_index, client):
7074
return
7175
else:
7276
# perfect_range.add_history(history_range, ask, range_height=RANGE_HEIGHT)
73-
# self.data[item] = history_range.dict()
77+
self.data[item] = history_range.dict()
7478
range_ordering.add_history(history_range)
75-
LOGGER.info(range_ordering.scores())
79+
self.data['range_ordering'] = range_ordering.model_dump()
80+
LOGGER.info(range_ordering.scores)
7681
except Exception as e:
7782
LOGGER.error(f"Error processing {item}: {e}")
7883
self.data[item] = None

0 commit comments

Comments
 (0)