Search…
Transaction cost
When running strategies, transactions costs are not considered by default. You can include transactions costs by running the following code.
1
import sigtech.framework as sig
2
3
env = sig.init()
4
env[sig.config.IGNORE_T_COSTS] = False
Copied!
There are default transaction costs defined either on the TradePricer class or on the instrument class. These can be overridden when the environment is initialised.
A transaction cost model can be set by its instrument classname, its instrument group or its individual ticker. If the transaction model is not defined in the register, it defaults to the model defined on the instrument class.
The parameters used in the transaction cost can be updated or a new transaction model with new models can be passed.
The overrides passed are a dictionary with the following keys:
• Class name
• Group ticker
• Instrument ticker
The values of the dictionary are a tuple with the first value being the name of the transaction model to use and the second any parameters the model might use.
Specifically, we can assign transaction cost models to instruments based on the following criteria:
• Class name, such as SingleStock, Future or CommodityFuture.
• Group ticker, such asES COMDTY FUTURES GROUP or US GOVT BOND GROUP.
• Instrument ticker, such as 1000045.SINGLE_STOCK.TRADABLE, ESH18 COMDTY or US 2.5 2006/10/31 GOVT.
Note: the above criteria is ordered by ascending specificity of scope.

# Environment

This section will import relevant internal and external libraries, as well as setting up the platform environment.
1
import sigtech.framework as sig
2
3
import pytz
4
import datetime as dtm
5
6
if not sig.config.is_initialised():
7
sig.init()
8
sig.config.set(sig.config.HISTORY_SCHEDULE_BUILD_FROM_DATA, True)
9
# Here is where the transaction costs overrides are set
10
sig.config.set(sig.config.T_COST_OVERRIDES, {
11
'TestEquity': ('fixed_cost', 2),
12
'US GOVT BOND GROUP': ('yield_transaction_type', None),
13
'TEST_2 EQUITY': ('foo', {'long_multiple': 1.2, 'short_multiple': 0.95}),
14
15
'EURUSD OTC OPTION GROUP': ('option_default', None),
16
'GBPUSD OTC OPTION GROUP': ('option_vol_spread', 0.001),
17
})
Copied!

# Transaction cost models available by default

A number of basic transaction cost models are available on the platform—they need to be passed calibrated parameters to function.

## Background

Some glossary terms:
1. 1.
Cost versus arrival price (
$C$
): represents the cost incurred on a trade, expressed as a percentage of price. Example: if
$C = 0.002$
and bid, or ask, price is £ 35.22, then cost incurred would be ca. £ 0.07.
2. 2.
Participation rate (
$r$
):
$\frac{Q}{V}$
where
$Q$
is the order size, in units, and
$V$
is a measure of the average daily volume, in units.
$r$
is the proportion of the daily volume that is constituted by this trade.
Cost of transacting (
$C$
) is typically characterised as containing three parts:
$C$
) = instant impact + temporary impact + permanent impact
• Instant impact: cost incurred immediately, such as crossing the bid/offer spread or incurring slippage.
• Temporary impact: adverse market price movement during the execution of the trade.
• Permanent impact: difference in market price before and after the trade.
The temporary and permanent impacts are sometimes grouped together under the market impact.

## Fixed commission

Applies a fixed commission to the trade, irrespective of quantity traded or price of instrument:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('fixed_commission', 6.5),
3
})
Copied!
Result: applies a fixed 6.50 units of cost to the trade, in the currency of the trade.

## Fixed cost (fixed spread from mid)

Applies a fixed cost from the mid of the instrument:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('fixed_cost', 2),
3
})
Copied!
Result: applies a cost of 2 units of currency from the mid of the instrument.

## Fixed relative cost (percentage spread from mid)

Applies a fixed cost relative to the mid of the instrument. 0.01 would equate to a charge of 1% of mid.
$C = k$
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('fixed_relative_cost', 0.002),
3
})
Copied!
Result: applies a cost of 20 bps spread from mid to the trade.

## Square-root market impact model

Cost is represented as only a market impact, expressed as:
$C = c \sigma r^\alpha$
Where
$\sigma$
is the daily volatility of returns and where
$c$
and
$a$
are some constants of
$\mathcal{O}(1)$
. Sotiropoulos et. al (2018) suggest
$c ≈ 0.35$
and
$a ≈ 0.40$
for the U.S. equity markets.
This model can also be thought of as a spread varying with volatility (the 𝑐𝜎cσ term) and proportional to the participation rate raised to some power (the
$r^\alpha$
term).
Usage 1:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('market_impact_square_root', (0.35, 0.40)),
3
})
Copied!
Result: applies a cost of
$c \sigma r^\alpha$
where
$c = 0.35$
and
$a = 0.40$
.
Usage 2:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('market_impact_square_root', (0.35, 0.40, 0.012)),
3
})
Copied!
Result: applies a cost of
$c \sigma r^\alpha$
, where
$c = 0.35$
,
$a = 0.40$
and daily volatility
$\sigma$
is set to 1.2%.

## Combined model (square-root market impact plus spread relative to mid)

Cost is represented as a combination of a spread and a market impact:
$C = k + c \sigma r^\alpha$
Usage:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('market_impact_square_root', (0.004, 0.33, 0.41)),
3
})
Copied!
Result: applies a cost of
$k + c \sigma r^\alpha$
where
$k = 0.004$
,
$c = 0.33$
and
$a = 0.41$
. This would be equivalent to a 40 bps spread plus a square-root market impact type cost with the given values for
$c$
and
$a$
.

# Building your own transaction cost model

Clients often have detailed and specific transaction cost data and models and wish to integrate these into the platform. The SigTech platform's flexible transaction cost API makes this as simple as building a method that takes inputs and returns a final trade price.

## Anatomy of a transaction cost model

When a trade is to be executed, the platform goes through the following steps:
1. 1.
Using the instrument name, look in T_COST_OVERRIDES for a given method to run and parameters to pass to mentioned method. If none found, fall back to the default set of methods.
2. 2.
Run the method with the parameters stored in T_COST_OVERRIDES.
3. 3.
4. 4.
Inside method: return the price
$p$
at which the trade will be executed.
5. 5.
$p$
.
A client's custom transaction cost model would need to be called by this method and make use of the inputs given by the parent class.

## Creating a transaction cost model

Creating a transaction cost model is as simple as defining a new method and attaching this method to either the TradePricer, for non-FX instruments, or FxPricer, for FX instruments:
1
def my_transaction_cost_model(self):
2
""" A custom transaction cost model that multiplies the price of an instrument by a certain factor. """
3
# Gets gross price
4
base_price = self.instrument.valuation_price_base(self.d)
5
6
# Depending on whether we are long or short, return different multiples
7
if self.quantity >= 0:
8
return base_price * self.params['multiple_long']
9
else:
10
return base_price * self.params['multiple_short']
11
12
13
Copied!
The custom transaction cost model is saved and can be set in the sig.config.T_COST_OVERRIDES:
1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
'Instrument_scope_here': ('my_transaction_cost_model', {'multiple_long': 1.1, 'multiple_short': 1/1.1}),
3
})
Copied!
The model takes one parameter, namely multiple, which is passed in through a dictionary.

## Inputs: information available to a custom transaction cost model

A custom transaction cost model can make use of any of the properties existing in the parent class, TradePricer:
• datetime of trade
• Transaction type, such as outright and roll.
Tip: to see a full list, view the source code for TradePricer by querying sig.TradePricer??.

# FX-specific transaction costs

The FX spot and forward transaction costs can be customised using the same T_COST_OVERRIDES dictionary.
Due to how the FX time series is cached in the platform, the transaction costs applied to FX instruments are done so through the FxPricer class, rather than the TradePricer.
Note: transaction costs set in this way act on the entire history rather than one time point, which is the main difference.
Note: only spread_from_mid is currently supported.

## Set by currency cross

1
sig.config.set(sig.config.T_COST_OVERRIDES, {
2
3
})
Copied!
This sets the EURUSD pair to use the spread_from_mid transaction model passing the parameter 0.0005. This results in a 5 bps, or 0.05%, transaction cost (from mid) being applied.
Input
Output
1
Copied!
1
2
Docstring:
3
Price computed as mid price plus/minus spread.
4
5
:param t_cost: Transaction cost.
6
File: ~/framework/framework/sigtech/framework/transaction_cost/transaction_cost.py
7
Type: function
Copied!

# Use case: custom option transaction cost model

The default behaviour is to take the bid/ask from the market vol surface data. The following is an example of doing the same with an override and how to apply a custom spread to the vol.

## Adding the transaction cost methods

Two new transaction cost methods are added to the TradePricer in the following example:
Note: once they are added they can then be used as an override in the environment configuration.
1
def option_default(self):
2
""" Take bid/ask from the volsurface data - this is the default behaviour"""
3
4
print('-- Running option_default cost retrieval for {} unit --'.format(str(self.quantity)))
5
6
option = self.instrument
7
if self.quantity >= 0:
8
9
else:
10
11
12
13
14
print('-- Running option_vol_spread cost retrieval for {} unit --'.format(str(self.quantity)))
15
16
option = self.instrument
17
grp = option.group()
18
19
20
# Find the mid vol for the option
21
mid_vol = grp.vol_from_strike(
22
option.option_type,
23
option.strike,
24
25
option.maturity_date,
26
mode='mid'
27
)
28
29
# Apply a spread to the vol based on the direction to trade
30
if self.quantity >= 0:
31
vol = mid_vol + self.params
32
33
else:
34
vol = mid_vol - self.params
35
mode = 'BID'
36
37
# Evaluate the option valuation
38
price = option.env.quant_bridge.black_calculator(
39
option._quant_library_option.option_info['payoff'],
40
41
option.maturity_date,
42
43
44
vol,
45
46
grp.get_vol_surface().day_count,
47
['NPV'],
48
)
49
50
# Return the valuation adjusted for fx rates
51
return option.env.fx_cross.convert_at_d(
52
price['NPV'],
53
54
from_ccy=option.currency,
55
to_ccy=self.currency,
56
field=mode + 'Price'
57
)
58
59
60
# Add the method to the pricer
61
62
Copied!

## Testing the costs

Once the config is initialised with the overrides, these costs are applied throughout the framework.
The following code block shows how to retrieve the prices used on examples of EURGBP and GBPUSD options. The vol surface will be loaded for the first pricing of an option in each group.
EURUSD
Input
Output
1
eurusd_fx_option_group = sig.obj.get('EURUSD OTC OPTION GROUP')
2
eurusd_call_option = eurusd_fx_option_group.get_option(
3
'Call',
4
1.3,
5
dtm.date(2010, 4, 6),
6
dtm.date(2011, 4, 6)
7
)
8
tz = pytz.timezone('Europe/London')
9
10
11
eurusd_call_option.name,
12
tz.localize(dtm.datetime(2010, 5, 6, 16)),
13
1,
14
eurusd_call_option.currency,
15
'outright'
16
17
18
19
eurusd_call_option.name,
20
tz.localize(dtm.datetime(2010, 5, 6, 16)),
21
-1,
22
eurusd_call_option.currency,
23
'outright'
24
25
26
print('{} : Ask {} - Bid {}'.format(
27
eurusd_call_option.name,
28
29
str(eurusd_bid)
30
))
Copied!
1
-- Running option_default cost retrieval for 1 unit --
2
-- Running option_default cost retrieval for -1 unit --
3
EURUSD 20110406 CALL 1.3 2432C0B2 CURNCY : Ask 0.05857657771580961 - Bid 0.05711512613362098
Copied!
GBPUSD
Input
Output
1
gbpusd_fx_option_group = sig.obj.get('GBPUSD OTC OPTION GROUP')
2
gbpusd_call_option = gbpusd_fx_option_group.get_option(
3
'Call',
4
1.52,
5
dtm.date(2010, 4, 6),
6
dtm.date(2011, 4, 6)
7
)
8
9
10
gbpusd_call_option.name,
11
tz.localize(dtm.datetime(2010, 5, 6, 15)),
12
1,
13
gbpusd_call_option.currency,
14
'outright'
15
16
17
18
gbpusd_call_option.name,
19
tz.localize(dtm.datetime(2010, 5, 6, 15)),
20
-1,
21
gbpusd_call_option.currency,
22
'outright'
23
24
25
print('{} Ask {} - Bid {}'.format(
26
gbpusd_call_option.name,
27
28
str(gbpusd_bid)
29
))
Copied!
1
-- Running option_vol_spread cost retrieval for 1 unit --
2
-- Running option_vol_spread cost retrieval for -1 unit --
3
GBPUSD 20110406 CALL 1.52 88628D24 CURNCY Ask 0.06781705290343404 - Bid 0.06668396352274569
Copied!

## Querying the transaction cost model

The TradePricer interface allows you to get the information about the transaction cost model applied to a given instrument:
1
lme_future = sig.LMEFuture( underlying_ticker='LX',
2
future_ticker='K21',
3
strike=0)
4