Search…
Position rounding
This page shows the platform's functionality to investigate the impact of rounding off orders on the strategy performance.
Orders have a minimum size and can often only be traded in discrete units. By default, the platform simulation allows fractional units, with rounding applied when trades are exported. This allows strategies to be analysed and compared without consideration for the capital allocated to them.
The platform has additional functionality to see the impact that size has, or run the simulation with the discrete sizes enforced.
Learn more: Example notebooks

Environment

This section will import relevant internal and external libraries, as well as setting up the platform environment.
Learn more: Environment setup
1
import sigtech.framework as sig
2
from sigtech.framework.instruments.bonds import GovernmentBond
3
4
import datetime as dtm
5
import pandas as pd
6
import pytz
7
import seaborn as sns
8
9
sns.set(rc={'figure.figsize': (18, 6)})
10
11
if not sig.config.is_initialised():
12
sig.config.init()
13
sig.config.set(sig.config.FORCE_ORDER_ROUNDING, True)
Copied!

Unit rounding definitions

The tradable sizes for each instrument vary and are defined on the instrument classes.
An example for government bonds:
Input
Output
1
GovernmentBond.rounded_units??
Copied!
1
Signature: GovernmentBond.rounded_units(self, units, dt, to_trade=True)
2
Source:
3
def rounded_units(self, units, dt, to_trade=True):
4
"""Round bond face values to multiples of 1000"""
5
return self.positions_to_units(int(self.units_to_positions(units, dt) / 1000), dt) * 1000
6
File: ~/code/framework/sigtech/framework/instruments/bonds.py
7
Type: function
8
If required these settings can be adjusted. An example for adjusting this is given in the code below.
9
10
from sigtech.framework.instruments.bonds import GovernmentBond
11
12
def rounded_units(self, units, dt, to_trade=True):
13
"""Round bond face values to multiples of 10000"""
14
return self.positions_to_units(int(self.units_to_positions(units, dt) / 10000), dt) * 10000
15
16
GovernmentBond.rounded_units = rounded_units
Copied!

Large AUM strategy example

To observe the impact of this flag we can create a simple basket of two rolling future strategies. Each of the rolling futures strategies are initialised with an AUM of 1m USD and the basket has 10m USD:
1
def get_rfs(contract_code: str) -> sig.RollingFutureStrategy:
2
return sig.RollingFutureStrategy(
3
currency='USD',
4
start_date=dtm.date(2010, 1, 4),
5
contract_code=contract_code,
6
contract_sector='INDEX',
7
rolling_rule='front',
8
front_offset='-6:-4'
9
)
Copied!
1
ROLLING_FUTURE_START_AUM = 10 ** 6
2
3
st = sig.BasketStrategy(
4
currency='USD',
5
start_date=dtm.date(2017, 1, 6),
6
constituent_names=[
7
get_rfs('ES').clone_strategy(
8
{'initial_cash': ROLLING_FUTURE_START_AUM}
9
).name,
10
get_rfs('NQ').clone_strategy(
11
{'initial_cash': ROLLING_FUTURE_START_AUM}
12
).name
13
],
14
weights=[0.5, 0.5],
15
rebalance_frequency='EOM',
16
initial_cash=10000000,
17
)
Copied!
Input
Output
1
st.history().plot(title='Basket Strategy Performance');
Copied!
All the trades are in whole units:
Input
Output
1
st.inspect.evaluate_trades(dtm.datetime(2017, 1, 10, tzinfo=pytz.utc))
Copied!

Small AUM strategy example

If the size of the basket is reduced to 100,000 USD, it is now too small to support the holdings of the rolling future strategies and no positions are held:
1
st2 = sig.BasketStrategy(
2
currency='USD',
3
start_date=dtm.date(2017, 1, 6),
4
constituent_names=[
5
get_rfs('ES').clone_strategy(
6
{'initial_cash': ROLLING_FUTURE_START_AUM}
7
).name,
8
get_rfs('NQ').clone_strategy(
9
{'initial_cash': ROLLING_FUTURE_START_AUM}
10
).name
11
],
12
weights=[0.5, 0.5],
13
rebalance_frequency='EOM',
14
initial_cash=100000,
15
)
Copied!
Input
Output
1
st2.inspect.evaluate_trades(dtm.datetime(2017, 1, 10, tzinfo=pytz.utc))
Copied!

Rounding impact summary

In addition to the flag provided in the configuration, each strategy has a method to investigate the impact of trading it with different allocations.
Note: this is independent of the FORCE_ORDER_ROUNDING flag, so does not require it to be active.
Call the approximate_rounding_summary method on the strategy to generate a report:
Input
Output
1
impact_data = st.analytics.approximate_rounding_summary(steps=24)
2
impact_data.plot(subplots=True);
Copied!
The performance of this basket drops off near an AUM of 10e5.5 USD:
Input
Output
1
st.inspect.evaluate_trades(dtm.datetime(2019, 4, 10, tzinfo=pytz.utc), aum=10**5.5)
Copied!

Impact approximation

The deviation from the simulated performance can also be observed over the history using the approximate_rounding_impact method on the strategy.
Input
Output
1
ax = (100 * pd.concat(
2
[st.analytics.approximate_rounding_impact(initial_aum=10 ** x).rename(str(10 ** x) + ' AUM') for x in (5, 6, 7)],
3
axis=1)).plot()
4
ax.set_ylabel('% Return Deviation');
Copied!