Writing Your First Codon Strategy¶
This tutorial walks through creating an SMA crossover strategy in Codon. The strategy buys when a fast SMA crosses above a slow SMA, and sells on the reverse crossover.
Prerequisites¶
- Flox built with C API enabled
- Codon compiler installed
cmake -B build -DFLOX_ENABLE_CAPI=ON -DFLOX_ENABLE_BACKTEST=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
Step 1: Create the Strategy File¶
Create my_sma_strategy.codon:
from flox.strategy import Strategy
from flox.context import SymbolContext
from flox.types import TradeData
from flox.indicators import StreamingSMA
class SmaCrossover(Strategy):
fast_sma: StreamingSMA
slow_sma: StreamingSMA
order_size: float
long_position: bool
def __init__(self, symbols: List[int], fast: int = 10, slow: int = 30):
super().__init__(symbols)
self.fast_sma = StreamingSMA(fast)
self.slow_sma = StreamingSMA(slow)
self.order_size = 1.0
self.long_position = False
def on_trade(self, ctx: SymbolContext, trade: TradeData):
price = trade.price.to_double()
fast = self.fast_sma.update(price)
slow = self.slow_sma.update(price)
# Wait until both SMAs have enough data
if not self.slow_sma.ready:
return
sym = self.primary_symbol
# Golden cross: fast crosses above slow
if fast > slow and not self.long_position:
self.emit_market_buy(sym, self.order_size)
self.long_position = True
# Death cross: fast crosses below slow
elif fast < slow and self.long_position:
self.emit_market_sell(sym, self.order_size)
self.long_position = False
def on_start(self):
print("Strategy started!")
def on_stop(self):
pos = self.position()
print(f"Strategy stopped. Final position: {pos}")
Step 2: Compile to Native Binary¶
Step 3: Understanding the Code¶
Imports¶
Strategy-- base class providing emit/query methodsSymbolContext-- per-symbol state (position, book, prices)TradeData-- trade event with price, quantity, sideStreamingSMA-- O(1) streaming SMA (pure Codon, no FFI)
Key Patterns¶
- Override
on_tradeto receive trade events - Use streaming indicators for per-tick computation
- Call
emit_*methods to submit orders - Query
position()to check current state
Performance¶
Because Codon compiles to native code:
- on_trade is compiled to a native function, not interpreted
- StreamingSMA.update() is a native function call
- No GIL, no interpreter, no garbage collector pauses
Step 4: Compare with C++ and Python¶
The same strategy in C++:
void onSymbolTrade(SymbolContext& c, const TradeEvent& ev) override {
prices_.push_back(ev.trade.price.toDouble());
// ... SMA logic ...
if (fast_above && !long_position_)
emitMarketBuy(symbol(), orderSize_);
}
And in Python:
def on_trade(self, ctx, trade):
self.prices.append(trade.price)
# ... SMA logic ...
if fast > slow and not self.long_position:
self.emit_market_buy(ctx.symbol_id, 1.0)
All three produce identical trading behavior with the same API shape.
Next Steps¶
- See Pairs Trading for multi-symbol strategies
- See Indicators Reference for all available indicators
- See Strategy Classes for the full API reference