Ejemplo n.º 1
0
class BacktestBroker(Broker):
    def __init__(self, hub: Hub) -> None:
        self.hub = hub
        self.publisher = Publisher(hub, prefix='broker')
        self.subscriber = Subscriber(hub, 'broker')
        self.subscriber.add_sync_listener(events.AnyNewTrade,
                                          self.log_trade_timestamp)
        self.orders = []
        self.positions = []
        self._timestamp = None

    def submit_order(self, order: Order) -> None:
        order.timestamp = self.get_timestamp()
        exchange = order.exchange
        self.publisher.publish([exchange.name, 'new-order'], order)
        self.orders.append(order)
        position = self.make_position(order)
        self.positions.append(position)
        position.open(order.price)

    def make_position(self, order: Order) -> Position:
        position = Position(order.market.quote,
                            order.amount,
                            timestamp=order.timestamp)
        self.publisher.publish([str(order.market), 'new-position'], position)
        return position

    def get_timestamp(self) -> datetime:
        if self._timestamp is not None:
            return self._timestamp
        else:
            return utc_now()

    def log_trade_timestamp(self, key: Key, trade: Trade) -> None:
        self._timestamp = trade.timestamp
Ejemplo n.º 2
0
class MultiStrategy:
    """
    MutliStrategy is a base class for strategies that need to take in
    information from multiple markets.
    """
    pairs = []

    def __init__(self, hub: Hub, broker: Broker) -> None:
        self.subscriber = Subscriber(hub, 'multi_strategy')
        for pair in self.pairs:
            namespace = f"{pair[0]}-{pair[1]}"
            bar_key = Key('*', namespace, 'new-bar')
            self.subscriber.add_sync_listener(bar_key, self.store_new_bar)
            self.subscriber.add_sync_listener(bar_key, self.on_new_bar)
        self.broker = broker
        self.bars = {}

    def store_new_bar(self, key: Key, bar: Bar) -> None:
        namespace = key[1]
        if namespace not in self.bars:
            self.bars[namespace] = []
        self.bars[namespace].append(bar)

    def on_new_bar(self, key: Key, bar: Bar) -> None:
        pass
Ejemplo n.º 3
0
class Strategy:
    """
    MutliStrategy is a base class for strategies that need to operate on a
    single market.
    """
    exchange = None
    market = None

    def __init__(self, hub: Hub, broker: Broker) -> None:
        self.subscriber = Subscriber(hub, 'strategy')
        bar_key = Key('*', f"{self.exchange}-{self.market}", 'new-bar')
        self.subscriber.add_sync_listener(bar_key, self.store_new_bar)
        self.subscriber.add_sync_listener(bar_key, self.on_new_bar)
        self.position_manager = PositionManager(hub, self.market)
        self.broker = broker
        self.bars = []

    def __call__(self, bar: Bar):
        pass

    def store_new_bar(self, key: Key, bar: Bar) -> None:
        self.bars.append(bar)

    def on_new_bar(self, key: Key, bar: Bar) -> None:
        self.__call__(bar)
Ejemplo n.º 4
0
class ThresholdBarGenerator:
    """
    ThresholdBarGenerator samples price statistics and generates a new bar
    when predefined threshold of given statistics has been reached.
    """
    def __init__(self, hub: Hub, threshold: int):
        self.subscriber = Subscriber(hub, 'threshold_bars')
        self.subscriber.add_sync_listener(events.AnyNewTrade,
                                          self.on_new_trade)
        self.publisher = Publisher(hub, prefix='threshold_bars')
        self.threshold = threshold
        self.bar = None
        self.value = 0

    def on_new_trade(self, key: Key, trade: Trade) -> None:
        if not self.bar:
            self.bar = Bar(trade)
        self.bar.append(trade)
        if self.value >= self.threshold:
            namespace = key[1]
            self._build_new_bar(trade, namespace)
        self.value += self.metric(trade)

    def _build_new_bar(self, trade: Trade, namespace: str) -> Bar:
        self.value = 0
        key = [namespace, 'new-bar']
        self.publisher.publish(key, self.bar)
        self.bar = Bar(trade)
        return self.bar
Ejemplo n.º 5
0
 def __init__(self, hub: Hub, broker: Broker) -> None:
     self.subscriber = Subscriber(hub, 'strategy')
     bar_key = Key('*', f"{self.exchange}-{self.market}", 'new-bar')
     self.subscriber.add_sync_listener(bar_key, self.store_new_bar)
     self.subscriber.add_sync_listener(bar_key, self.on_new_bar)
     self.position_manager = PositionManager(hub, self.market)
     self.broker = broker
     self.bars = []
Ejemplo n.º 6
0
 def __init__(self, hub: Hub, market: Market):
     self.subscriber = Subscriber(hub, 'PositionManager')
     new_position = Key('*', '*', 'new-position')
     self.subscriber.add_sync_listener(new_position, self.on_new_position)
     self.market = market
     self.position = None
     self.positions = []
     self.closed_positions = []
Ejemplo n.º 7
0
 def __init__(self, hub: Hub, threshold: int):
     self.subscriber = Subscriber(hub, 'threshold_bars')
     self.subscriber.add_sync_listener(events.AnyNewTrade,
                                       self.on_new_trade)
     self.publisher = Publisher(hub, prefix='threshold_bars')
     self.threshold = threshold
     self.bar = None
     self.value = 0
Ejemplo n.º 8
0
 def __init__(self, hub: Hub, broker: Broker) -> None:
     self.subscriber = Subscriber(hub, 'multi_strategy')
     for pair in self.pairs:
         namespace = f"{pair[0]}-{pair[1]}"
         bar_key = Key('*', namespace, 'new-bar')
         self.subscriber.add_sync_listener(bar_key, self.store_new_bar)
         self.subscriber.add_sync_listener(bar_key, self.on_new_bar)
     self.broker = broker
     self.bars = {}
Ejemplo n.º 9
0
 def __init__(self, hub: Hub) -> None:
     self.hub = hub
     self.publisher = Publisher(hub, prefix='broker')
     self.subscriber = Subscriber(hub, 'broker')
     self.subscriber.add_sync_listener(events.AnyNewTrade,
                                       self.log_trade_timestamp)
     self.orders = []
     self.positions = []
     self._timestamp = None
Ejemplo n.º 10
0
 def __init__(self,
              hub: Hub,
              filename: str,
              dump_wait: bool = True) -> None:
     self.filename = filename
     self.subscriber = Subscriber(hub, 'csv_bar_writer')
     self.subscriber.add_sync_listener(events.AnyNewBar, self.on_new_bar)
     self.subscriber.add_sync_listener(events.AnyProcessingFinished,
                                       self.on_processing_finished)
     self.bars = []
     self.dump_wait = dump_wait
Ejemplo n.º 11
0
 def __init__(self, hub: Hub, initial_theta: int = 50) -> None:
     self.subscriber = Subscriber(hub, 'tick_imbalance_bar_generator')
     self.subscriber.add_sync_listener(events.AnyNewTrade,
                                       self.on_new_trade)
     self.publisher = Publisher(hub, prefix='tick_imbalance_bar_generator')
     self.bar = None
     self.last_trade = None
     self.b_ts = []
     self.t_vals = []
     self.theta = 0
     self.expected_theta = initial_theta
     self.initial_T = 50
Ejemplo n.º 12
0
class TickImbalanceBarGenerator(ImbalanceBarGenerator):
    """
    Tick imbalance bars (TIBs) are produced more frequently when there is
    informed trading (asymmetric information).

    Attributes:
    last_trade (Trade): the last observed trade
    b_ts (List[int]): the sign of the price change between ticks
    theta (int): current tick imbalance
    expected_theta (int): expected tick imbalance
    """
    def __init__(self, hub: Hub, initial_theta: int = 50) -> None:
        self.subscriber = Subscriber(hub, 'tick_imbalance_bar_generator')
        self.subscriber.add_sync_listener(events.AnyNewTrade,
                                          self.on_new_trade)
        self.publisher = Publisher(hub, prefix='tick_imbalance_bar_generator')
        self.bar = None
        self.last_trade = None
        self.b_ts = []
        self.t_vals = []
        self.theta = 0
        self.expected_theta = initial_theta
        self.initial_T = 50

    def price_change(self, trade: Trade) -> int:
        if self.last_trade:
            return np.sign(trade.price - self.last_trade.price)
        else:
            return 0

    def on_new_trade(self, key: Key, trade: Trade) -> None:
        if not self.bar:
            self.bar = Bar(trade)
        b_t = self.price_change(trade)
        self.b_ts.append(b_t)
        self.theta += b_t
        self.bar.append(trade)
        self.last_trade = trade
        if np.abs(self.theta) >= np.abs(self.expected_theta):
            self.t_vals.append(self.bar.count)
            window = 5
            if len(self.t_vals) > window:
                expected_T = ema(self.t_vals, window)[-1]
            else:
                expected_T = self.t_vals[-1]
            namespace = key[1]
            self._build_new_bar(trade, namespace)
            self.theta = 0
            logger.info(f"Expected T: {expected_T}")
            # print(ema(self.b_ts, 10))
            probability = ema(self.b_ts, 10)[-1]
            self.expected_theta = expected_T * (2 * probability - 1)
            logger.info(self.expected_theta)
Ejemplo n.º 13
0
def test_tick_bar_generation_after_threshold():
    hub = Hub()
    publisher = Publisher(hub, 'test-publisher')
    generator = TickBarGenerator(hub, threshold=3)
    subscriber = Subscriber(hub, 'test-subscriber')
    bars = []
    def new_bar_handler(key, bar):
        bars.append(bar)
    subscriber.add_sync_listener(events.AnyNewBar, new_bar_handler)
    assert len(bars) == 0
    for i in range(5):
        trade = Trade(12345, price=1.0, amount=1.0)
        publisher.publish(['test-BTCUSD', 'new-trade'], trade)
    assert len(bars) == 1
Ejemplo n.º 14
0
class PositionManager:
    """
    PositionManager handles opening, closing and book-keeping of positions
    on the market.
    """

    def __init__(self, hub: Hub, market: Market):
        self.subscriber = Subscriber(hub, 'PositionManager')
        new_position = Key('*', '*', 'new-position')
        self.subscriber.add_sync_listener(new_position, self.on_new_position)
        self.market = market
        self.position = None
        self.positions = []
        self.closed_positions = []

    @property
    def is_open(self) -> bool:
        """
        Is any position open?
        """
        return bool(self.position)

    def on_new_position(self, key: Key, position: Position) -> None:
        self.positions.append(position)
        self.position = position

    def close_position(self, price: float) -> None:
        if not self.position:
            raise Exception("Cannot close position when no position open!")
        self.position.close(price)
        self.closed_positions.append(self.position)
        self.position = None

    def total_realized_pnl(self) -> float:
        """
        Returns realized PnL of all past (closed) positions.
        """
        return np.sum([pos.realized_pnl for pos in self.closed_positions])
Ejemplo n.º 15
0
class CSVBarWriter:
    def __init__(self,
                 hub: Hub,
                 filename: str,
                 dump_wait: bool = True) -> None:
        self.filename = filename
        self.subscriber = Subscriber(hub, 'csv_bar_writer')
        self.subscriber.add_sync_listener(events.AnyNewBar, self.on_new_bar)
        self.subscriber.add_sync_listener(events.AnyProcessingFinished,
                                          self.on_processing_finished)
        self.bars = []
        self.dump_wait = dump_wait

    def on_new_bar(self, key: Key, bar: Bar) -> None:
        self.bars.append(bar.to_dict())
        if not self.dump_wait:
            self.on_processing_finished(None, None)

    def on_processing_finished(self, key: Key, _: None) -> None:
        logger.info('Saving CSV file with bars')
        df = pd.DataFrame(self.bars)
        logger.info(df.head())
        df.to_csv(self.filename)
Ejemplo n.º 16
0
def subscriber(hub):
    return Subscriber(hub, 'test-subscriber')