diff --git a/CHANGELOG.md b/CHANGELOG.md index ad85b29..142d6ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html - above, below, cross, crossover, etc.. - Added sanitise_name to convert '.' to ',' to support name nesting - Added more Exceptions to improve error's +- Added Candle ability to accept json str timestamp, therefore allowing direct conversion from Pandas -> Hexital - Fixed Hammer index pattern working correctly - Changed Sub/managed indicators to auto populate candles field - Changed 'as_list' property a method that can now take a nested indicator name diff --git a/README.md b/README.md index c11b2c2..d749362 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ pip install git+https://github.com/merlinr/hexital.git@development ### Single Indicator ```python from hexital import EMA, Candle +import pandas as pd my_candles = [ {"open": 17213, "high": 2395, "low": 7813, "close": 3615, "volume": 19661}, @@ -110,6 +111,10 @@ my_candles = [ ] # Convert Basic candles candles = Candle.from_dicts(my_candles) +# Or directly from a Numpy Dataframe +# df = pd.read_csv("path/to/symbol.csv", sep=",") +# candles = Candle.from_dicts(df.to_dict("records")) + my_ema = EMA(candles=candles, period=3) my_ema.calculate() diff --git a/hexital/core/candle.py b/hexital/core/candle.py index e4d92f0..71d4e4f 100644 --- a/hexital/core/candle.py +++ b/hexital/core/candle.py @@ -36,7 +36,7 @@ def __init__( low: float, close: float, volume: int, - timestamp: Optional[datetime] = None, + timestamp: Optional[datetime | str] = None, indicators: Optional[Dict[str, float | Dict[str, float | None] | None]] = None, sub_indicators: Optional[Dict[str, float | Dict[str, float | None] | None]] = None, ): @@ -45,7 +45,11 @@ def __init__( self.low = low self.close = close self.volume = volume - self.timestamp = timestamp + + if isinstance(timestamp, datetime): + self.timestamp = timestamp + elif isinstance(timestamp, str): + self.timestamp = datetime.fromisoformat(timestamp) self.clean_values = {} self.indicators = indicators if indicators else {} diff --git a/tests/core/test_candle.py b/tests/core/test_candle.py index ce5c409..4456ec5 100644 --- a/tests/core/test_candle.py +++ b/tests/core/test_candle.py @@ -72,6 +72,53 @@ def fixture_candle_dict_datetime(): } +@pytest.fixture(name="candle_dicts_numpy") +def fixture_candle_dicts_numpy(): + # dicts = df.to_dict("records") + return [ + { + "open": 14851.6, + "high": 14854.1, + "low": 14847.2, + "close": 14848.1, + "volume": 247, + "timestamp": "2023-10-03T09:01:00", + }, + { + "open": 14848.2, + "high": 14848.2, + "low": 14843.6, + "close": 14844.7, + "volume": 332, + "timestamp": "2023-10-03T09:02:00", + }, + { + "open": 14844.6, + "high": 14846.6, + "low": 14842.4, + "close": 14842.6, + "volume": 196, + "timestamp": "2023-10-03T09:03:00", + }, + { + "open": 14842.5, + "high": 14842.9, + "low": 14831.7, + "close": 14835.6, + "volume": 540, + "timestamp": "2023-10-03T09:04:00", + }, + { + "open": 14835.5, + "high": 14842.1, + "low": 14835.4, + "close": 14839.7, + "volume": 171, + "timestamp": "2023-10-03T09:05:00", + }, + ] + + @pytest.fixture(name="candle_list") def fixture_candle_list(): return [ @@ -132,6 +179,51 @@ def test_candle_from_dicts(candle_dict): ] +def test_candle_from_dicts_numpy(candle_dicts_numpy): + assert Candle.from_dicts(candle_dicts_numpy) == [ + Candle( + open=14851.6, + high=14854.1, + low=14847.2, + close=14848.1, + volume=247, + timestamp=datetime(2023, 10, 3, 9, 1), + ), + Candle( + open=14848.2, + high=14848.2, + low=14843.6, + close=14844.7, + volume=332, + timestamp=datetime(2023, 10, 3, 9, 2), + ), + Candle( + open=14844.6, + high=14846.6, + low=14842.4, + close=14842.6, + volume=196, + timestamp=datetime(2023, 10, 3, 9, 3), + ), + Candle( + open=14842.5, + high=14842.9, + low=14831.7, + close=14835.6, + volume=540, + timestamp=datetime(2023, 10, 3, 9, 4), + ), + Candle( + open=14835.5, + high=14842.1, + low=14835.4, + close=14839.7, + volume=171, + timestamp=datetime(2023, 10, 3, 9, 5), + ), + ] + + def test_candle_from_list(candle_list): assert Candle.from_list(candle_list[0]) == Candle( open=12331.69043,