Skip to content

Commit

Permalink
[Backtesting] update orders management and price channel
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume De Saint Martin committed Jan 26, 2020
1 parent 3ac6804 commit e90cc04
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 9 deletions.
13 changes: 10 additions & 3 deletions octobot_trading/data/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from asyncio import Lock

import math

from octobot_backtesting.api.backtesting import get_backtesting_current_time
from octobot_commons.dict_util import get_value_or_default

from octobot_commons.logging.logging_util import get_logger
Expand Down Expand Up @@ -91,7 +93,12 @@ def update(self, symbol, order_id="", status=OrderStatus.OPEN,
self.timestamp = timestamp
if not self.timestamp:
if not timestamp:
self.creation_time = time.time()
if self.exchange_manager.is_backtesting:
# in backtesting, set creation time at current backtesting progress time
# using exchange simulator's backtesting
self.creation_time = get_backtesting_current_time(self.exchange_manager.exchange.backtesting)
else:
self.creation_time = time.time()
else:
# if we have a timestamp, it's a real trader => need to format timestamp if necessary
self.creation_time = self.exchange_manager.exchange.get_uniform_timestamp(timestamp)
Expand Down Expand Up @@ -251,10 +258,10 @@ async def default_exchange_update_order_status(self):
await self.cancel_from_exchange()

def generate_executed_time(self, simulated_time=False):
if not simulated_time or not self.last_prices: # TODO
if not simulated_time: # TODO
return time.time()
else:
return self.last_prices[-1][ECOC.TIMESTAMP.value]
return get_backtesting_current_time(self.exchange_manager.exchange.backtesting)

def is_self_managed(self):
# stop losses and take profits are self managed by the bot
Expand Down
1 change: 1 addition & 0 deletions octobot_trading/exchanges/exchange_simulator.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ cdef class ExchangeSimulator(AbstractExchange):
cpdef get_uniform_timestamp(self, float timestamp)
cpdef dict get_fees(self, str symbol=*)
cpdef dict get_trade_fee(self, str symbol, object order_type, float quantity, float price, str taker_or_maker=*)
cpdef tuple get_split_pair_from_exchange(self, str pair)
9 changes: 8 additions & 1 deletion octobot_trading/exchanges/exchange_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from octobot_trading.enums import ExchangeConstantsMarketStatusColumns, ExchangeConstantsMarketPropertyColumns, \
TraderOrderType, FeePropertyColumns
from octobot_trading.exchanges.abstract_exchange import AbstractExchange
from octobot_trading.producers import MarkPriceUpdater
from octobot_trading.producers.simulator import UNAUTHENTICATED_UPDATER_SIMULATOR_PRODUCERS


Expand Down Expand Up @@ -68,7 +69,10 @@ async def modify_channels(self):
async def create_backtesting_exchange_producers(self):
for importer in self.exchange_importers:
for updater in UNAUTHENTICATED_UPDATER_SIMULATOR_PRODUCERS:
await updater(get_trading_chan(updater.CHANNEL_NAME, self.exchange_manager.id), importer).run()
if updater == MarkPriceUpdater:
await updater(get_trading_chan(updater.CHANNEL_NAME, self.exchange_manager.id)).run()
else:
await updater(get_trading_chan(updater.CHANNEL_NAME, self.exchange_manager.id), importer).run()

async def stop(self):
pass # TODO
Expand Down Expand Up @@ -158,3 +162,6 @@ def get_trade_fee(self, symbol, order_type, quantity, price,

def is_authenticated(self) -> bool:
return False

def get_split_pair_from_exchange(self, pair) -> (str, str):
return split_symbol(pair)
3 changes: 2 additions & 1 deletion octobot_trading/producers/simulator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
from octobot_trading.producers import MarkPriceUpdater
from octobot_trading.producers.balance_updater import BalanceProfitabilityUpdater
from octobot_trading.producers.simulator.positions_updater_simulator import PositionsUpdaterSimulator
from octobot_trading.producers.simulator.orders_updater_simulator import CloseOrdersUpdaterSimulator, \
Expand All @@ -25,7 +26,7 @@

UNAUTHENTICATED_UPDATER_SIMULATOR_PRODUCERS = [OHLCVUpdaterSimulator, OrderBookUpdaterSimulator,
RecentTradeUpdaterSimulator, TickerUpdaterSimulator,
KlineUpdaterSimulator]
KlineUpdaterSimulator, MarkPriceUpdater]

# TODO PositionsUpdaterSimulator
AUTHENTICATED_UPDATER_SIMULATOR_PRODUCERS = [CloseOrdersUpdaterSimulator, OpenOrdersUpdaterSimulator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ async def start(self):

async def handle_recent_trade(self, exchange: str, exchange_id: str, symbol: str, recent_trades: list):
try:
failed_order_updates = await self.__update_orders_status(symbol=symbol, last_prices=recent_trades)
failed_order_updates = await self.__update_orders_status(symbol=symbol,
last_prices=recent_trades,
simulated_time=True)

if failed_order_updates:
self.logger.info(f"Forcing real trader refresh.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@

from octobot_backtesting.data import DataBaseNotExists
from octobot_channels.channels.channel import get_chan
from octobot_trading.channels.exchange_channel import get_chan as get_exchange_chan

from octobot_commons.channels_name import OctoBotBacktestingChannelsName
from octobot_commons.enums import PriceIndexes
from octobot_trading.constants import OHLCV_CHANNEL
from octobot_trading.enums import ExchangeConstantsOrderColumns
from octobot_trading.producers.recent_trade_updater import RecentTradeUpdater


class RecentTradeUpdaterSimulator(RecentTradeUpdater):
SIMULATED_RECENT_TRADE_LIMIT = 2

def __init__(self, channel, importer):
super().__init__(channel)
self.exchange_data_importer = importer
Expand All @@ -46,12 +52,31 @@ async def handle_timestamp(self, timestamp, **kwargs):
self.last_timestamp_pushed = recent_trades_data[0]
await self.push(pair, recent_trades_data[-1])
except DataBaseNotExists as e:
self.logger.warning(f"Not enough data : {e}")
self.logger.warning(f"Not enough data : {e} will use ohlcv data to simulate recent trades.")
await self.pause()
await self.stop()
await get_exchange_chan(OHLCV_CHANNEL, self.channel.exchange_manager.id) \
.new_consumer(self.recent_trades_from_ohlcv_callback)
except IndexError as e:
self.logger.warning(f"Failed to access recent_trades_data : {e}")

async def recent_trades_from_ohlcv_callback(self, exchange: str, exchange_id: str, symbol: str, time_frame, candle):
if candle:
last_candle_open_price = candle[PriceIndexes.IND_PRICE_OPEN.value]
last_candle_timestamp = candle[PriceIndexes.IND_PRICE_TIME.value]
recent_trades = [self._generate_recent_trade(last_candle_timestamp, last_candle_open_price)] \
* self.SIMULATED_RECENT_TRADE_LIMIT
if last_candle_timestamp > self.last_timestamp_pushed:
self.last_timestamp_pushed = last_candle_timestamp
await self.push(symbol, recent_trades, partial=True)

@staticmethod
def _generate_recent_trade(timestamp, price):
return {
ExchangeConstantsOrderColumns.TIMESTAMP.value: timestamp,
ExchangeConstantsOrderColumns.PRICE.value: price
}

async def pause(self):
if self.time_consumer is not None:
await get_chan(OctoBotBacktestingChannelsName.TIME_CHANNEL.value).remove_consumer(self.time_consumer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import json

from octobot_backtesting.data import DataBaseNotExists
from octobot_channels.channels.channel import get_chan

Expand Down

0 comments on commit e90cc04

Please sign in to comment.