Generic(eventGen, orderFactory)
wakes up at moments of time given by eventGen
and asks orderFactory
to create an order.
For example, a crossing averages strategy that sends market orders with exponentially distributed volume sizes in even intervals of time could be written as:
strategy.Generic(event.Every(constant(1.)),
order.factory.Market(
side = parts.side.TwoAverages(
MidPrice(orderbook.OfTrader()),
alpha1, alpha2),
volume = rnd.Expovariate(1.)
))
There are also handy specialisations of this generic strategy. Usually they accept parameters defining strategy logic and an order factory in a curried form.
Liquidity provider strategy (strategy.LiquidityProvider
class) is an array of two strategies providing liquidity for each side of trade over the asset (strategy.LiquidityProviderSide
). Every strategy.LiquidityProviderSide
wakes up at moments of time given by eventGen
, calculates a base price of the asset (it is taken as price of the best order in the queue, if none, price of the last trade is taken, if none we take some default value). Then an order with price equal to the base price multiplied by a value taken from priceDistr
and volume taken from volumeDistr
is created.
NotNone(x, defaultValue) ::=
if x == None then defaultValue else x
price.LiquidityProviderSide(orderqueue, priceDistr, defaultValue) ::=
priceDistr * (NotNone(BestPrice(orderqueue),
NotNone(LastTradePrice(orderqueue),
defaultValue))
strategy.LiquidityProviderSide(eventGen, orderFactory, side, priceDistr, defaultValue) ::=
orderbook = orderbook.OfTrader() in
strategy.Generic(eventGen,
orderFactory(ops.constant(side),
price.LiquidityProviderSide(
orderbook.queue(side),
priceDistr,
defaultValue)))
strategy.LiquidityProvider(eventGen, orderFactory, priceDistr, defaultValue) ::=
strategy.Array([
strategy.LiquidityProviderSide(eventGen, orderFactory, Side.Sell, priceDistr, defaultValue),
strategy.LiquidityProviderSide(eventGen, orderFactory, Side.Buy, priceDistr, defaultValue),
])
Noise strategy wakes up at moments of time given by eventGen
and chooses randomly trade side.
side.Random() ::=
if uniform(0,1) > 0.5 then Side.Sell else Side.Buy
strategy.Noise(eventGen, orderFactory) ::=
strategy.Generic(eventGen, orderFactory(side.Random()))
Signal strategy listens to some discrete signal
and when the signal becomes more than some threshold
the strategy starts to buy. When the signal gets lower than -threshold
the strategy starts to sell.
side.Signal(x, threshold) ::=
if x > threshold then Side.Buy else
if -x > threshold then Side.Sell else
None
strategy.Signal(eventGen, orderFactory, signal, threshold) ::=
strategy.Generic(eventGen, orderFactory(
side.Signal(signal, threshold)))
Trend follower can be considered as a sort of a signal strategy where the signal
is a trend of the asset's price. Under trend we understand the first derivative of some moving average. If the derivative is positive, the trader buys; if the derivative is negative, it sells. Since a moving average is a continuously changing signal, we check its derivative at moments of time given by eventGen
.
side.TrendFollower(price, alpha) ::=
side.Signal(Derivative(EWMA(price, alpha)), 0)
strategy.TrendFollower(eventGen, orderFactory, alpha) ::=
strategy.Generic(eventGen, orderFactory(
side.TrendFollower(observable.MidPrice(), alpha)))
Crossing averages strategy can be considered as a sort of a signal strategy where the signal
is a difference between two moving averages of the asset's price.
side.TwoAverages(price, alpha1, alpha2) ::=
side.Signal(EWMA(price, alpha1) - EWMA(price, alpha2), 0)
strategy.TwoAverages(eventGen, orderFactory, alpha1, alpha2) ::=
strategy.Generic(eventGen, orderFactory(
side.TwoAverages(observable.MidPrice(), alpha1, alpha2)))
Fundamental value strategy believes that an asset should cost some specific price (fundamental value
) and if current asset price is lower than the fundamental value it starts to buy the asset and if the price is higher than the fundamental value it starts to sell the asset.
side.FundamentalValue(orderbook, fv) ::=
if BestPrice(Asks(orderbook)) < fv then Side.Buy else
if BestPrice(Bids(orderbook)) > fv then Side.Sell else
None
strategy.FundamentalValue(eventGen, orderFactory, fv) ::=
strategy.Generic(eventGen, orderFactory(
side.FundamentalValue(orderbook.OfTrader(), fv)))
Mean reverting strategy is a kind of a fundamental value strategy with fundamentalValue
equal to a moving average of the asset's price.
side.MeanReverting(orderbook, alpha) ::=
side.FundamentalValue(orderbook, EWMA(MidPrice(orderbook), alpha))
strategy.MeanReverting(eventGen, orderFactory, alpha) ::=
strategy.Generic(eventGen, orderFactory,
side.MeanReverting(orderbook.OfTrader(), alpha))
Dependent price strategy believes that the fair price of an asset A
is completely correlated with price of another asset B
and the following relation should be held: Price(A) = k*Price(B)
, where k
is some factor. It may be considered as a variety of a fundamental value strategy.
side.Dependency(orderbook, otherOrderbook, factor) ::=
side.FundamentalValue(orderbook, MidPrice(otherOrderbook) * factor)
strategy.Dependency(eventGen, orderFactory, otherOrderBook, factor) ::=
strategy.Generic(eventGen, orderFactory(
side.Dependency(orderbook.OfTrader(), otherOrderBook, factor)))
These strategies keep track of the trader's position (actual position + pending orders volume) and if desiredVolume
changes it creates orders in order to cover the gap between the current and the desired position.
signed_volume.DesiredPosition(trader, desiredPosition) ::=
desiredPosition - VolumeTraded(trader) - VolumePending(trader)
Bollinger band strategy believes that a trader should take a position equal to the difference between the current asset price and its average divided on its standard deviation (and scaled by some factor
).
signed_volume.BollingerBands(alpha, k) ::=
trader = thisTrader(),
price = MidPrice(orderbook.OfTrader(trader)),
mean = EWMA(price, alpha),
stddev = StdDevEW(price, alpha) in
signed_volume.DesiredPosition(trader, (price - mean) / stddev * k)
Relative Strength Index strategy believes that a trader should take a position equal to deviation of its relative strength index from 50 scaled by some factor
.
signed_volume.RSI(alpha, k, timeframe) ::=
trader = thisTrader()
rsi = RSI(orderbook.OfTrader(trader), timeframe, alpha) in
signed_volume.DesiredPosition(trader, OnEveryDt(1, (50 - rsi) * k))
This strategy allows to drive the asset price based on historical market data by creating large volume orders for the given price. Every time step of 1 time unit in the simulation corresponds to a 1 day in the market data. At each time step the previous Limit Buy/Sell orders are cancelled and new ones are created based on the next price of the market data.
It is implemented as a strategy that wakes up once and create a composition of iceberg and floating price orders. The floating price is equal to the current quote plus/minus some delta and the iceberg order breaks an 'infinite' limit order into small lots.
class MarketData(types.ISingleAssetStrategy):
def getImpl(self):
quotes = observable.Quote(self.ticker, self.start, self.end) # TODO: should be in definitions
return strategy.Array([
strategy.Generic(
order.factory.Iceberg(
const(self.volume),
order.factory.FloatingPrice(
ops.constant(sign*self.delta) + quotes,
order.factory.price.Limit(
side = const(side),
volume = const(self.volume * 1000000)))),
event.After(ops.constant(0)))\
for side, sign in {Side.Buy : -1, Side.Sell : 1}.iteritems()
])