Skip to content

Commit

Permalink
added extra features
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaron committed Mar 5, 2024
1 parent 8cd3550 commit 5df6f64
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Roboquant is an open-source algorithmic trading platform. It is flexible, user-f
So whether you are a beginning retail trader or an established trading firm, roboquant can help you to develop robust and fully automated trading strategies. You can find out more at [roboquant.org](https://roboquant.org).

## Usage
The following code snippet shows the steps to run a full back-test on a number of stocks.
The following code snippet shows all the steps to run a full back-test on a number of stocks.

```python
import roboquant as rq
Expand All @@ -32,6 +32,9 @@ Make sure you have Python version 3.10 or higher installed.
python3 -m pip install --upgrade roboquant
```

You can also try roboquant in an online Jupyter Notebook [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/neurallayer/roboquant-notebooks/HEAD?labpath=%2Fintro_roboquant.ipynb)


The core of roboquant limits the number of dependencies.
But you can install roboquant including one or more of the optional dependencies if you require certain functionality:

Expand Down Expand Up @@ -76,8 +79,11 @@ python -m unittest discover -s tests/unit
flake8 roboquant tests
```

## Kotlin version
Next to this Python version of `roboquant`, there is also a Kotlin version available.
Both (will) share a similar API, just the used computer language is different.
## License
Roboquant is made available under the Apache 2.0 license. You can read more about the Apache 2.0 license on this page: https://www.apache.org/licenses/LICENSE-2.0.html

Which one to use depends very much on personal preferences, skills and usage.
## Disclaimer
Absolutely no warranty is implied with this product. Use at your own risk. I provide no guarantee that it will be profitable, or that it won't lose all your money very quickly or doesn't contain bugs. All financial trading offers the possibility of loss. Leveraged trading, may result in you losing all your money, and still owing more. Backtested results are no guarantee of future performance. I can take no responsibility for any losses caused by live trading using roboquant. Use at your own risk. I am not registered or authorised by any financial regulator.

## Kotlin version
Next to this Python version of `roboquant`, there is also a Kotlin version available. Both (will) share a similar API, just the used computer language is different. Which one to use depends very much on personal preferences, skills and usage.
83 changes: 83 additions & 0 deletions roboquant/strategies/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ def returns(self, period=1):
return ReturnsFeature(self)
return LongReturnsFeature(self, period)

def __getitem__(self, *args):
return SlicedFeature(self, args)


class SlicedFeature(Feature):

def __init__(self, feature: Feature, args: tuple) -> None:
super().__init__()
self.args = args
self.feature = feature

def calc(self, evt):
values = self.feature.calc(evt)
return values[*self.args]


class FixedValueFeature(Feature):

Expand All @@ -37,6 +52,7 @@ def calc(self, evt):


class PriceFeature(Feature):
"""Extract a single price for a symbol"""

def __init__(self, symbol: str, price_type: str = "DEFAULT") -> None:
self.symbol = symbol
Expand All @@ -50,6 +66,7 @@ def calc(self, evt):


class CandleFeature(Feature):
"""Extract the ohlcv values for a symbol"""

def __init__(self, symbol: str) -> None:

Expand All @@ -64,7 +81,28 @@ def calc(self, evt):
return np.full((5,), float("nan"))


class FillFeature(Feature):
"""If the feature returns nan, use the last complete values instead"""

def __init__(self, feature: Feature) -> None:
super().__init__()
self.history = None
self.feature: Feature = feature

def calc(self, evt):
values = self.feature.calc(evt)

if np.any(np.isnan(values)):
if self.history is not None:
return self.history
return values

self.history = values
return values


class VolumeFeature(Feature):
"""Extract the volume for a symbol"""

def __init__(self, symbol, volume_type: str = "DEFAULT") -> None:
super().__init__()
Expand Down Expand Up @@ -116,6 +154,50 @@ def calc(self, evt):
return r


class MaxReturnFeature(Feature):
"""Calculate the maximum return over a certain period.
This will only work on features that return a single value.
"""
def __init__(self, feature: Feature, period: int) -> None:
super().__init__()
self.history = deque(maxlen=period)
self.feature: Feature = feature

def calc(self, evt):
values = self.feature.calc(evt)
h = self.history

if len(h) < h.maxlen: # type: ignore
h.append(values)
return np.full(values.shape, float("nan"))

r = max(h) / h[0] - 1.0
h.append(values)
return r


class MinReturnFeature(Feature):
"""Calculate the minimum return over a certain period.
This will only work on features that return a single value.
"""
def __init__(self, feature: Feature, period: int) -> None:
super().__init__()
self.history = deque(maxlen=period)
self.feature: Feature = feature

def calc(self, evt):
values = self.feature.calc(evt)
h = self.history

if len(h) < h.maxlen: # type: ignore
h.append(values)
return np.full(values.shape, float("nan"))

r = min(h) / h[0] - 1.0
h.append(values)
return r


class SMAFeature(Feature):

def __init__(self, feature, period) -> None:
Expand All @@ -141,6 +223,7 @@ def calc(self, evt):


class DayOfWeekFeature(Feature):
"""Calculate a one-hot-encoded day of week"""

def __init__(self, tz=timezone.utc) -> None:
self.tz = tz
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/test_features.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest

import numpy as np
from roboquant.event import Event

from roboquant.strategies.features import (
PriceFeature,
Expand Down Expand Up @@ -36,6 +37,16 @@ def test_all_features(self):
result = feature.calc(evt)
self.assertTrue(len(result) > 0)

def test_core_feature(self):
f = FixedValueFeature(np.ones(10,))[2:5]
values = f.calc(Event.empty())
self.assertEqual(3, len(values))

f = FixedValueFeature(np.ones(10,)).returns()
values = f.calc(Event.empty())
values = f.calc(Event.empty())
self.assertEqual(0, values[0])


if __name__ == "__main__":
unittest.main()

0 comments on commit 5df6f64

Please sign in to comment.