Skip to content

First Strategy

Write a simple trading strategy that reacts to market data.

Prerequisites

1. Strategy Interface

All strategies implement IStrategy, which combines: - ISubsystem — lifecycle (start(), stop()) - IMarketDataSubscriber — market data callbacks

#include "flox/strategy/abstract_strategy.h"

class MyStrategy : public flox::IStrategy
{
public:
  // Required: unique identifier for event routing
  flox::SubscriberId id() const override {
    return reinterpret_cast<flox::SubscriberId>(this);
  }

  // Lifecycle
  void start() override { /* initialization */ }
  void stop() override { /* cleanup */ }

  // Market data callbacks
  void onTrade(const flox::TradeEvent& ev) override { /* react to trades */ }
  void onBookUpdate(const flox::BookUpdateEvent& ev) override { /* react to book changes */ }
  void onCandle(const flox::CandleEvent& ev) override { /* react to candles */ }
};

2. Simple Example

A strategy that prints trades and tracks best bid/ask:

#include "flox/strategy/abstract_strategy.h"
#include "flox/book/events/trade_event.h"
#include "flox/book/events/book_update_event.h"
#include "flox/log/log.h"

using namespace flox;

class PrintingStrategy : public IStrategy
{
public:
  explicit PrintingStrategy(SymbolId symbol) : _symbol(symbol) {}

  SubscriberId id() const override {
    return reinterpret_cast<SubscriberId>(this);
  }

  void start() override {
    FLOX_LOG("[PrintingStrategy] Started for symbol " << _symbol);
  }

  void stop() override {
    FLOX_LOG("[PrintingStrategy] Stopped. Trades seen: " << _tradeCount);
  }

  void onTrade(const TradeEvent& ev) override {
    // Filter by symbol
    if (ev.trade.symbol != _symbol) return;

    ++_tradeCount;
    FLOX_LOG("Trade: " << ev.trade.price.toDouble()
             << " x " << ev.trade.quantity.toDouble()
             << " (" << (ev.trade.isBuy ? "BUY" : "SELL") << ")");
  }

  void onBookUpdate(const BookUpdateEvent& ev) override {
    if (ev.update.symbol != _symbol) return;

    if (!ev.update.bids.empty()) {
      _bestBid = ev.update.bids[0].price;
    }
    if (!ev.update.asks.empty()) {
      _bestAsk = ev.update.asks[0].price;
    }

    FLOX_LOG("Book: " << _bestBid.toDouble() << " / " << _bestAsk.toDouble());
  }

private:
  SymbolId _symbol;
  uint64_t _tradeCount{0};
  Price _bestBid{};
  Price _bestAsk{};
};

3. Strategy with Order Execution

A strategy that submits orders based on trades:

#include "flox/strategy/abstract_strategy.h"
#include "flox/execution/abstract_order_executor.h"
#include "flox/execution/order.h"

using namespace flox;

class TradingStrategy : public IStrategy
{
public:
  TradingStrategy(SymbolId symbol, IOrderExecutor* executor)
    : _symbol(symbol), _executor(executor) {}

  SubscriberId id() const override {
    return reinterpret_cast<SubscriberId>(this);
  }

  void onTrade(const TradeEvent& ev) override {
    if (ev.trade.symbol != _symbol) return;

    // Simple logic: buy after every 10th trade
    if (++_tradeCount % 10 != 0) return;

    Order order{};
    order.id = _nextOrderId++;
    order.symbol = _symbol;
    order.side = Side::BUY;
    order.price = ev.trade.price - Price::fromDouble(0.01);  // 1 cent below
    order.quantity = Quantity::fromDouble(1.0);
    order.type = OrderType::LIMIT;

    _executor->submitOrder(order);
  }

private:
  SymbolId _symbol;
  IOrderExecutor* _executor;
  uint64_t _tradeCount{0};
  OrderId _nextOrderId{1};
};

4. Wiring the Strategy

Connect your strategy to the engine:

#include "flox/book/bus/trade_bus.h"
#include "flox/book/bus/book_update_bus.h"
#include "flox/engine/engine.h"

// Create buses
auto tradeBus = std::make_unique<TradeBus>();
auto bookBus = std::make_unique<BookUpdateBus>();

// Create strategy
auto strategy = std::make_unique<PrintingStrategy>(/*symbolId=*/0);

// Subscribe to buses
tradeBus->subscribe(strategy.get());
bookBus->subscribe(strategy.get());

// Add to subsystems
std::vector<std::unique_ptr<ISubsystem>> subsystems;
subsystems.push_back(std::move(tradeBus));
subsystems.push_back(std::move(bookBus));
subsystems.push_back(std::move(strategy));

// Create and run engine
EngineConfig config{};
Engine engine(config, std::move(subsystems), std::move(connectors));
engine.start();

5. Best Practices

Do: - Keep callbacks fast and non-blocking - Filter events by symbol early (first line of callback) - Use Price::fromDouble() and Quantity::fromDouble() for conversions - Implement id() to return a unique value

Don't: - Block in callbacks (no I/O, no locks, no allocations) - Store pointers to events (they're recycled) - Throw exceptions from callbacks

Key Types

Type Description
SymbolId uint32_t identifier for an instrument
Price Fixed-point price (use fromDouble(), toDouble())
Quantity Fixed-point quantity
Side BUY or SELL
OrderType LIMIT, MARKET, etc.

Next Steps