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.
This section will import relevant internal and external libraries, as well as setting up the platform environment.
import sigtech.framework as sig
from sigtech.framework.instruments.bonds import GovernmentBond
import datetime as dtm
import pandas as pd
import pytz
import seaborn as sns
sns.set(rc={'figure.figsize': (18, 6)})
if not sig.config.is_initialised():
sig.config.init()
sig.config.set(sig.config.FORCE_ORDER_ROUNDING, True)
The tradable sizes for each instrument vary and are defined on the instrument classes.
An example for government bonds:
Input
Output
GovernmentBond.rounded_units??
Signature: GovernmentBond.rounded_units(self, units, dt, to_trade=True)
Source:
def rounded_units(self, units, dt, to_trade=True):
"""Round bond face values to multiples of 1000"""
return self.positions_to_units(int(self.units_to_positions(units, dt) / 1000), dt) * 1000
File: ~/code/framework/sigtech/framework/instruments/bonds.py
Type: function
If required these settings can be adjusted. An example for adjusting this is given in the code below.
from sigtech.framework.instruments.bonds import GovernmentBond
def rounded_units(self, units, dt, to_trade=True):
"""Round bond face values to multiples of 10000"""
return self.positions_to_units(int(self.units_to_positions(units, dt) / 10000), dt) * 10000
GovernmentBond.rounded_units = rounded_units
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:
def get_rfs(contract_code: str) -> sig.RollingFutureStrategy:
return sig.RollingFutureStrategy(
currency='USD',
start_date=dtm.date(2010, 1, 4),
contract_code=contract_code,
contract_sector='INDEX',
rolling_rule='front',
front_offset='-6:-4'
)
ROLLING_FUTURE_START_AUM = 10 ** 6
st = sig.BasketStrategy(
currency='USD',
start_date=dtm.date(2017, 1, 6),
constituent_names=[
get_rfs('ES').clone_strategy(
{'initial_cash': ROLLING_FUTURE_START_AUM}
).name,
get_rfs('NQ').clone_strategy(
{'initial_cash': ROLLING_FUTURE_START_AUM}
).name
],
weights=[0.5, 0.5],
rebalance_frequency='EOM',
initial_cash=10000000,
)
Input
Output
st.history().plot(title='Basket Strategy Performance');

All the trades are in whole units:
Input
Output
st.inspect.evaluate_trades(dtm.datetime(2017, 1, 10, tzinfo=pytz.utc))

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:
st2 = sig.BasketStrategy(
currency='USD',
start_date=dtm.date(2017, 1, 6),
constituent_names=[
get_rfs('ES').clone_strategy(
{'initial_cash': ROLLING_FUTURE_START_AUM}
).name,
get_rfs('NQ').clone_strategy(
{'initial_cash': ROLLING_FUTURE_START_AUM}
).name
],
weights=[0.5, 0.5],
rebalance_frequency='EOM',
initial_cash=100000,
)
Input
Output
st2.inspect.evaluate_trades(dtm.datetime(2017, 1, 10, tzinfo=pytz.utc))

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
impact_data = st.analytics.approximate_rounding_summary(steps=24)
impact_data.plot(subplots=True);

The performance of this basket drops off near an AUM of 10e5.5 USD:
Input
Output
st.inspect.evaluate_trades(dtm.datetime(2019, 4, 10, tzinfo=pytz.utc), aum=10**5.5)

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

Last modified 1yr ago