Skip to content

Commit

Permalink
Fix window padding calculation bug.
Browse files Browse the repository at this point in the history
  • Loading branch information
notadamking committed Nov 11, 2019
1 parent 48cfbad commit 70c3953
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 71 deletions.
74 changes: 41 additions & 33 deletions tensortrade/exchanges/live/ccxt_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,45 @@ class CCXTExchange(Exchange):

def __init__(self, exchange: Union[Exchange, str] = 'coinbase', **kwargs):
super(CCXTExchange, self).__init__(
dtype=self.default('dtype', np.float16, kwargs),
feature_pipeline=self.default('feature_pipeline', None, kwargs)
dtype=self.default('dtype', np.float32, kwargs),
feature_pipeline=self.default('feature_pipeline', None, kwargs),
**kwargs
)

exchange = self.default('exchange', exchange)

self._exchange = getattr(ccxt, exchange)() if \
isinstance(exchange, str) else exchange

self._credentials = self.default('credentials', None, kwargs)
self._exchange.enableRateLimit = self.default('enable_rate_limit', True, kwargs)
self._markets = self._exchange.load_markets()
self._timeframe = self.default('timeframe', '10m', kwargs)
self._observation_type = self.default('observation_type', 'trades', kwargs)
self._observation_symbol = self.default('observation_symbol', 'ETH/BTC', kwargs)
self._timeframe = self.default('timeframe', '10m', kwargs)
self._window_size = self.default('window_size', 1, kwargs)
self._async_timeout_in_ms = self.default('async_timeout_in_ms', 15, kwargs)
self._max_trade_wait_in_sec = self.default('max_trade_wait_in_sec', 15, kwargs)
self._exchange.enableRateLimit = self.default('enable_rate_limit', True, kwargs)

self._markets = self._exchange.load_markets()

self._min_trade_amount = float(
self._markets[self._observation_symbol]['limits']['amount']['min'])
self._max_trade_amount = float(
self._markets[self._observation_symbol]['limits']['amount']['max'])
self._min_trade_price = float(
self._markets[self._observation_symbol]['limits']['price']['min'])
self._max_trade_price = float(
self._markets[self._observation_symbol]['limits']['price']['max'])

@property
def data_frame(self) -> pd.DataFrame:
return self._data_frame

@data_frame.setter
def data_frame(self, data_frame: pd.DataFrame):
self._data_frame = data_frame

if len(self._data_frame) >= self._window_size:
self._data_frame = self._data_frame.iloc[-(self._window_size - 1):]

@property
def base_precision(self) -> float:
Expand Down Expand Up @@ -91,32 +113,20 @@ def trades(self) -> List[Trade]:
def performance(self) -> pd.DataFrame:
return self._performance

@property
def generated_space(self) -> Space:
low_price = float(self._markets[self._observation_symbol]['limits']['price']['min'])
high_price = float(self._markets[self._observation_symbol]['limits']['price']['max'])
low_volume = float(self._markets[self._observation_symbol]['limits']['amount']['min'])
high_volume = float(self._markets[self._observation_symbol]['limits']['amount']['max'])

if self._observation_type == 'ohlcv':
low = np.array([low_price, low_price, low_price, low_price, low_volume])
high = np.array([high_price, high_price, high_price, high_price, high_volume])
else:
low = np.array([0, low_price, low_price, low_price * low_volume])
high = np.array([1, high_price, high_volume, high_price * high_volume])

low = np.asarray([max(np.finfo(self._dtype).min, x) for x in low], dtype=self._dtype)
high = np.asarray([min(np.finfo(self._dtype).max, x) for x in high], dtype=self._dtype)

return Box(low=low, high=high, dtype=self._dtype)

@property
def generated_columns(self) -> List[str]:
if self._observation_type == 'ohlcv':
return list(['open', 'high', 'low', 'close', 'volume'])

return list(['side', 'price', 'amount', 'cost'])

@property
def observation_columns(self) -> List[str]:
if self.has_next_observation:
self._next_observation().columns

return None

@property
def has_next_observation(self) -> bool:
if self._observation_type == 'ohlcv':
Expand All @@ -139,17 +149,15 @@ def _next_observation(self) -> pd.DataFrame:
obs = pd.DataFrame(obs, columns=self.generated_columns)
obs = pd.concat([self._data_frame, obs], ignore_index=True)

self._data_frame = obs
self.data_frame = obs

if len(self._data_frame) >= self._window_size:
self._data_frame = self._data_frame.iloc[-(self._window_size - 1):]
if self._feature_pipeline is not None:
obs = self._feature_pipeline.transform(obs)

if len(obs) < self._window_size:
padding = np.zeros((len(self.generated_columns), self._window_size - len(obs)))
obs = pd.concat([pd.DataFrame(padding), obs], ignore_index=True)

if self._feature_pipeline is not None:
obs = self._feature_pipeline.transform(obs, self.generated_space)
padding = np.zeros((self._window_size - len(obs), len(self.observation_columns)))
padding = pd.DataFrame(padding, columns=self.observation_columns)
obs = pd.concat([padding, obs], ignore_index=True)

return obs

Expand Down
65 changes: 27 additions & 38 deletions tensortrade/exchanges/simulated/simulated_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,17 @@ class SimulatedExchange(Exchange):

def __init__(self, data_frame: pd.DataFrame = None, **kwargs):
super().__init__(
dtype=self.default('dtype', np.float16),
feature_pipeline=self.default('feature_pipeline', None)
dtype=self.default('dtype', np.float32),
feature_pipeline=self.default('feature_pipeline', None),
**kwargs
)

self._commission_percent = self.default('commission_percent', 0.3, kwargs)
self._base_precision = self.default('base_precision', 2, kwargs)
self._instrument_precision = self.default('instrument_precision', 8, kwargs)
self._min_trade_price = self.default('min_trade_price', 1e-6, kwargs)
self._max_trade_price = self.default('max_trade_price', 1e6, kwargs)
self._min_trade_amount = self.default('min_trade_amount', 1e-3, kwargs)
self._max_trade_amount = self.default('max_trade_amount', 1e6, kwargs)
self._min_order_amount = self.default('min_order_amount', 1e-3, kwargs)

self._initial_balance = self.default('initial_balance', 1e4, kwargs)
self._observation_columns = self.default(
'observation_columns',
['open', 'high', 'low', 'close', 'volume'],
kwargs
)
self._price_column = self.default('price_column', 'close', kwargs)
self._window_size = self.default('window_size', 1, kwargs)
self._pretransform = self.default('pretransform', True, kwargs)
self._price_history = None

self.data_frame = self.default('data_frame', data_frame)

Expand All @@ -71,10 +60,12 @@ def data_frame(self) -> pd.DataFrame:
def data_frame(self, data_frame: pd.DataFrame):
if not isinstance(data_frame, pd.DataFrame):
self._data_frame = data_frame
self._price_history = None
return

self._data_frame = data_frame[self._observation_columns]
self._data_frame = data_frame
self._price_history = data_frame[self._price_column]
self._pre_transformed_columns = data_frame.columns

if self._pretransform:
self.transform_data_frame()
Expand Down Expand Up @@ -113,20 +104,16 @@ def performance(self) -> pd.DataFrame:
return self._performance

@property
def generated_space(self) -> Space:
low = np.array([self._min_trade_price, ] *
(len(self._observation_columns)-1) + [self._min_trade_amount, ])
high = np.array([self._max_trade_price, ] *
(len(self._observation_columns) - 1) + [self._max_trade_amount, ])
def observation_columns(self) -> List[str]:
if self._data_frame is None:
return None

low = np.asarray([max(np.finfo(self._dtype).min, x) for x in low], dtype=self._dtype)
high = np.asarray([min(np.finfo(self._dtype).max, x) for x in high], dtype=self._dtype)
data_frame = self._data_frame.iloc[0:10]

return Box(low=low, high=high, dtype=self._dtype)
if self._feature_pipeline is not None:
data_frame = self._feature_pipeline.transform(data_frame)

@property
def generated_columns(self) -> List[str]:
return list(self._observation_columns)
return data_frame.select_dtypes(include=[np.float, np.number]).columns

@property
def has_next_observation(self) -> bool:
Expand All @@ -138,21 +125,23 @@ def _next_observation(self) -> pd.DataFrame:

obs = self._data_frame.iloc[lower_range:upper_range]

if not self._pretransform and self._feature_pipeline is not None:
obs = self._feature_pipeline.transform(obs)

if len(obs) < self._window_size:
padding = np.zeros((len(self.generated_columns), self._window_size - len(obs)))
obs = pd.concat([pd.DataFrame(padding), obs], ignore_index=True)
padding = np.zeros((self._window_size - len(obs), len(self.observation_columns)))
padding = pd.DataFrame(padding, columns=self.observation_columns)
obs = pd.concat([padding, obs], ignore_index=True)

if not self._pretransform and self._feature_pipeline is not None:
obs = self._feature_pipeline.transform(obs, self.generated_space)
obs = obs.select_dtypes(include='number')

self._current_step += 1

return obs

def transform_data_frame(self) -> bool:
if self._feature_pipeline is not None:
self._data_frame = self._feature_pipeline.transform(self._data_frame,
self.generated_space)
self._data_frame = self._feature_pipeline.transform(self._data_frame)

def current_price(self, symbol: str) -> float:
if self._price_history is not None:
Expand All @@ -161,12 +150,12 @@ def current_price(self, symbol: str) -> float:
return 0

def _is_valid_trade(self, trade: Trade) -> bool:
if trade.trade_type is TradeType.MARKET_BUY or trade.trade_type is TradeType.LIMIT_BUY:
return trade.amount >= self._min_order_amount and self._balance >= trade.amount * trade.price
elif trade.trade_type is TradeType.MARKET_SELL or trade.trade_type is TradeType.LIMIT_SELL:
return trade.amount >= self._min_order_amount and self._portfolio.get(trade.symbol, 0) >= trade.amount
if trade.is_buy and self._balance < trade.amount * trade.price:
return False
elif trade.is_sell and self._portfolio.get(trade.symbol, 0) < trade.amount:
return False

return True
return trade.amount >= self._min_trade_amount and trade.amount <= self._max_trade_amount

def _update_account(self, trade: Trade):
if self._is_valid_trade(trade) and not trade.is_hold:
Expand Down

0 comments on commit 70c3953

Please sign in to comment.