Esempio n. 1
0
    def __init__(
        self,
        symbol: Symbol,
        bar_spec: BarSpecification,
        fast_ema: int = 10,
        slow_ema: int = 20,
        extra_id_tag: str = '',
    ):
        """
        Initialize a new instance of the EMACross class.

        Parameters
        ----------
        symbol : Symbol
            The symbol for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        fast_ema : int
            The fast EMA period.
        slow_ema : int
            The slow EMA period.
        extra_id_tag : str, optional
            An additional order identifier tag.

        """
        super().__init__(order_id_tag=symbol.code.replace('/', '') +
                         extra_id_tag)

        # Custom strategy variables
        self.symbol = symbol
        self.bar_type = BarType(symbol, bar_spec)

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema)
        self.slow_ema = ExponentialMovingAverage(slow_ema)
    def __init__(
            self,
            symbol: Symbol,
            bar_spec: BarSpecification,
            trade_size: Decimal,
            fast_ema_period: int,
            slow_ema_period: int,
            atr_period: int,
            trail_atr_multiple: float,
            order_id_tag: str,  # Must be unique at 'trader level'
    ):
        """
        Initialize a new instance of the `EMACrossStopEntryWithTrailingStop` class.

        Parameters
        ----------
        symbol : Symbol
            The symbol for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema_period : int
            The period for the fast EMA indicator.
        slow_ema_period : int
            The period for the slow EMA indicator.
        atr_period : int
            The period for the ATR indicator.
        trail_atr_multiple : float
            The ATR multiple for the trailing stop.
        order_id_tag : str
            The unique order identifier tag for the strategy. Must be unique
            amongst all running strategies for a particular trader identifier.

        """
        super().__init__(order_id_tag=order_id_tag)

        # Custom strategy variables
        self.symbol = symbol
        self.bar_type = BarType(symbol, bar_spec)
        self.trade_size = trade_size
        self.trail_atr_multiple = trail_atr_multiple
        self.instrument = None  # Initialize in on_start
        self.tick_size = None  # Initialize in on_start
        self.price_precision = None  # Initialize in on_start

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(slow_ema_period)
        self.atr = AverageTrueRange(atr_period)

        # Users order management variables
        self.entry = None
        self.trailing_stop = None
Esempio n. 3
0
    def test_handle_bar_updates_indicator(self):
        # Arrange
        indicator = ExponentialMovingAverage(10)

        bar = TestStubs.bar_5decimal()

        # Act
        indicator.handle_bar(bar)

        # Assert
        self.assertTrue(indicator.has_inputs)
        self.assertEqual(1.00003, indicator.value)
Esempio n. 4
0
    def test_handle_trade_tick_updates_indicator(self):
        # Arrange
        indicator = ExponentialMovingAverage(10)

        tick = TestStubs.trade_tick_5decimal(AUDUSD_SIM.symbol)

        # Act
        indicator.handle_trade_tick(tick)

        # Assert
        self.assertTrue(indicator.has_inputs)
        self.assertEqual(1.00001, indicator.value)
Esempio n. 5
0
    def test_handle_quote_tick_updates_indicator(self):
        # Arrange
        indicator = ExponentialMovingAverage(10, PriceType.MID)

        tick = TestStubs.quote_tick_5decimal(AUDUSD_SIM.id)

        # Act
        indicator.handle_quote_tick(tick)

        # Assert
        self.assertTrue(indicator.has_inputs)
        self.assertEqual(1.00002, indicator.value)
Esempio n. 6
0
    def __init__(self, bar_type: BarType):
        super().__init__()

        self.object_storer = ObjectStorer()
        self.bar_type = bar_type

        self.ema1 = ExponentialMovingAverage(10)
        self.ema2 = ExponentialMovingAverage(20)

        self.position_id: Optional[PositionId] = None

        self.calls: List[str] = []
Esempio n. 7
0
    def test_handle_bar_updates_indicator(self):
        # Arrange
        indicator = ExponentialMovingAverage(10)

        bar = TestDataStubs.bar_5decimal()

        # Act
        indicator.handle_bar(bar)

        # Assert
        assert indicator.has_inputs
        assert indicator.value == 1.00003
Esempio n. 8
0
    def test_handle_trade_tick_updates_indicator(self):
        # Arrange
        indicator = ExponentialMovingAverage(10)

        tick = TestDataStubs.trade_tick_5decimal(AUDUSD_SIM.id)

        # Act
        indicator.handle_trade_tick(tick)

        # Assert
        assert indicator.has_inputs
        assert indicator.value == 1.00001
    def __init__(self, config: EMACrossConfig):
        super().__init__(config)

        # Configuration
        self.instrument_id = InstrumentId.from_str(config.instrument_id)
        self.bar_type = BarType.from_str(config.bar_type)
        self.trade_size = Decimal(config.trade_size)

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)

        self.instrument: Optional[Instrument] = None  # Initialized in on_start
Esempio n. 10
0
    def __init__(
        self,
        symbol: Symbol,
        bar_spec: BarSpecification,
        trade_size: Decimal,
        fast_ema_period: int=10,
        slow_ema_period: int=20,
        atr_period: int=20,
        trail_atr_multiple: float=2.0,
    ):
        """
        Initialize a new instance of the `EMACrossWithTrailingStop` class.

        Parameters
        ----------
        symbol : Symbol
            The symbol for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema_period : int
            The period for the fast EMA indicator.
        slow_ema_period : int
            The period for the slow EMA indicator.
        atr_period : int
            The period for the ATR indicator.
        trail_atr_multiple : float
            The ATR multiple for the trailing stop.

        """
        # The order_id_tag should be unique at the 'trader level', here we are
        # just using the traded instruments symbol as the strategy order id tag.
        super().__init__(order_id_tag=symbol.code.replace('/', ""))

        # Custom strategy variables
        self.symbol = symbol
        self.bar_type = BarType(symbol, bar_spec)
        self.trade_size = trade_size
        self.trail_atr_multiple = trail_atr_multiple
        self.instrument = None  # Initialize in on_start
        self.tick_size = None   # Initialize in on_start

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(slow_ema_period)
        self.atr = AverageTrueRange(atr_period)

        # Users order management variables
        self.entry = None
        self.trailing_stop = None
Esempio n. 11
0
    def __init__(self, bar_type, id_tag_strategy="001"):
        """Initialize a new instance of the TestStrategy1 class."""
        super().__init__(order_id_tag=id_tag_strategy)

        self.object_storer = ObjectStorer()
        self.bar_type = bar_type

        self.ema1 = ExponentialMovingAverage(10)
        self.ema2 = ExponentialMovingAverage(20)

        self.register_indicator_for_bars(self.bar_type, self.ema1)
        self.register_indicator_for_bars(self.bar_type, self.ema2)

        self.position_id = None
Esempio n. 12
0
    def __init__(self, bar_type, id_tag_strategy='001', clock=TestClock()):
        """
        Initializes a new instance of the TestStrategy1 class.
        """
        super().__init__(order_id_tag=id_tag_strategy, clock=clock)

        self.object_storer = ObjectStorer()
        self.bar_type = bar_type

        self.ema1 = ExponentialMovingAverage(10)
        self.ema2 = ExponentialMovingAverage(20)

        self.register_indicator(self.bar_type, self.ema1, self.ema1.update)
        self.register_indicator(self.bar_type, self.ema2, self.ema2.update)

        self.position_id = None
Esempio n. 13
0
    def __init__(
        self,
        instrument_id: InstrumentId,
        bar_spec: BarSpecification,
        trade_size: Decimal,
        fast_ema: int = 10,
        slow_ema: int = 20,
        extra_id_tag: str = "",
    ):
        """
        Initialize a new instance of the ``EMACross`` class.

        Parameters
        ----------
        instrument_id : InstrumentId
            The instrument identifier for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema : int
            The fast EMA period.
        slow_ema : int
            The slow EMA period.
        extra_id_tag : str
            An additional order identifier tag.

        """
        if extra_id_tag is None:
            extra_id_tag = ""
        super().__init__(
            order_id_tag=instrument_id.symbol.value.replace("/", "") +
            extra_id_tag)

        # Custom strategy variables
        self.instrument_id = instrument_id
        self.instrument = None
        self.bar_type = BarType(instrument_id, bar_spec)
        self.trade_size = trade_size

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema)
        self.slow_ema = ExponentialMovingAverage(slow_ema)
Esempio n. 14
0
    def __init__(self, config: EMACrossWithTrailingStopConfig):
        super().__init__(config)

        # Configuration
        self.instrument_id = InstrumentId.from_str(config.instrument_id)
        self.bar_type = BarType.from_str(config.bar_type)
        self.trade_size = Decimal(config.trade_size)
        self.trail_atr_multiple = config.trail_atr_multiple

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)
        self.atr = AverageTrueRange(config.atr_period)

        self.instrument: Optional[Instrument] = None  # Initialized in on_start
        self.tick_size = None  # Initialized in on_start

        # Users order management variables
        self.entry = None
        self.trailing_stop = None
Esempio n. 15
0
    def __init__(self, bar_type: BarType):
        """
        Initialize a new instance of the `MockStrategy` class.

        Parameters
        ----------
        bar_type : BarType
            The bar type for the strategy.

        """
        super().__init__(order_id_tag="001")

        self.object_storer = ObjectStorer()
        self.bar_type = bar_type

        self.ema1 = ExponentialMovingAverage(10)
        self.ema2 = ExponentialMovingAverage(20)

        self.position_id = None

        self.calls = []
Esempio n. 16
0
    def __init__(
        self,
        instrument_id: InstrumentId,
        bar_spec: BarSpecification,
        trade_size: Decimal,
        fast_ema_period: int,
        slow_ema_period: int,
        order_id_tag: str,  # Must be unique at 'trader level'
    ):
        """
        Initialize a new instance of the ``EMACross`` class.

        Parameters
        ----------
        instrument_id : InstrumentId
            The instrument identifier for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema_period : int
            The period for the fast EMA.
        slow_ema_period : int
            The period for the slow EMA.
        order_id_tag : str
            The unique order identifier tag for the strategy. Must be unique
            amongst all running strategies for a particular trader identifier.

        """
        super().__init__(order_id_tag=order_id_tag)

        # Custom strategy variables
        self.instrument_id = instrument_id
        self.instrument = None  # Initialize in on_start
        self.bar_type = BarType(instrument_id, bar_spec)
        self.trade_size = trade_size

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(slow_ema_period)
Esempio n. 17
0
    def __init__(self,
                 symbol: Symbol,
                 bar_spec: BarSpecification,
                 risk_bp: float = 10.0,
                 fast_ema: int = 10,
                 slow_ema: int = 20,
                 atr_period: int = 20,
                 sl_atr_multiple: float = 2.0,
                 extra_id_tag: str = ''):
        """
        Initializes a new instance of the EMACrossPy class.

        :param symbol: The symbol for the strategy.
        :param bar_spec: The bar specification for the strategy.
        :param risk_bp: The risk per trade (basis points).
        :param fast_ema: The fast EMA period.
        :param slow_ema: The slow EMA period.
        :param atr_period: The ATR period.
        :param sl_atr_multiple: The ATR multiple for stop-loss prices.
        :param extra_id_tag: An optional extra tag to append to order ids.
        """
        super().__init__(order_id_tag=symbol.code + extra_id_tag,
                         bar_capacity=40)

        # Custom strategy variables
        self.symbol = symbol
        self.bar_type = BarType(symbol, bar_spec)
        self.precision = 5  # dummy initial value for FX
        self.risk_bp = risk_bp
        self.entry_buffer = 0.0  # instrument.tick_size
        self.SL_buffer = 0.0  # instrument.tick_size * 10
        self.SL_atr_multiple = sl_atr_multiple

        self.position_sizer = None  # initialized in on_start()
        self.quote_currency = None  # initialized in on_start()

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema)
        self.slow_ema = ExponentialMovingAverage(slow_ema)
        self.atr = AverageTrueRange(atr_period)
    def test_register_indicator_for_trade_ticks_when_already_registered(self):
        # Arrange
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema1 = ExponentialMovingAverage(10)
        ema2 = ExponentialMovingAverage(10)

        # Act
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema1)
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema2)
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema2)

        assert len(strategy.registered_indicators) == 2
        assert ema1 in strategy.registered_indicators
        assert ema2 in strategy.registered_indicators
Esempio n. 19
0
    def __init__(
        self,
        symbol: Symbol,
        bar_spec: BarSpecification,
        trade_size: Decimal,
        fast_ema: int = 10,
        slow_ema: int = 20,
    ):
        """
        Initialize a new instance of the `EMACross` class.

        Parameters
        ----------
        symbol : Symbol
            The symbol for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema : int
            The fast EMA period.
        slow_ema : int
            The slow EMA period.

        """
        # The order_id_tag should be unique at the 'trader level', here we are
        # just using the traded instruments symbol as the strategy order id tag.
        super().__init__(order_id_tag=symbol.code.replace('/', ""))

        # Custom strategy variables
        self.symbol = symbol
        self.bar_type = BarType(symbol, bar_spec)
        self.trade_size = trade_size

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema)
        self.slow_ema = ExponentialMovingAverage(slow_ema)
    def test_register_indicator_for_bars_when_already_registered(self):
        # Arrange
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema1 = ExponentialMovingAverage(10)
        ema2 = ExponentialMovingAverage(10)
        bar_type = TestStubs.bartype_audusd_1min_bid()

        # Act
        strategy.register_indicator_for_bars(bar_type, ema1)
        strategy.register_indicator_for_bars(bar_type, ema2)
        strategy.register_indicator_for_bars(bar_type, ema2)

        assert len(strategy.registered_indicators) == 2
        assert ema1 in strategy.registered_indicators
        assert ema2 in strategy.registered_indicators
    def test_handle_trade_ticks_with_no_ticks_logs_and_continues(self):
        # Arrange
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema = ExponentialMovingAverage(10)
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema)

        # Act
        strategy.handle_trade_ticks([])

        # Assert
        assert ema.count == 0
    def test_handle_bars_with_no_bars_logs_and_continues(self):
        # Arrange
        bar_type = TestStubs.bartype_gbpusd_1sec_mid()
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema = ExponentialMovingAverage(10)
        strategy.register_indicator_for_bars(bar_type, ema)

        # Act
        strategy.handle_bars([])

        # Assert
        assert ema.count == 0
    def test_handle_bars_updates_indicator_registered_for_bars(self):
        # Arrange
        bar_type = TestStubs.bartype_audusd_1min_bid()
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema = ExponentialMovingAverage(10)
        strategy.register_indicator_for_bars(bar_type, ema)
        bar = TestStubs.bar_5decimal()

        # Act
        strategy.handle_bars([bar])

        # Assert
        assert ema.count == 1
    def test_register_indicator_for_multiple_data_sources(self):
        # Arrange
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema = ExponentialMovingAverage(10)
        bar_type = TestStubs.bartype_audusd_1min_bid()

        # Act
        strategy.register_indicator_for_quote_ticks(AUDUSD_SIM.id, ema)
        strategy.register_indicator_for_quote_ticks(GBPUSD_SIM.id, ema)
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema)
        strategy.register_indicator_for_bars(bar_type, ema)

        assert len(strategy.registered_indicators) == 1
        assert ema in strategy.registered_indicators
    def test_handle_trade_ticks_updates_indicator_registered_for_trade_ticks(
            self):
        # Arrange
        strategy = TradingStrategy()
        strategy.register(
            trader_id=self.trader_id,
            portfolio=self.portfolio,
            msgbus=self.msgbus,
            cache=self.cache,
            clock=self.clock,
            logger=self.logger,
        )

        ema = ExponentialMovingAverage(10)
        strategy.register_indicator_for_trade_ticks(AUDUSD_SIM.id, ema)

        tick = TestStubs.trade_tick_5decimal(AUDUSD_SIM.id)

        # Act
        strategy.handle_trade_ticks([tick])

        # Assert
        assert ema.count == 1
Esempio n. 26
0
class EMACrossWithTrailingStop(TradingStrategy):
    """
    A simple moving average cross example strategy with a stop-market entry and
    trailing stop.

    When the fast EMA crosses the slow EMA then submits a stop-market order one
    tick above the current bar for BUY, or one tick below the current bar
    for SELL.

    If the entry order is filled then a trailing stop at a specified ATR
    distance is submitted and managed.

    Cancels all orders and flattens all positions on stop.

    Parameters
    ----------
    config : EMACrossWithTrailingStopConfig
        The configuration for the instance.
    """
    def __init__(self, config: EMACrossWithTrailingStopConfig):
        super().__init__(config)

        # Configuration
        self.instrument_id = InstrumentId.from_str(config.instrument_id)
        self.bar_type = BarType.from_str(config.bar_type)
        self.trade_size = Decimal(config.trade_size)
        self.trail_atr_multiple = config.trail_atr_multiple

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)
        self.atr = AverageTrueRange(config.atr_period)

        self.instrument: Optional[Instrument] = None  # Initialized in on_start
        self.tick_size = None  # Initialized in on_start

        # Users order management variables
        self.entry = None
        self.trailing_stop = None

    def on_start(self):
        """Actions to be performed on strategy start."""
        self.instrument = self.cache.instrument(self.instrument_id)
        if self.instrument is None:
            self.log.error(
                f"Could not find instrument for {self.instrument_id}")
            self.stop()
            return

        self.tick_size = self.instrument.price_increment

        # Register the indicators for updating
        self.register_indicator_for_bars(self.bar_type, self.fast_ema)
        self.register_indicator_for_bars(self.bar_type, self.slow_ema)
        self.register_indicator_for_bars(self.bar_type, self.atr)

        # Get historical data
        self.request_bars(self.bar_type)

        # Subscribe to live data
        self.subscribe_bars(self.bar_type)

    def on_instrument(self, instrument: Instrument):
        """
        Actions to be performed when the strategy is running and receives an
        instrument.

        Parameters
        ----------
        instrument : Instrument
            The instrument received.

        """
        pass

    def on_order_book(self, order_book: OrderBook):
        """
        Actions to be performed when the strategy is running and receives an order book.

        Parameters
        ----------
        order_book : OrderBook
            The order book received.

        """
        # self.log.info(f"Received {order_book}")  # For debugging (must add a subscription)

    def on_quote_tick(self, tick: QuoteTick):
        """
        Actions to be performed when the strategy is running and receives a quote tick.

        Parameters
        ----------
        tick : QuoteTick
            The quote tick received.

        """
        pass

    def on_trade_tick(self, tick: TradeTick):
        """
        Actions to be performed when the strategy is running and receives a trade tick.

        Parameters
        ----------
        tick : TradeTick
            The tick received.

        """
        pass

    def on_bar(self, bar: Bar):
        """
        Actions to be performed when the strategy is running and receives a bar.

        Parameters
        ----------
        bar : Bar
            The bar received.

        """
        self.log.info(f"Received {repr(bar)}")

        # Check if indicators ready
        if not self.indicators_initialized():
            self.log.info(
                f"Waiting for indicators to warm up "
                f"[{self.cache.bar_count(self.bar_type)}]...",
                color=LogColor.BLUE,
            )
            return  # Wait for indicators to warm up...

        if self.portfolio.is_flat(self.instrument_id):
            if self.fast_ema.value >= self.slow_ema.value:
                self.entry_buy()
                self.trailing_stop_sell(bar)
            else:  # fast_ema.value < self.slow_ema.value
                self.entry_sell()
                self.trailing_stop_buy(bar)
        else:
            self.manage_trailing_stop(bar)

    def entry_buy(self):
        """
        Users simple buy entry method (example).
        """
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=self.instrument.make_qty(self.trade_size),
        )

        self.submit_order(order)

    def entry_sell(self):
        """
        Users simple sell entry method (example).
        """
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=self.instrument.make_qty(self.trade_size),
        )

        self.submit_order(order)

    def trailing_stop_buy(self, last_bar: Bar):
        """
        Users simple trailing stop BUY for (``SHORT`` positions).

        Parameters
        ----------
        last_bar : Bar
            The last bar received.

        """
        price: Decimal = last_bar.high + (self.atr.value *
                                          self.trail_atr_multiple)
        order: StopMarketOrder = self.order_factory.stop_market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=self.instrument.make_qty(self.trade_size),
            price=self.instrument.make_price(price),
            reduce_only=True,
        )

        self.trailing_stop = order
        self.submit_order(order)

    def trailing_stop_sell(self, last_bar: Bar):
        """
        Users simple trailing stop SELL for (LONG positions).
        """
        price: Decimal = last_bar.low - (self.atr.value *
                                         self.trail_atr_multiple)
        order: StopMarketOrder = self.order_factory.stop_market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=self.instrument.make_qty(self.trade_size),
            price=self.instrument.make_price(price),
            reduce_only=True,
        )

        self.trailing_stop = order
        self.submit_order(order)

    def manage_trailing_stop(self, last_bar: Bar):
        """
        Users simple trailing stop management method (example).

        Parameters
        ----------
        last_bar : Bar
            The last bar received.

        """
        if not self.trailing_stop:
            self.log.error("Trailing Stop order was None!")
            self.flatten_all_positions(self.instrument_id)
            return

        if self.trailing_stop.is_sell:
            new_trailing_price = last_bar.low - (self.atr.value *
                                                 self.trail_atr_multiple)
            if new_trailing_price > self.trailing_stop.price:
                self.cancel_order(self.trailing_stop)
                self.trailing_stop_sell(last_bar)
        else:  # trailing_stop.is_buy
            new_trailing_price = last_bar.high + (self.atr.value *
                                                  self.trail_atr_multiple)
            if new_trailing_price < self.trailing_stop.price:
                self.cancel_order(self.trailing_stop)
                self.trailing_stop_buy(last_bar)

    def on_data(self, data: Data):
        """
        Actions to be performed when the strategy is running and receives generic data.

        Parameters
        ----------
        data : Data
            The data received.

        """
        pass

    def on_event(self, event: Event):
        """
        Actions to be performed when the strategy is running and receives an event.

        Parameters
        ----------
        event : Event
            The event received.

        """
        if isinstance(event, OrderFilled) and self.trailing_stop:
            if event.client_order_id == self.trailing_stop.client_order_id:
                last_bar = self.cache.bar(self.bar_type)
                if event.order_side == OrderSide.BUY:
                    self.trailing_stop_sell(last_bar)
                elif event.order_side == OrderSide.SELL:
                    self.trailing_stop_buy(last_bar)
            elif event.client_order_id == self.trailing_stop.client_order_id:
                self.trailing_stop = None

    def on_stop(self):
        """
        Actions to be performed when the strategy is stopped.
        """
        self.cancel_all_orders(self.instrument_id)
        self.flatten_all_positions(self.instrument_id)

        # Unsubscribe from data
        self.unsubscribe_bars(self.bar_type)

    def on_reset(self):
        """
        Actions to be performed when the strategy is reset.
        """
        # Reset indicators here
        self.fast_ema.reset()
        self.slow_ema.reset()
        self.atr.reset()

    def on_save(self) -> Dict[str, bytes]:
        """
        Actions to be performed when the strategy is saved.

        Create and return a state dictionary of values to be saved.

        Returns
        -------
        dict[str, bytes]
            The strategy state dictionary.

        """
        return {}

    def on_load(self, state: Dict[str, bytes]):
        """
        Actions to be performed when the strategy is loaded.

        Saved state values will be contained in the give state dictionary.

        Parameters
        ----------
        state : dict[str, bytes]
            The strategy state dictionary.

        """
        pass

    def on_dispose(self):
        """
        Actions to be performed when the strategy is disposed.

        Cleanup any resources used by the strategy here.

        """
        pass
Esempio n. 27
0
class EMACrossBracket(TradingStrategy):
    """
    A simple moving average cross example strategy.

    When the fast EMA crosses the slow EMA then enter a position in that
    direction.

    Cancels all orders and flattens all positions on stop.

    Parameters
    ----------
    config : EMACrossConfig
        The configuration for the instance.
    """

    def __init__(self, config: EMACrossBracketConfig):
        super().__init__(config)

        # Configuration
        self.instrument_id = InstrumentId.from_str(config.instrument_id)
        self.bar_type = BarType.from_str(config.bar_type)
        self.bracket_distance_atr = config.bracket_distance_atr
        self.trade_size = Decimal(config.trade_size)

        # Create the indicators for the strategy
        self.atr = AverageTrueRange(config.atr_period)
        self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
        self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)

        self.instrument: Optional[Instrument] = None  # Initialized in on_start

    def on_start(self):
        """Actions to be performed on strategy start."""
        self.instrument = self.cache.instrument(self.instrument_id)
        if self.instrument is None:
            self.log.error(f"Could not find instrument for {self.instrument_id}")
            self.stop()
            return

        # Register the indicators for updating
        self.register_indicator_for_bars(self.bar_type, self.atr)
        self.register_indicator_for_bars(self.bar_type, self.fast_ema)
        self.register_indicator_for_bars(self.bar_type, self.slow_ema)

        # Get historical data
        self.request_bars(self.bar_type)

        # Subscribe to live data
        self.subscribe_bars(self.bar_type)

    def on_bar(self, bar: Bar):
        """
        Actions to be performed when the strategy is running and receives a bar.

        Parameters
        ----------
        bar : Bar
            The bar received.

        """
        self.log.info(f"Received {repr(bar)}")

        # Check if indicators ready
        if not self.indicators_initialized():
            self.log.info(
                f"Waiting for indicators to warm up " f"[{self.cache.bar_count(self.bar_type)}]...",
                color=LogColor.BLUE,
            )
            return  # Wait for indicators to warm up...

        # BUY LOGIC
        if self.fast_ema.value >= self.slow_ema.value:
            if self.portfolio.is_flat(self.instrument_id):
                self.buy(bar)
            elif self.portfolio.is_net_short(self.instrument_id):
                self.flatten_all_positions(self.instrument_id)
                self.cancel_all_orders(self.instrument_id)
                self.buy(bar)

        # SELL LOGIC
        elif self.fast_ema.value < self.slow_ema.value:
            if self.portfolio.is_flat(self.instrument_id):
                self.sell(bar)
            elif self.portfolio.is_net_long(self.instrument_id):
                self.flatten_all_positions(self.instrument_id)
                self.cancel_all_orders(self.instrument_id)
                self.sell(bar)

    def buy(self, last_bar: Bar):
        """
        Users bracket buy method (example).
        """
        bracket_distance: float = self.bracket_distance_atr * self.atr.value
        order_list: OrderList = self.order_factory.bracket_market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=self.instrument.make_qty(self.trade_size),
            stop_loss=self.instrument.make_price(last_bar.close - bracket_distance),
            take_profit=self.instrument.make_price(last_bar.close + bracket_distance),
        )

        self.submit_order_list(order_list)

    def sell(self, last_bar: Bar):
        """
        Users bracket sell method (example).
        """
        bracket_distance: float = self.bracket_distance_atr * self.atr.value
        order_list: OrderList = self.order_factory.bracket_market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=self.instrument.make_qty(self.trade_size),
            stop_loss=self.instrument.make_price(last_bar.close + bracket_distance),
            take_profit=self.instrument.make_price(last_bar.close - bracket_distance),
        )

        self.submit_order_list(order_list)

    def on_data(self, data: Data):
        """
        Actions to be performed when the strategy is running and receives generic data.

        Parameters
        ----------
        data : Data
            The data received.

        """
        pass

    def on_event(self, event: Event):
        """
        Actions to be performed when the strategy is running and receives an event.

        Parameters
        ----------
        event : Event
            The event received.

        """
        pass

    def on_stop(self):
        """
        Actions to be performed when the strategy is stopped.
        """
        self.cancel_all_orders(self.instrument_id)
        self.flatten_all_positions(self.instrument_id)

        # Unsubscribe from data
        self.unsubscribe_bars(self.bar_type)

    def on_reset(self):
        """
        Actions to be performed when the strategy is reset.
        """
        # Reset indicators here
        self.fast_ema.reset()
        self.slow_ema.reset()

    def on_save(self) -> Dict[str, bytes]:
        """
        Actions to be performed when the strategy is saved.

        Create and return a state dictionary of values to be saved.

        Returns
        -------
        dict[str, bytes]
            The strategy state dictionary.

        """
        return {}

    def on_load(self, state: Dict[str, bytes]):
        """
        Actions to be performed when the strategy is loaded.

        Saved state values will be contained in the give state dictionary.

        Parameters
        ----------
        state : dict[str, bytes]
            The strategy state dictionary.

        """
        pass

    def on_dispose(self):
        """
        Actions to be performed when the strategy is disposed.

        Cleanup any resources used by the strategy here.

        """
        pass
Esempio n. 28
0
 def setUp(self):
     # Arrange
     self.ema = ExponentialMovingAverage(10)
Esempio n. 29
0
class ExponentialMovingAverageTests(unittest.TestCase):

    # Fixture Setup
    def setUp(self):
        # Arrange
        self.ema = ExponentialMovingAverage(10)

    def test_name_returns_expected_name(self):
        # Act
        # Assert
        self.assertEqual('ExponentialMovingAverage', self.ema.name)

    def test_str_returns_expected_string(self):
        # Act
        # Assert
        self.assertEqual('ExponentialMovingAverage(10)', str(self.ema))

    def test_repr_returns_expected_string(self):
        # Act
        # Assert
        self.assertTrue(
            repr(self.ema).startswith(
                '<ExponentialMovingAverage(10) object at'))
        self.assertTrue(repr(self.ema).endswith('>'))

    def test_period_returns_expected_value(self):
        # Act
        # Assert
        self.assertEqual(10, self.ema.period)

    def test_multiplier_returns_expected_value(self):
        # Act
        # Assert
        self.assertEqual(0.18181818181818182, self.ema.alpha)

    def test_initialized_without_inputs_returns_false(self):
        # Act
        # Assert
        self.assertEqual(False, self.ema.initialized)

    def test_initialized_with_required_inputs_returns_true(self):
        # Arrange
        self.ema.update_raw(1.00000)
        self.ema.update_raw(2.00000)
        self.ema.update_raw(3.00000)
        self.ema.update_raw(4.00000)
        self.ema.update_raw(5.00000)
        self.ema.update_raw(6.00000)
        self.ema.update_raw(7.00000)
        self.ema.update_raw(8.00000)
        self.ema.update_raw(9.00000)
        self.ema.update_raw(10.00000)

        # Act

        # Assert
        self.assertEqual(True, self.ema.initialized)

    def test_value_with_one_input_returns_expected_value(self):
        # Arrange
        self.ema.update_raw(1.00000)

        # Act
        # Assert
        self.assertEqual(1.0, self.ema.value)

    def test_value_with_three_inputs_returns_expected_value(self):
        # Arrange
        self.ema.update_raw(1.00000)
        self.ema.update_raw(2.00000)
        self.ema.update_raw(3.00000)

        # Act
        # Assert
        self.assertEqual(1.5123966942148757, self.ema.value)

    def test_reset_successfully_returns_indicator_to_fresh_state(self):
        # Arrange
        for _i in range(1000):
            self.ema.update_raw(1.00000)

        # Act
        self.ema.reset()

        # Assert
        self.assertEqual(0.0, self.ema.value)  # No assertion errors.

    def test_with_battery_signal(self):
        # Arrange
        battery_signal = BatterySeries.create()
        output = []

        # Act
        for point in battery_signal:
            self.ema.update_raw(point)
            output.append(self.ema.value)

        # Assert
        self.assertEqual(len(battery_signal), len(output))
Esempio n. 30
0
class EMACross(TradingStrategy):
    """
    A simple moving average cross example strategy.

    When the fast EMA crosses the slow EMA then enter a position in that
    direction.
    """
    def __init__(
        self,
        instrument_id: InstrumentId,
        bar_spec: BarSpecification,
        trade_size: Decimal,
        fast_ema: int = 10,
        slow_ema: int = 20,
        extra_id_tag: str = "",
    ):
        """
        Initialize a new instance of the ``EMACross`` class.

        Parameters
        ----------
        instrument_id : InstrumentId
            The instrument identifier for the strategy.
        bar_spec : BarSpecification
            The bar specification for the strategy.
        trade_size : Decimal
            The position size per trade.
        fast_ema : int
            The fast EMA period.
        slow_ema : int
            The slow EMA period.
        extra_id_tag : str
            An additional order identifier tag.

        """
        if extra_id_tag is None:
            extra_id_tag = ""
        super().__init__(
            order_id_tag=instrument_id.symbol.value.replace("/", "") +
            extra_id_tag)

        # Custom strategy variables
        self.instrument_id = instrument_id
        self.instrument = None
        self.bar_type = BarType(instrument_id, bar_spec)
        self.trade_size = trade_size

        # Create the indicators for the strategy
        self.fast_ema = ExponentialMovingAverage(fast_ema)
        self.slow_ema = ExponentialMovingAverage(slow_ema)

    def on_start(self):
        """Actions to be performed on strategy start."""
        self.instrument = self.cache.instrument(self.instrument_id)
        if self.instrument is None:
            self.log.error(
                f"Could not find instrument for {self.instrument_id}")
            self.stop()
            return

        # Register the indicators for updating
        self.register_indicator_for_bars(self.bar_type, self.fast_ema)
        self.register_indicator_for_bars(self.bar_type, self.slow_ema)

        # Get historical data
        self.request_bars(self.bar_type)

        # Subscribe to live data
        self.subscribe_bars(self.bar_type)

    def on_trade_tick(self, tick: TradeTick):
        """
        Actions to be performed when the strategy is running and receives a trade tick.

        Parameters
        ----------
        tick : TradeTick
            The tick received.

        """
        pass

    def on_order_book(self, order_book: OrderBook):
        """
        Actions to be performed when the strategy is running and receives an order book.

        Parameters
        ----------
        order_book : OrderBook
            The order book received.

        """
        pass

    def on_quote_tick(self, tick: QuoteTick):
        """
        Actions to be performed when the strategy is running and receives a quote tick.

        Parameters
        ----------
        tick : QuoteTick
            The quote tick received.

        """
        pass

    def on_bar(self, bar: Bar):
        """
        Actions to be performed when the strategy is running and receives a bar.

        Parameters
        ----------
        bar : Bar
            The bar received.

        """
        self.log.info(f"Received {repr(bar)}")

        # Check if indicators ready
        if not self.indicators_initialized():
            self.log.info(
                f"Waiting for indicators to warm up "
                f"[{self.cache.bar_count(self.bar_type)}]...",
                color=LogColor.BLUE,
            )
            return  # Wait for indicators to warm up...

        # BUY LOGIC
        if self.fast_ema.value >= self.slow_ema.value:
            if self.portfolio.is_flat(self.instrument_id):
                self.buy()
            elif self.portfolio.is_net_short(self.instrument_id):
                self.flatten_all_positions(self.instrument_id)
                self.buy()

        # SELL LOGIC
        elif self.fast_ema.value < self.slow_ema.value:
            if self.portfolio.is_flat(self.instrument_id):
                self.sell()
            elif self.portfolio.is_net_long(self.instrument_id):
                self.flatten_all_positions(self.instrument_id)
                self.sell()

    def buy(self):
        """
        Users simple buy method (example).
        """
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            quantity=self.instrument.make_qty(self.trade_size),
        )

        self.submit_order(order)

    def sell(self):
        """
        Users simple sell method (example).
        """
        order = self.order_factory.market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=self.instrument.make_qty(self.trade_size),
        )

        self.submit_order(order)

    def on_data(self, data):
        """
        Actions to be performed when the strategy is running and receives a data object.

        Parameters
        ----------
        data : object
            The data object received.

        """
        pass

    def on_event(self, event):
        """
        Actions to be performed when the strategy is running and receives an event.

        Parameters
        ----------
        event : Event
            The event received.

        """
        pass

    def on_stop(self):
        """
        Actions to be performed when the strategy is stopped.

        """
        self.cancel_all_orders(self.instrument_id)
        self.flatten_all_positions(self.instrument_id)

    def on_reset(self):
        """
        Actions to be performed when the strategy is reset.

        """
        # Reset indicators here
        self.fast_ema.reset()
        self.slow_ema.reset()

    def on_save(self) -> {}:
        """
        Actions to be performed when the strategy is saved.

        Create and return a state dictionary of values to be saved.

        Returns
        -------
        dict[str, bytes]
            The strategy state dictionary.

        """
        return {"example": b"123456"}

    def on_load(self, state: {}):
        """
        Actions to be performed when the strategy is loaded.

        Saved state values will be contained in the give state dictionary.

        Parameters
        ----------
        state : dict[str, bytes]
            The strategy state dictionary.

        """
        self.log.info(f"Loaded users state {state['example']}")

    def on_dispose(self):
        """
        Actions to be performed when the strategy is disposed.

        Cleanup any resources used by the strategy here.

        """
        self.unsubscribe_bars(self.bar_type)