DOC: updating the code docstrings

This commit is contained in:
fredfortier
2017-11-01 21:31:51 -04:00
parent 35677c553c
commit 5b6bbacab0
8 changed files with 527 additions and 193 deletions
+141 -58
View File
@@ -1,5 +1,4 @@
import abc import abc
import re
from abc import ABCMeta, abstractmethod, abstractproperty from abc import ABCMeta, abstractmethod, abstractproperty
from datetime import timedelta from datetime import timedelta
from time import sleep from time import sleep
@@ -16,7 +15,7 @@ from catalyst.exchange.bundle_utils import get_start_dt, \
from catalyst.exchange.exchange_bundle import ExchangeBundle from catalyst.exchange.exchange_bundle import ExchangeBundle
from catalyst.exchange.exchange_errors import MismatchingBaseCurrencies, \ from catalyst.exchange.exchange_errors import MismatchingBaseCurrencies, \
InvalidOrderStyle, BaseCurrencyNotFoundError, SymbolNotFoundOnExchange, \ InvalidOrderStyle, BaseCurrencyNotFoundError, SymbolNotFoundOnExchange, \
InvalidHistoryFrequencyError, PricingDataNotLoadedError, \ PricingDataNotLoadedError, \
NoDataAvailableOnExchange NoDataAvailableOnExchange
from catalyst.exchange.exchange_execution import ExchangeStopLimitOrder, \ from catalyst.exchange.exchange_execution import ExchangeStopLimitOrder, \
ExchangeLimitOrder, ExchangeStopOrder ExchangeLimitOrder, ExchangeStopOrder
@@ -53,9 +52,11 @@ class Exchange:
@property @property
def portfolio(self): def portfolio(self):
""" """
Return the Portfolio The exchange portfolio
:return: Returns
-------
ExchangePortfolio
""" """
if self._portfolio is None: if self._portfolio is None:
self._portfolio = ExchangePortfolio( self._portfolio = ExchangePortfolio(
@@ -75,9 +76,16 @@ class Exchange:
def is_open(self, dt): def is_open(self, dt):
""" """
Is the exchange open? Is the exchange open
:param dt:
:return: Parameters
----------
dt: Timestamp
Returns
-------
bool
""" """
# TODO: implement for each exchange. # TODO: implement for each exchange.
return True return True
@@ -90,7 +98,9 @@ class Exchange:
The application will pause if the maximum requests per minute The application will pause if the maximum requests per minute
permitted by the exchange is exceeded. permitted by the exchange is exceeded.
:return boolean: Returns
-------
bool
""" """
now = pd.Timestamp.utcnow() now = pd.Timestamp.utcnow()
@@ -122,10 +132,16 @@ class Exchange:
def get_symbol(self, asset): def get_symbol(self, asset):
""" """
Get the exchange specific symbol of the given asset. The the exchange specific symbol of the specified market.
Parameters
----------
asset: TradingPair
Returns
-------
str
:param asset: Asset
:return: symbol: str
""" """
symbol = None symbol = None
@@ -143,17 +159,34 @@ class Exchange:
""" """
Get a list of symbols corresponding to each given asset. Get a list of symbols corresponding to each given asset.
:param assets: Asset[] Parameters
:return: ----------
assets: list[TradingPair]
Returns
-------
list[str]
""" """
symbols = [] symbols = []
for asset in assets: for asset in assets:
symbols.append(self.get_symbol(asset)) symbols.append(self.get_symbol(asset))
return symbols return symbols
def get_assets(self, symbols=None): def get_assets(self, symbols=None):
"""
The list of markets for the specified symbols.
Parameters
----------
symbols: list[str]
Returns
-------
list[TradingPair]
"""
assets = [] assets = []
if symbols is not None: if symbols is not None:
@@ -168,9 +201,16 @@ class Exchange:
def get_asset(self, symbol): def get_asset(self, symbol):
""" """
Find an Asset on the current exchange based on its Catalyst symbol The market for the specified symbol.
:param symbol: the [target]_[base] currency pair symbol
:return: Asset Parameters
----------
symbol: str
Returns
-------
TradingPair
""" """
asset = None asset = None
@@ -201,7 +241,6 @@ class Exchange:
currency pair symbol. The universal symbol is contained in the currency pair symbol. The universal symbol is contained in the
'symbol' attribute of each asset. 'symbol' attribute of each asset.
Notes Notes
----- -----
The sid of each asset is calculated based on a numeric hash of the The sid of each asset is calculated based on a numeric hash of the
@@ -210,8 +249,8 @@ class Exchange:
This method can be overridden if an exchange offers equivalent data This method can be overridden if an exchange offers equivalent data
via its api. via its api.
"""
"""
symbol_map = self.fetch_symbol_map() symbol_map = self.fetch_symbol_map()
for exchange_symbol in symbol_map: for exchange_symbol in symbol_map:
asset = symbol_map[exchange_symbol] asset = symbol_map[exchange_symbol]
@@ -272,8 +311,10 @@ class Exchange:
For each executed order found, create a transaction and apply to the For each executed order found, create a transaction and apply to the
Portfolio. Portfolio.
:return: Returns
transactions: Transaction[] -------
list[Transaction]
""" """
transactions = list() transactions = list()
if self.portfolio.open_orders: if self.portfolio.open_orders:
@@ -390,14 +431,20 @@ class Exchange:
""" """
Get a series of field data for the specified candles. Get a series of field data for the specified candles.
:param candles: Parameters
:param start_dt: ----------
:param end_dt: candles: list[dict[str, float]]
:param field: start_dt: datetime
:param previous_value: end_dt: datetime
:return: data_frequency: str
""" field: str
previous_value: float
Returns
-------
Series
"""
dates = [candle['last_traded'] for candle in candles] dates = [candle['last_traded'] for candle in candles]
values = [candle[field] for candle in candles] values = [candle[field] for candle in candles]
series = pd.Series(values, index=dates) series = pd.Series(values, index=dates)
@@ -430,10 +477,11 @@ class Exchange:
Parameters Parameters
---------- ----------
assets : list of catalyst.data.Asset objects assets : list[TradingPair]
The assets whose data is desired. The assets whose data is desired.
end_dt: not applicable to cryptocurrencies end_dt: datetime
The date of the last bar
bar_count: int bar_count: int
The number of bars desired. The number of bars desired.
@@ -493,10 +541,11 @@ class Exchange:
Parameters Parameters
---------- ----------
assets : list of catalyst.data.Asset objects assets : list[TradingPair]
The assets whose data is desired. The assets whose data is desired.
end_dt: not applicable to cryptocurrencies end_dt: datetime
The date of the last bar.
bar_count: int bar_count: int
The number of bars desired. The number of bars desired.
@@ -518,9 +567,10 @@ class Exchange:
Returns Returns
------- -------
A dataframe containing the requested data. DataFrame
""" A dataframe containing the requested data.
"""
freq, candle_size, unit, data_frequency = get_frequency( freq, candle_size, unit, data_frequency = get_frequency(
frequency, data_frequency frequency, data_frequency
) )
@@ -591,7 +641,6 @@ class Exchange:
Update the portfolio cash and position balances based on the Update the portfolio cash and position balances based on the
latest ticker prices. latest ticker prices.
:return:
""" """
log.debug('synchronizing portfolio with exchange {}'.format(self.name)) log.debug('synchronizing portfolio with exchange {}'.format(self.name))
balances = self.get_balances() balances = self.get_balances()
@@ -635,16 +684,20 @@ class Exchange:
Parameters Parameters
---------- ----------
asset : Asset asset : TradingPair
The asset that this order is for. The asset that this order is for.
amount : int amount : int
The amount of shares to order. If ``amount`` is positive, this is The amount of shares to order. If ``amount`` is positive, this is
the number of shares to buy or cover. If ``amount`` is negative, the number of shares to buy or cover. If ``amount`` is negative,
this is the number of shares to sell or short. this is the number of shares to sell or short.
limit_price : float, optional limit_price : float, optional
The limit price for the order. The limit price for the order.
stop_price : float, optional stop_price : float, optional
The stop price for the order. The stop price for the order.
style : ExecutionStyle, optional style : ExecutionStyle, optional
The execution style for the order. The execution style for the order.
@@ -669,6 +722,7 @@ class Exchange:
:class:`catalyst.finance.execution.ExecutionStyle` :class:`catalyst.finance.execution.ExecutionStyle`
:func:`catalyst.api.order_value` :func:`catalyst.api.order_value`
:func:`catalyst.api.order_percent` :func:`catalyst.api.order_percent`
""" """
if amount == 0: if amount == 0:
log.warn('skipping order amount of 0') log.warn('skipping order amount of 0')
@@ -718,8 +772,12 @@ class Exchange:
@abstractmethod @abstractmethod
def get_balances(self): def get_balances(self):
""" """
Retrieve wallet balances for the exchange Retrieve wallet balances for the exchange.
:return balances: A dict of currency => available balance
Returns
-------
dict[TradingPair, float]
""" """
pass pass
@@ -728,17 +786,25 @@ class Exchange:
""" """
Place an order on the exchange. Place an order on the exchange.
:param asset : Asset Parameters
The asset that this order is for. ----------
:param amount : int asset: TradingPair
The target market.
amount: float
The amount of shares to order. If ``amount`` is positive, this is The amount of shares to order. If ``amount`` is positive, this is
the number of shares to buy or cover. If ``amount`` is negative, the number of shares to buy or cover. If ``amount`` is negative,
this is the number of shares to sell or short. this is the number of shares to sell or short.
:param style : ExecutionStyle
The execution style for the order. is_buy: bool
:param is_buy: boolean
Is it a buy order? Is it a buy order?
:return:
style: ExecutionStyle
Returns
-------
Order
""" """
pass pass
@@ -798,19 +864,27 @@ class Exchange:
""" """
Retrieve OHLCV candles for the given assets Retrieve OHLCV candles for the given assets
:param freq: Parameters
----------
freq: str
The frequency alias per convention: The frequency alias per convention:
http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
:param assets: list[TradingPair]
assets: list[TradingPair]
The targeted assets. The targeted assets.
:param bar_count:
bar_count: int
The number of bar desired. (default 1) The number of bar desired. (default 1)
:param end_dt: datetime, optional
end_dt: datetime, optional
The last bar date. The last bar date.
:param start_dt: datetime, optional
start_dt: datetime, optional
The first bar date. The first bar date.
:return dict[TradingPair, dict[str, Object]]: OHLCV data Returns
-------
dict[TradingPair, dict[str, Object]]
A dictionary of OHLCV candles. Each TradingPair instance is A dictionary of OHLCV candles. Each TradingPair instance is
mapped to a list of dictionaries with this structure: mapped to a list of dictionaries with this structure:
open: float open: float
@@ -830,8 +904,14 @@ class Exchange:
""" """
Retrieve current tick data for the given assets Retrieve current tick data for the given assets
:param assets: Parameters
:return: ----------
assets: list[TradingPair]
Returns
-------
list[dict[str, float]
""" """
pass pass
@@ -839,7 +919,6 @@ class Exchange:
def get_account(self): def get_account(self):
""" """
Retrieve the account parameters. Retrieve the account parameters.
:return:
""" """
pass pass
@@ -848,11 +927,15 @@ class Exchange:
""" """
Retrieve the the orderbook for the given trading pair. Retrieve the the orderbook for the given trading pair.
:param asset: TradingPair Parameters
:param order_type: str ----------
asset: TradingPair
order_type: str
The type of orders: bid, ask or all The type of orders: bid, ask or all
:param limit limit: int
:return: Returns
-------
list[dict[str, float]
""" """
pass pass
+110 -8
View File
@@ -127,7 +127,13 @@ class ExchangeTradingAlgorithmBase(TradingAlgorithm):
""" """
Creates a dictionary representing the state of the tracker. Creates a dictionary representing the state of the tracker.
Parameters
----------
start_dt: datetime
end_dt: datetime
Notes
-----
I rewrote this in an attempt to better control the stats. I rewrote this in an attempt to better control the stats.
I don't want things to happen magically through complex logic I don't want things to happen magically through complex logic
pertaining to backtesting. pertaining to backtesting.
@@ -296,6 +302,18 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
self.exchange.minute_reader = BcolzMinuteBarReader(root) self.exchange.minute_reader = BcolzMinuteBarReader(root)
def signal_handler(self, signal, frame): def signal_handler(self, signal, frame):
"""
Handles the keyboard interruption signal.
Parameters
----------
signal
frame
Returns
-------
"""
self.is_running = False self.is_running = False
if self._analyze is None: if self._analyze is None:
@@ -384,7 +402,11 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
""" """
We skip the entire performance tracker business and update the We skip the entire performance tracker business and update the
portfolio directly. portfolio directly.
:return:
Returns
-------
ExchangePortfolio
""" """
# TODO: build cumulative portfolio # TODO: build cumulative portfolio
return self.perf_tracker.get_portfolio(False) return self.perf_tracker.get_portfolio(False)
@@ -450,6 +472,17 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
) )
def add_pnl_stats(self, period_stats): def add_pnl_stats(self, period_stats):
"""
Save p&l stats.
Parameters
----------
period_stats
Returns
-------
"""
starting = period_stats['starting_cash'] starting = period_stats['starting_cash']
current = period_stats['portfolio_value'] current = period_stats['portfolio_value']
appreciation = (current / starting) - 1 appreciation = (current / starting) - 1
@@ -466,6 +499,17 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
save_algo_df(self.algo_namespace, 'pnl_stats', self.pnl_stats) save_algo_df(self.algo_namespace, 'pnl_stats', self.pnl_stats)
def add_custom_signals_stats(self, period_stats): def add_custom_signals_stats(self, period_stats):
"""
Save custom signals stats.
Parameters
----------
period_stats
Returns
-------
"""
log.debug('adding custom signals stats: {}'.format(self.recorded_vars)) log.debug('adding custom signals stats: {}'.format(self.recorded_vars))
df = pd.DataFrame( df = pd.DataFrame(
data=[self.recorded_vars], data=[self.recorded_vars],
@@ -477,6 +521,17 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
self.custom_signals_stats) self.custom_signals_stats)
def add_exposure_stats(self, period_stats): def add_exposure_stats(self, period_stats):
"""
Save exposure stats.
Parameters
----------
period_stats
Returns
-------
"""
data = dict( data = dict(
long_exposure=period_stats['long_exposure'], long_exposure=period_stats['long_exposure'],
base_currency=period_stats['ending_cash'] base_currency=period_stats['ending_cash']
@@ -493,6 +548,14 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
self.exposure_stats) self.exposure_stats)
def handle_data(self, data): def handle_data(self, data):
"""
Wrapper around the handle_data method of each algo.
Parameters
----------
data
"""
if not self.is_running: if not self.is_running:
return return
@@ -619,15 +682,16 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
The cumulative portfolio does not contain open orders but exchange The cumulative portfolio does not contain open orders but exchange
portfolios do. portfolios do.
:param asset: TradingPair Parameters
:param amount: float ----------
:param limit_price: float asset: TradingPair
:param stop_price: float amount: float
:param style: Style limit_price: float
:return order: Order stop_price: float
style: Style
order: Order
The catalyst order object or None The catalyst order object or None
""" """
amount, style = self._calculate_order(asset, amount, amount, style = self._calculate_order(asset, amount,
limit_price, stop_price, limit_price, stop_price,
style) style)
@@ -689,15 +753,53 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase):
'get_open_orders. Use `asset` instead.') 'get_open_orders. Use `asset` instead.')
@api_method @api_method
def get_open_orders(self, asset=None): def get_open_orders(self, asset=None):
"""Retrieve all of the current open orders.
Parameters
----------
asset : Asset
If passed and not None, return only the open orders for the given
asset instead of all open orders.
Returns
-------
open_orders : dict[list[Order]] or list[Order]
If no asset is passed this will return a dict mapping Assets
to a list containing all the open orders for the asset.
If an asset is passed then this will return a list of the open
orders for this asset.
"""
return self._get_open_orders(asset) return self._get_open_orders(asset)
@api_method @api_method
def get_order(self, order_id, exchange_name): def get_order(self, order_id, exchange_name):
"""Lookup an order based on the order id returned from one of the
order functions.
Parameters
----------
order_id : str
The unique identifier for the order.
Returns
-------
order : Order
The order object.
execution_price: float
The execution price per share of the order
"""
exchange = self.exchanges[exchange_name] exchange = self.exchanges[exchange_name]
return exchange.get_order(order_id) return exchange.get_order(order_id)
@api_method @api_method
def cancel_order(self, order_param, exchange_name): def cancel_order(self, order_param, exchange_name):
"""Cancel an open order.
Parameters
----------
order_param : str or Order
The order_id or order object to cancel.
"""
exchange = self.exchanges[exchange_name] exchange = self.exchanges[exchange_name]
order_id = order_param order_id = order_param
+18 -10
View File
@@ -39,17 +39,25 @@ class BcolzExchangeBarReader(BcolzMinuteBarReader):
return self._data_frequency return self._data_frequency
def load_raw_arrays(self, fields, start_dt, end_dt, sids): def load_raw_arrays(self, fields, start_dt, end_dt, sids):
"""
Parameters
----------
fields : list of str
'open', 'high', 'low', 'close', or 'volume'
start_dt: Timestamp
Beginning of the window range.
end_dt: Timestamp
End of the window range.
sids : list of int
The asset identifiers in the window.
# if self._data_frequency == 'minute': Returns
# return super(BcolzExchangeBarReader, self) \ -------
# .load_raw_arrays(fields, start_dt, end_dt, sids) list of np.ndarray
# A list with an entry per field of ndarrays with shape
# else: (minutes in range, sids) with a dtype of float64, containing the
# return self._load_daily_raw_arrays(fields, start_dt, end_dt, sids) values for the respective field over start and end dt range.
"""
return self._load_raw_arrays(fields, start_dt, end_dt, sids)
def _load_raw_arrays(self, fields, start_dt, end_dt, sids):
start_idx = self._find_position_of_minute(start_dt) start_idx = self._find_position_of_minute(start_dt)
end_idx = self._find_position_of_minute(end_dt) end_idx = self._find_position_of_minute(end_dt)
+101 -56
View File
@@ -59,7 +59,10 @@ class ExchangeBundle:
""" """
Get a data writer object, either a new object or from cache Get a data writer object, either a new object or from cache
:return: BcolzMinuteBarReader or BcolzDailyBarReader Returns
-------
BcolzMinuteBarReader | BcolzDailyBarReader
""" """
if path is None: if path is None:
root = get_exchange_folder(self.exchange.name) root = get_exchange_folder(self.exchange.name)
@@ -88,7 +91,10 @@ class ExchangeBundle:
""" """
Get a data writer object, either a new object or from cache Get a data writer object, either a new object or from cache
:return: BcolzMinuteBarWriter or BcolzDailyBarWriter Returns
-------
BcolzMinuteBarWriter | BcolzDailyBarWriter
""" """
root = get_exchange_folder(self.exchange.name) root = get_exchange_folder(self.exchange.name)
path = BUNDLE_NAME_TEMPLATE.format( path = BUNDLE_NAME_TEMPLATE.format(
@@ -144,13 +150,19 @@ class ExchangeBundle:
If the data exists, the chunk ingestion is complete. If the data exists, the chunk ingestion is complete.
If any data is missing we ingest the data. If any data is missing we ingest the data.
:param assets: list[TradingPair] Parameters
----------
assets: list[TradingPair]
The assets is scope. The assets is scope.
:param start_dt: start_dt: datetime
The chunk start date. The chunk start date.
:param end_dt: end_dt: datetime
The chunk end date. The chunk end date.
:return: list[TradingPair] data_frequency: str
Returns
-------
list[TradingPair]
The assets missing from the bundle The assets missing from the bundle
""" """
reader = self.get_reader(data_frequency) reader = self.get_reader(data_frequency)
@@ -164,13 +176,6 @@ class ExchangeBundle:
return missing_assets return missing_assets
def _write(self, data, writer, data_frequency): def _write(self, data, writer, data_frequency):
"""
Write data to the writer
:param df:
:param writer:
:return:
"""
try: try:
writer.write( writer.write(
data=data, data=data,
@@ -195,6 +200,20 @@ class ExchangeBundle:
) )
def get_calendar_periods_range(self, start_dt, end_dt, data_frequency): def get_calendar_periods_range(self, start_dt, end_dt, data_frequency):
"""
Get a list of dates for the specified range.
Parameters
----------
start_dt: datetime
end_dt: datetime
data_frequency: str
Returns
-------
list[datetime]
"""
return self.calendar.minutes_in_range(start_dt, end_dt) \ return self.calendar.minutes_in_range(start_dt, end_dt) \
if data_frequency == 'minute' \ if data_frequency == 'minute' \
else self.calendar.sessions_in_range(start_dt, end_dt) else self.calendar.sessions_in_range(start_dt, end_dt)
@@ -204,13 +223,14 @@ class ExchangeBundle:
""" """
Ingest a DataFrame of OHLCV data for a given market. Ingest a DataFrame of OHLCV data for a given market.
:param ohlcv_df: Parameters
:param data_frequency: ----------
:param asset: ohlcv_df: DataFrame
:param writer: data_frequency: str
:param path: asset: TradingPair
:param empty_rows_behavior: writer:
:return: empty_rows_behavior: str
""" """
if empty_rows_behavior is not 'ignore': if empty_rows_behavior is not 'ignore':
nan_rows = ohlcv_df[ohlcv_df.isnull().T.any().T].index nan_rows = ohlcv_df[ohlcv_df.isnull().T.any().T].index
@@ -269,14 +289,16 @@ class ExchangeBundle:
""" """
Merge a ctable bundle chunk into the main bundle for the exchange. Merge a ctable bundle chunk into the main bundle for the exchange.
:param asset: TradingPair Parameters
:param data_frequency: str ----------
:param period: str asset: TradingPair
:param writer: data_frequency: str
:param empty_rows_behavior: str period: str
writer:
empty_rows_behavior: str
Ensure that the bundle does not have any missing data. Ensure that the bundle does not have any missing data.
:param cleanup: bool cleanup: bool
Remove the temp bundle directory after ingestion. Remove the temp bundle directory after ingestion.
:return: :return:
@@ -331,13 +353,19 @@ class ExchangeBundle:
def get_adj_dates(self, start, end, assets, data_frequency): def get_adj_dates(self, start, end, assets, data_frequency):
""" """
Contains a date range to the trading availability of the specified pairs. Contains a date range to the trading availability of the specified
markets.
:param start: Parameters
:param end: ----------
:param assets: start: datetime
:param data_frequency: end: datetime
:return: assets: list[TradingPair]
data_frequency: str
Returns
-------
datetime, datetime
""" """
earliest_trade = None earliest_trade = None
last_entry = None last_entry = None
@@ -380,11 +408,17 @@ class ExchangeBundle:
Split a price data request into chunks corresponding to individual Split a price data request into chunks corresponding to individual
bundles. bundles.
:param assets: Parameters
:param data_frequency: ----------
:param start_dt: assets: list[TradingPair]
:param end_dt: data_frequency: str
:return: start_dt: datetime
end_dt: datetime
Returns
-------
dict[TradingPair, list[dict(str, Object]]]
""" """
reader = self.get_reader(data_frequency) reader = self.get_reader(data_frequency)
@@ -456,10 +490,12 @@ class ExchangeBundle:
""" """
Determine if data is missing from the bundle and attempt to ingest it. Determine if data is missing from the bundle and attempt to ingest it.
:param assets: Parameters
:param start_dt: ----------
:param end_dt: assets: list[TradingPair]
:return: start_dt: datetime
end_dt: datetime
""" """
if start_dt is None: if start_dt is None:
@@ -538,15 +574,18 @@ class ExchangeBundle:
exclude_symbols=None, start=None, end=None, exclude_symbols=None, start=None, end=None,
show_progress=True, environ=os.environ): show_progress=True, environ=os.environ):
""" """
Inject data based on specified parameters.
Parameters
----------
data_frequency: str
include_symbols: str
exclude_symbols: str
start: datetime
end: datetime
show_progress: bool
environ:
:param data_frequency:
:param include_symbols:
:param exclude_symbols:
:param start:
:param end:
:param show_progress:
:param environ:
:return:
""" """
assets = self.get_assets(include_symbols, exclude_symbols) assets = self.get_assets(include_symbols, exclude_symbols)
@@ -562,16 +601,22 @@ class ExchangeBundle:
data_frequency, # type: str data_frequency, # type: str
algo_end_dt=None # type: Timestamp algo_end_dt=None # type: Timestamp
): ):
# type: (...) -> Dict[str, Series]
""" """
Retrieve price data history, ingest missing data. Retrieve price data history, ingest missing data.
:param assets: Parameters
:param end_dt: ----------
:param bar_count: assets: list[TradingPair]
:param field: end_dt: datetime
:param data_frequency: bar_count: int
:return: field: str
data_frequency: str
algo_end_dt: datetime
Returns
-------
Series
""" """
try: try:
series = self.get_history_window_series( series = self.get_history_window_series(
+66 -37
View File
@@ -1,16 +1,3 @@
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import abc import abc
from time import sleep from time import sleep
@@ -238,6 +225,25 @@ class DataPortalExchangeLive(DataPortalExchangeBase):
field, field,
data_frequency, data_frequency,
ffill=True): ffill=True):
"""
Fetching price history window from the exchange.
Parameters
----------
exchange: Exchange
assets: list[TradingPair]
end_dt: datetime
bar_count: int
frequency: str
field: str
data_frequency: str
ffill: bool
Returns
-------
DataFrame
"""
df = exchange.get_history_window( df = exchange.get_history_window(
assets, assets,
end_dt, end_dt,
@@ -250,6 +256,22 @@ class DataPortalExchangeLive(DataPortalExchangeBase):
def get_exchange_spot_value(self, exchange, assets, field, dt, def get_exchange_spot_value(self, exchange, assets, field, dt,
data_frequency): data_frequency):
"""
A spot value for the exchange.
Parameters
----------
exchange: Exchange
assets: list[TradingPair]
field: str
dt: datetime
data_frequency: str
Returns
-------
float
"""
exchange_spot_values = exchange.get_spot_value( exchange_spot_values = exchange.get_spot_value(
assets, field, dt, data_frequency) assets, field, dt, data_frequency)
@@ -288,18 +310,21 @@ class DataPortalExchangeBacktest(DataPortalExchangeBase):
""" """
Fetching price history window from the exchange bundle. Fetching price history window from the exchange bundle.
Using a try... except approach to minimize reads most of the time, Parameters
when the data exists. ----------
exchange: Exchange
assets: list[TradingPair]
end_dt: datetime
bar_count: int
frequency: str
field: str
data_frequency: str
ffill: bool
Returns
-------
DataFrame
:param exchange:
:param assets:
:param end_dt:
:param bar_count:
:param frequency:
:param field:
:param data_frequency:
:param ffill:
:return:
""" """
bundle = self.exchange_bundles[exchange.name] # type: ExchangeBundle bundle = self.exchange_bundles[exchange.name] # type: ExchangeBundle
@@ -321,26 +346,30 @@ class DataPortalExchangeBacktest(DataPortalExchangeBase):
return df return df
def get_exchange_spot_value(self, def get_exchange_spot_value(self,
exchange, # type: Exchange exchange,
assets, # type: List[TradingPair] assets,
field, # type: str field,
dt, # type: Timestamp dt,
data_frequency # type: str data_frequency
): ):
# type: (...) -> float
""" """
A spot value for the exchange bundle. Try to ingest data if not in A spot value for the exchange bundle. Try to ingest data if not in
the bundle. the bundle.
:param exchange: Parameters
:param assets: ----------
:param field: exchange: Exchange
:param dt: assets: list[TradingPair]
:param data_frequency: field: str
:return: dt: datetime
data_frequency: str
Returns
-------
float
""" """
bundle = self.exchange_bundles[exchange.name] bundle = self.exchange_bundles[exchange.name]
if data_frequency == 'daily': if data_frequency == 'daily':
dt = dt.floor('1D') dt = dt.floor('1D')
else: else:
+40 -12
View File
@@ -4,9 +4,16 @@ from catalyst.finance.execution import LimitOrder, StopOrder, StopLimitOrder
class ExchangeLimitOrder(LimitOrder): class ExchangeLimitOrder(LimitOrder):
def get_limit_price(self, is_buy): def get_limit_price(self, is_buy):
""" """
We may be trading Satoshis with 8 decimals, we cannot round numbers We may be trading Satoshis with 8 decimals, we cannot round numbers.
:param is_buy:
:return: Parameters
----------
is_buy: bool
Returns
-------
float
""" """
return self.limit_price return self.limit_price
@@ -14,9 +21,16 @@ class ExchangeLimitOrder(LimitOrder):
class ExchangeStopOrder(StopOrder): class ExchangeStopOrder(StopOrder):
def get_stop_price(self, is_buy): def get_stop_price(self, is_buy):
""" """
We may be trading Satoshis with 8 decimals, we cannot round numbers We may be trading Satoshis with 8 decimals, we cannot round numbers.
:param is_buy:
:return: Parameters
----------
is_buy: bool
Returns
-------
float
""" """
return self.stop_price return self.stop_price
@@ -24,16 +38,30 @@ class ExchangeStopOrder(StopOrder):
class ExchangeStopLimitOrder(StopLimitOrder): class ExchangeStopLimitOrder(StopLimitOrder):
def get_limit_price(self, is_buy): def get_limit_price(self, is_buy):
""" """
We may be trading Satoshis with 8 decimals, we cannot round numbers We may be trading Satoshis with 8 decimals, we cannot round numbers.
:param is_buy:
:return: Parameters
----------
is_buy: bool
Returns
-------
float
""" """
return self.limit_price return self.limit_price
def get_stop_price(self, is_buy): def get_stop_price(self, is_buy):
""" """
We may be trading Satoshis with 8 decimals, we cannot round numbers We may be trading Satoshis with 8 decimals, we cannot round numbers.
:param is_buy:
:return: Parameters
----------
is_buy: bool
Returns
-------
float
""" """
return self.stop_price return self.stop_price
+31 -3
View File
@@ -3,6 +3,7 @@ from logbook import Logger
from catalyst.constants import LOG_LEVEL from catalyst.constants import LOG_LEVEL
from catalyst.protocol import Portfolio, Positions, Position from catalyst.protocol import Portfolio, Positions, Position
from catalyst.utils.deprecate import deprecated
log = Logger('ExchangePortfolio', level=LOG_LEVEL) log = Logger('ExchangePortfolio', level=LOG_LEVEL)
@@ -29,10 +30,15 @@ class ExchangePortfolio(Portfolio):
self.positions_value = 0.0 self.positions_value = 0.0
self.open_orders = dict() self.open_orders = dict()
def calculate_pnl(self):
log.debug('calculating pnl')
def create_order(self, order): def create_order(self, order):
"""
Create an open order and store in memory.
Parameters
----------
order: Order
"""
log.debug('creating order {}'.format(order.id)) log.debug('creating order {}'.format(order.id))
self.open_orders[order.id] = order self.open_orders[order.id] = order
@@ -47,6 +53,18 @@ class ExchangePortfolio(Portfolio):
log.debug('open order added to portfolio') log.debug('open order added to portfolio')
def execute_order(self, order, transaction): def execute_order(self, order, transaction):
"""
Update the open orders and positions to apply an executed order.
Unlike with backtesting, we do not need to add slippage and fees.
The executed price includes transaction fees.
Parameters
----------
order: Order
transaction: Transaction
"""
log.debug('executing order {}'.format(order.id)) log.debug('executing order {}'.format(order.id))
del self.open_orders[order.id] del self.open_orders[order.id]
@@ -71,7 +89,9 @@ class ExchangePortfolio(Portfolio):
log.debug('updated portfolio with executed order') log.debug('updated portfolio with executed order')
@deprecated
def execute_transaction(self, transaction): def execute_transaction(self, transaction):
# TODO: almost duplicate of execute_order. Not sure why Poloniex needs this.
log.debug('executing transaction {}'.format(transaction.order_id)) log.debug('executing transaction {}'.format(transaction.order_id))
order_position = self.positions[transaction.asset] \ order_position = self.positions[transaction.asset] \
@@ -96,6 +116,14 @@ class ExchangePortfolio(Portfolio):
log.debug('updated portfolio with executed order') log.debug('updated portfolio with executed order')
def remove_order(self, order): def remove_order(self, order):
"""
Removing an open order.
Parameters
----------
order: Order
"""
log.info('removing cancelled order {}'.format(order.id)) log.info('removing cancelled order {}'.format(order.id))
del self.open_orders[order.id] del self.open_orders[order.id]
+20 -9
View File
@@ -2,12 +2,11 @@ import json
import os import os
import pickle import pickle
import re import re
from catalyst.assets._assets import TradingPair
from six.moves.urllib import request
from datetime import date, datetime from datetime import date, datetime
import pandas as pd import pandas as pd
from catalyst.assets._assets import TradingPair
from six.moves.urllib import request
from catalyst.exchange.exchange_errors import ExchangeSymbolsNotFound, \ from catalyst.exchange.exchange_errors import ExchangeSymbolsNotFound, \
InvalidHistoryFrequencyError, InvalidHistoryFrequencyAlias InvalidHistoryFrequencyError, InvalidHistoryFrequencyAlias
@@ -22,9 +21,15 @@ def get_exchange_folder(exchange_name, environ=None):
""" """
The root path of an exchange folder. The root path of an exchange folder.
:param exchange_name: Parameters
:param environ: ----------
:return: exchange_name: str
environ:
Returns
-------
str
""" """
if not environ: if not environ:
environ = os.environ environ = os.environ
@@ -40,9 +45,15 @@ def get_exchange_symbols_filename(exchange_name, environ=None):
""" """
The absolute path of the exchange's symbol.json file. The absolute path of the exchange's symbol.json file.
:param exchange_name: Parameters
:param environ: ----------
:return: exchange_name:
environ:
Returns
-------
str
""" """
exchange_folder = get_exchange_folder(exchange_name, environ) exchange_folder = get_exchange_folder(exchange_name, environ)
return os.path.join(exchange_folder, 'symbols.json') return os.path.join(exchange_folder, 'symbols.json')