Table of Contents
Compound modules are shown in notation of a special domain specific language (to be developed)
MACD(x, slow, fast) ::= EWMA(x, 2./(fast+1)) - EWMA(x, 2./(slow+1))
Currently they are implemented using a Python subset:
class MACD(ops.Function[float]):
def getImpl(self):
return EWMA(self.source, 2./(self.fast+1)) - EWMA(self.source, 2./(self.slow+1))
@property
def label(self):
return 'MACD_{%s}^{%s}(%s)' % (self.fast, self.slow, self.source.label)
_wrap.function(MACD, ['Statistics', 'MACD', 'Convergence/Divergence'],
""" Moving average convergence/divergence
""",
[
('source', 'MidPrice()', 'types.IObservable[float]'),
('fast', '12', 'types.positive'),
('slow', '26', 'types.positive'),
], globals())
Normally they return None if one of the operands is None
Constant[T]
/None[T]
functionsIdentity
function- Arithmetic operations (+,-,*,/,%)
- Comparisons (<, <=, >, >=, ==, !=)
- Conditional branching (
if condition then trueBranch else falseBranch
) - Math module functions (
Exp
,Pow
,Log
,Atan
etc.) - Random distrubutions (
uniform
,lognormvariate
,expovariate
etc.) Derivative
of a differentiable functionQuotes
: downloads external historical dataLagged
: returns function values with some lagCurrentTime
: current model time
Max(x,y) ::= if x > y then x else y
Min(x,y) ::= if x < y then x else y
Sqr(x) ::= x*x
- average (Mean): cumulative (
CMA
), moving (MA
), exponentially weighted (EWMA
) - variance (Variance): cumulative, moving, exponentially weighted
- moving minimum/maximum
Variances could be implemented via Mean but it looses precision
Var(x) ::= Mean(Sqr(x)) - Sqr(Mean(x))
Standard deviation
StdDev(x) ::= Sqrt(Variance(x))
Relative strength index
Ups(x, dt, alpha) ::= EWMA(max(0, x - Lagged(x, dt)), alpha)
Downs(x, dt, alpha) ::= EWMA(max(0, Lagged(x, dt) - x), alpha)
RSI(x, dt, alpha) ::= 100 - 100 / (1 + Ups(x,dt,alpha)/Downs(x,dt,alpha))
Moving average convergence/divergence
MACD(x, slow, fast) ::= EWMA(x, 2./(fast+1)) - EWMA(x, 2./(slow+1))
MACD_signal(x, slow, fast, timeframe) ::= EWMA(MACD(x, slow, fast), 2/(timeframe+1))
MACD_histogram(x, slow, fast, timeframe) ::= MACD(x,slow,fast) - MACD_signal(x,slow, fast, timeframe)
Bollinger bands
Bollinger_Hi(x) ::= Mean(x) + 2*StdDev(x)
Bollinger_Lo(x) ::= Mean(x) - 2*StdDev(x)
TickSize(orderbook)
Asks(orderbook)
/Bids(orderbook)
: return asks or bids queue of theorderbook
BestPrice(orderqueue)
: current price at theorderqueue
LastTradePrice(orderqueue)
: price of the last tradeLastTradeVolume(orderqueue)
: volume of the last tradePriceAtVolume(orderqueue, volume)
: price of order at the given depthCumulativePrice(volume)
: sum of the best order prices with total volume less thanvolume
Price of last trades weighted by their volumes
WeightedPrice(queue, alpha) ::= EWMA(LastTradePrice(queue)*LastTradeVolume(queue), alpha) /
EWMA(LastTradeVolume(queue), alpha)
Mid-price
MidPrice(orderbook) ::= (BestPrice(Asks(orderbook)) + BestPrice(Bids(orderbook))) / 2
Spread
Spread(orderbook) ::= BestPrice(Asks(orderbook)) - BestPrice(Bids(orderbook))
Position(trader)
Balance(trader)
PendingVolume(trader)
: cumulative volume of orders sent by thetrader
but haven't been matched
Efficiency(trader) ::= Balance(trader) + CumulativePrice(Orderbook(trader), Position(trader))
EfficiencyTrend(trader, alpha) ::= Derivative(EWMA(Efficiency(trader), alpha))
Price for a liquidity provider
NotNone(x, defaultValue) ::= if x == None then defaultValue else x
LiquidityProviderPrice(orderqueue, priceDistr, defaultValue) ::=
priceDistr * (NotNone(BestPrice(orderqueue),
NotNone(LastTradePrice(orderqueue),
defaultValue))
Side for a noise strategy
NoiseSide() ::= if uniform(0,1) > 0.5 then Side.Sell else Side.Buy
Side for a signal value strategy
SignalSide(x, threshold) ::= if x > threshold then Side.Buy else
if -x > threshold then Side.Sell else
None
Side for a trend follower
TrendFollowerSide(price, alpha) ::= SignalSide(Derivative(EWMA(price, alpha)), 0)
Side for crossing averages strategy
TwoAveragesSide(price, alpha1, alpha2) ::= SignalSide(EWMA(price, alpha1) - EWMA(price, alpha2), 0)
Side for fundamental value strategy
FundamentalValueSide(orderbook, fv) ::= if BestPrice(Asks(orderbook)) < fv then Side.Buy else
if BestPrice(Bids(orderbook)) > fv then Side.Sell else
Nothing
Side for mean reverting strategy
MeanRevertingSide(orderbook, alpha) ::= FundamentalValueSide(orderbook, EWMA(MidPrice(orderbook), alpha))
Side for dependency trading strategy
DependencySide(orderbook, otherOrderbook) ::= FundamentalValueSide(orderbook, MidPrice(otherOrderbook))
Signed volume for a desired position strategy
DesiredPositionVolume(x, trader) ::= x - (Position(trader) + PendingVolume(trader))
Signed volume for a RSI strategy
RSI_Volume(trader, alpha, k, lag) ::=
price = MidPrice(Orderbook(trader)) in
DesiredPositionVolume(k * (50 - RSI(price, lag, alpha)), trader)
Signed volume for Bollinger band strategy
BollingerVolume(trader, alpha, k) ::=
price = MidPrice(Orderbook(trader)) in
DesiredPositionVolume((price - EWMA(price, alpha)) / StdDevEW(price, alpha) * k, trader)
Base orders:
Market
orderLimit
order
Meta orders:
Iceberg(lotSize, orderFactory)
creates an order usingorderFactory
and sends it consequetively splitting on portions oflotSize
FloatingPrice(priceFunc, orderFactory)
creates an order with price controlled by priceFuncPeg(orderFactory)
creates an order that tries to keep its price the best. Implemented viaFloatingPrice
andMaximum
/Minimum
ImmediateOrCancel(orderFactory)
creates a (limit-like) order with an immediate cancellation requestWithExpiry(expiry, orderFactory)
creates limit-like orders that are cancelled afterexpiry
StopLoss(maxLoss, orderFactory)
sends an order and if losses from keeping its position are higher thanmaxLoss
liquidates it
It should be noted that meta orders can be combined in quite wide range. For example,
WithExpiry(expiry = const(10.),
factory = Iceberg(lotSize = const(1),
factory = Peg(
factory = Limit(volume = const(10))))),
creates limit orders with volume 10, price is taken as the best price (Peg order), sends them in portions of lotSize = 1
and cancels them after expiry = 10
units of time.
Generic(eventGen, orderFactory)
wakes up at moments of time given byeventGen
and asksorderFactory
to create an order
A crossing averages strategy that sends market orders with exponentially distributed volume sizes in even intervals of time could be written as:
Generic(event.Every(constant(1.)),
order.factory.Market(
side = parts.side.TwoAverages(MidPrice(orderbook.OfTrader()), alpha1, alpha2),
volume = rnd.Expovariate(1.)
))
Array(strategies)
aggregates an array of strategiesSuspendable(strategy, predicate)
passes orders issued bystrategy
only ifpredicate
is true
In order to estimate trade impact of a strategy there are two classes:
- VirtualMarket
for every order sent by the strategy it tries to estimate at what price it would be executed
- ActuallyTraded
tracks actual trades done on orders issued by the strategy
A strategy that wraps another strategy
and passes its orders only if it is considered as "effective" can be implemented in the following way:
Suspendable(strategy, Derivative(EWMA(Efficiency(VirtualMarket(strategy))), alpha) >= 0)
A strategy that provide liquidity using historical data serves as a good example of composing strategies, meta order factories and observables
class MarketData(types.ISingleAssetStrategy):
def getImpl(self):
quotes = observable.Quote(self.ticker, self.start, self.end)
return Array([
Generic(
order.factory.Iceberg(
ops.constant(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()
])
_wrap.strategy(MarketData, ['Market data'],
""" A Strategy that 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 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.
|ticker|
Ticker of the asset
|start|
Start date in DD-MM-YYYY format
|end|
End date in DD-MM-YYYY format
|delta|
Price difference between orders placed and underlying quotes
|volume|
Volume of Buy/Sell orders. Should be large compared to the volumes of other traders.
""",
[ ('ticker','"^GSPC"', 'str'),
('start', '"2001-1-1"', 'str'),
('end', '"2010-1-1"', 'str'),
('delta', '1', 'positive'),
('volume','1000', 'Volume')], globals())