Moonshot limit_position_sizes causing error

In reference to a previous post of mine: Force Moonshot to trade in quantities of 100 - #2 by Brian

I added the code mentioned in this post to my strategy file. We were working with a universe containing 1 stock at the time and the modification was working great in both back testing and live trading!

We moved on to back testing a universe of 39 stocks and the back test started failing. The outputted error is at the end of this post. We removed the limit_position_sizes() function and the backtest would execute successfully.

I increase our NLV from 100,000 to 1,000,000 in back testing thinking it could be failing due to a lack of cash with no success.

Error

       quantrocket-moonshot-1|Traceback (most recent call last):
       quantrocket-moonshot-1|  File "sym://qrocket_mshot_backtest_backtest_py", line 52, in mule_backtest_strategies
       quantrocket-moonshot-1|  File "sym://qrocket_mshot_backtest_backtest_py", line 183, in backtest_strategies
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/moonshot/strategies/base.py", line 1343, in backtest
       quantrocket-moonshot-1|    weights = self._constrain_weights(weights, prices)
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/moonshot/strategies/base.py", line 962, in _constrain_weights
       quantrocket-moonshot-1|    quantities > max_quantities_for_longs, quantities)
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/pandas/core/ops/common.py", line 69, in new_method
       quantrocket-moonshot-1|    return method(self, other)
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/pandas/core/arraylike.py", line 48, in __gt__
       quantrocket-moonshot-1|    return self._cmp_method(other, operator.gt)
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/pandas/core/frame.py", line 6851, in _cmp_method
       quantrocket-moonshot-1|    self, other = ops.align_method_FRAME(self, other, axis, flex=False, level=None)
       quantrocket-moonshot-1|  File "/opt/conda/lib/python3.9/site-packages/pandas/core/ops/__init__.py", line 288, in align_method_FRAME
       quantrocket-moonshot-1|    raise ValueError(
       quantrocket-moonshot-1|ValueError: Can only compare identically-labeled DataFrame objects

Any input is welcome on next steps for troubleshooting!

The error message means that the index or columns of the max_quantities_for_longs DataFrame doesn't match the index or columns of the DataFrame being returned from signals_to_target_weights (the quantities DataFrame in the error message is derived from the DataFrame returned by signals_to_target_weights).

Is this by chance a once-a-day intraday strategy as described in the docs? If so, you would need to modify the code in the other post to select a particular time from the closes DataFrame. Something like:

def limit_position_sizes(self, prices: pd.DataFrame):
    closes = prices.loc["Close"]
    # select a time to convert the intraday index to the daily index
    closes = closes.xs("15:59:00", level="Time")
    max_quantities_for_longs = pd.DataFrame(100, index=closes.index, columns=closes.columns)
    max_quantities_for_shorts = -max_quantities_for_longs
    return max_quantities_for_longs, max_quantities_for_shorts

More generally, a good way to deal with an issue like this is to develop interactively in a Jupyter notebook. Run the code in your signals_to_target_weights method to get the weights DataFrame, then run the code in your limit_position_sizes method to get that DataFrame, then compare them (e.g. weights > max_quantities_for_longs) and see if you get the same ValueError. It's easier to make sure the two DataFrames are properly aligned (same index, same columns) if you're playing with them interactively.

Thanks for the reply! It is a continues intraday. It trades every minute during market hours. Should l look into cross slicing the frame? It works fine with my realtime dataset. it isnt working with my historical US stocks db.

I can't tell what's going on from afar but the important first step is to see in what way the DataFrames don't match. Since the problem is with the history db and not the realtime db, you can try to compare them to see what's different. You can try to do that in the interactive notebook like I mentioned, or it may be easier just to print the shape of closes in limit_position_sizes when running with the realtime database vs the historical database to see what's different.

print(closes.shape)

See if it's the index (first number) or columns (second number) that don't match. From there you can perhaps try to print out the index or columns until you zero in on the difference.

Thanks for that time. I went ahead and pulled the outputs for the historical DB for both weights and limits.

The output is below. They appear to match from what I can tell. I validated all the column names match before posting and I am trusting the row counts prove that the rows are matching.

quantrocket-moonshot-1|From Weights
       quantrocket-moonshot-1|(8580, 27)
       quantrocket-moonshot-1|MultiIndex([('2024-01-26', '09:30:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:31:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:32:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:33:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:34:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:35:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:36:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:37:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:38:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:39:00'),
       quantrocket-moonshot-1|            ...
       quantrocket-moonshot-1|            ('2024-02-27', '15:50:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:51:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:52:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:53:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:54:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:55:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:56:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:57:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:58:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:59:00')],
       quantrocket-moonshot-1|           names=['Date', 'Time'], length=8580)
       quantrocket-moonshot-1|Index(['FIBBG000C23KJ3', 'FIBBG001QD41M9', 'FIBBG000BCSST7', 'FIBBG000BCVJ77',
       quantrocket-moonshot-1|       'FIBBG000FY4S11', 'FIBBG000BF6LY3', 'FIBBG000BBVJZ8', 'FIBBG000BZ9223',
       quantrocket-moonshot-1|       'FIBBG000BHZ5J9', 'FIBBG000BQPC32', 'FIBBG000BJDB15', 'FIBBG000BVWLJ6',
       quantrocket-moonshot-1|       'FIBBG000BMQPL1', 'FIBBG000BNJHS8', 'FIBBG000C2BXK4', 'FIBBG000BFXHL6',
       quantrocket-moonshot-1|       'FIBBG000C8H633', 'FIBBG000BSRN78', 'FIBBG000BJSBJ0', 'FIBBG000BPWXK1',
       quantrocket-moonshot-1|       'FIBBG000BKYDP9', 'FIBBG000BR2B91', 'FIBBG000BB5792', 'FIBBG000BSLZY7',
       quantrocket-moonshot-1|       'FIBBG002B04MT8', 'FIBBG000FFDM15', 'FIBBG000BWCKB6'],
       quantrocket-moonshot-1|      dtype='object')
quantrocket-moonshot-1|From Limit
       quantrocket-moonshot-1|(8580, 27)
       quantrocket-moonshot-1|MultiIndex([('2024-01-26', '09:30:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:31:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:32:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:33:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:34:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:35:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:36:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:37:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:38:00'),
       quantrocket-moonshot-1|            ('2024-01-26', '09:39:00'),
       quantrocket-moonshot-1|            ...
       quantrocket-moonshot-1|            ('2024-02-27', '15:50:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:51:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:52:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:53:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:54:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:55:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:56:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:57:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:58:00'),
       quantrocket-moonshot-1|            ('2024-02-27', '15:59:00')],
       quantrocket-moonshot-1|           names=['Date', 'Time'], length=8580)
       quantrocket-moonshot-1|Index(['FIBBG000C23KJ3', 'FIBBG001QD41M9', 'FIBBG000BCSST7', 'FIBBG000BCVJ77',
       quantrocket-moonshot-1|       'FIBBG000FY4S11', 'FIBBG000BF6LY3', 'FIBBG000BBVJZ8', 'FIBBG000BZ9223',
       quantrocket-moonshot-1|       'FIBBG000BHZ5J9', 'FIBBG000BQPC32', 'FIBBG000BJDB15', 'FIBBG000BVWLJ6',
       quantrocket-moonshot-1|       'FIBBG000BMQPL1', 'FIBBG000BNJHS8', 'FIBBG000C2BXK4', 'FIBBG000BFXHL6',
       quantrocket-moonshot-1|       'FIBBG000C8H633', 'FIBBG000BSRN78', 'FIBBG000BJSBJ0', 'FIBBG000BPWXK1',
       quantrocket-moonshot-1|       'FIBBG000BKYDP9', 'FIBBG000BR2B91', 'FIBBG000BB5792', 'FIBBG000BSLZY7',
       quantrocket-moonshot-1|       'FIBBG002B04MT8', 'FIBBG000FFDM15', 'FIBBG000BWCKB6'],
       quantrocket-moonshot-1|      dtype='object', name='Sid')

At a second glance, it looks like its coming out of limits with a name="sid" for the columns while in weights the column index doesn't have a name.

I imagine this is our issue. Any thoughts on how to address this one? Maybe there's a format like weights function I am unaware of :slight_smile:

I was able to get the name removed from the columns in the limits function with

closes.columns.name = (None)

But the same error is still occurring. The frames look identical in column and row indexes as well as length.

I exported out the dataframes into a CSV and looked at them in excel. All 3 dataframes (Weights, Max Long and Max Short) all three are the same as far as indexes and columns are concerned.

I am wondering if is an issue inside of moonshot itself. I am happy to provide the csv's of the dataframes.

Thanks!

Is your history database a Zipline bundle? If so, I was able to reproduce the error, and the issue is with the sorting of the columns - they contain the same values but not in the same order because one of the DataFrames is getting sorted along axis 1 and the other is not. This only happens with the Zipline bundle because Zipline is returning the data with columns unsorted while the realtime and history services returned sorted. The mismatched sorting causes the error you're getting. This issue will be fixed in the next release, but you can work around it in the meantime by sorting the columns in limit_position_sizes. Just change:

max_quantities_for_longs = pd.DataFrame(100, index=closes.index, columns=closes.columns)

to

max_quantities_for_longs = pd.DataFrame(100, index=closes.index, columns=closes.columns.sort_values())

and I think the error should go away.

Hi Brian! Yes, my history DB is a zipline bundle and adding .sort_values() worked! Thank you for looking into this.

Looking forward to the next release!