diff --git a/roboquant/account.py b/roboquant/account.py index a089c54..456e719 100644 --- a/roboquant/account.py +++ b/roboquant/account.py @@ -9,10 +9,10 @@ @dataclass(slots=True) class Position: - """Position of an asset""" + """Position of an asset in the account""" size: Decimal - """Position size""" + """Position size as a Decimal""" avg_price: float """Average price paid denoted in the currency of the asset""" @@ -22,10 +22,12 @@ class Position: @property def is_short(self): + """Return True is this is a short position, False otherwise""" return self.size < 0 @property def is_long(self): + """Return True is this is a long position, False otherwise""" return self.size > 0 diff --git a/roboquant/event.py b/roboquant/event.py index e315271..3e0a397 100644 --- a/roboquant/event.py +++ b/roboquant/event.py @@ -133,7 +133,8 @@ class Event: An event represents zero of items of information happening at a certain moment in time. - `Event.time` is a datetime object with the timezone set at UTC. - - An item can be any type of object. But the most common use-case are price-items like quotes, trades or bars. + - An item can be any type of object. But the most common use-case are of the type `PriceItem`, + like `Quote`, `Trade` or `Bar`. """ def __init__(self, dt: datetime, items: list[Any]): @@ -150,9 +151,6 @@ def is_empty(self) -> bool: """return True if this is an empty event without any items, False otherwise""" return len(self.items) == 0 - # def __len__(self) -> int: - # return len(self.items) - @cached_property def price_items(self) -> dict[Asset, PriceItem]: """Returns the price-items in this event for each asset. diff --git a/roboquant/monetary.py b/roboquant/monetary.py index f4846fe..a7adaf4 100644 --- a/roboquant/monetary.py +++ b/roboquant/monetary.py @@ -72,13 +72,13 @@ def convert(self, amount: "Amount", to_currency: Currency, dt: datetime) -> floa class ECBConversion(CurrencyConverter): - """Currency that gets it exchange rates from the ECB (European Central Bank).""" + """CurrencyConverter that gets it exchange rates from the ECB (European Central Bank).""" __file_name = Path.home() / ".roboquant" / "eurofxref-hist.csv" - def __init__(self): + def __init__(self, force_download=False): self._rates: Dict[Currency, List[Any]] = {} - if not self.exists(): + if force_download or not self.up_to_date(): self._download() self._parse() @@ -89,7 +89,7 @@ def _download(self): p = Path.home() / ".roboquant" z.extractall(p) - def exists(self): + def up_to_date(self): """True if there is already a recently (< 12 hours) downloaded file""" if not self.__file_name.exists(): return False @@ -158,7 +158,7 @@ def convert(self, amount: "Amount", to_currency: str, dt: datetime) -> float: @dataclass(frozen=True, slots=True) class Amount: - """A monetary value denoted in a single currency. Amounts are immutable""" + """A monetary value denoted in a single `Currency`. Amounts are immutable""" currency: Currency value: float @@ -177,7 +177,7 @@ def amounts(self): def __add__(self, other: "Amount") -> "Wallet": """Add another amount to this amount. - This will always return a wallet, even if both amounts have the same currency. + This will always return a wallet, even if both amounts are denoted in the same currency. """ return Wallet(self, other) diff --git a/roboquant/order.py b/roboquant/order.py index 3baf3ab..e0b1d5c 100644 --- a/roboquant/order.py +++ b/roboquant/order.py @@ -12,13 +12,13 @@ class Order: """ A trading order for an asset. Each order has a `size` and a `limit` price. + Order with a positive `size` are buy orders and with a negative `size` are sell orders. + The `gtd` (good till date) is optional and if not set implies the order is valid - for ever. - - The `info` will hold any abritrary properties set on the order. + for ever. The `info` will hold any abritrary properties (kwargs) set on the order. The `id` is automatically assigned by the `Broker` and should not be set manually. - Also, the `fill` is managed by the broker and should not be manually set. + The same applies to the `fill` property. """ asset: Asset diff --git a/tests/samples/monetary.py b/tests/samples/monetary.py index 3d36beb..d6c7bb8 100644 --- a/tests/samples/monetary.py +++ b/tests/samples/monetary.py @@ -10,7 +10,7 @@ print("The wallet contains", wallet) # %% -# Install a currency converter +# Install the ECB currency converter ECBConversion().register() # Convert a wallet to a single currency diff --git a/tests/samples/walkforward_multiprocess.py b/tests/samples/walkforward_multiprocess.py index 62f5d9e..41c46d2 100644 --- a/tests/samples/walkforward_multiprocess.py +++ b/tests/samples/walkforward_multiprocess.py @@ -1,4 +1,6 @@ """This example shows how to perform a walk-forward using a multi-process approach. +This allows you to utlize all the CPU's available on the machine (at the cost of higher memory usage) + Each run is over a certain timeframe and set of parameters for the EMA Crossover strategy. """ @@ -13,7 +15,7 @@ def _walkforward(params): - """Perform a run over the provided timeframe""" + """Perform a run over the provided timeframe and EMA parameters""" timeframe, (fast, slow) = params strategy = rq.strategies.EMACrossover(fast, slow) acc = rq.run(FEED, strategy, timeframe=timeframe) @@ -22,9 +24,10 @@ def _walkforward(params): if __name__ == "__main__": - # Using "fork" ensures that the FEED object is not being created for each process - # The pool is created with default number of processes (equals CPU cores available) + # Using "fork" ensures that the FEED object is not being recreated for each process + # The pool is created with default number of processes (equal to the number of CPU cores) with get_context("fork").Pool() as p: + # Split overal timeframe into 5 equal non-overlapping timeframes timeframes = FEED.timeframe().split(5)