Option added for screen related API endpoints to output as DataFrame

We recently added a flag to the screen run, screen backtest and screen rolling backtest API endpoints so the output can be returned as a Pandas DataFrame. The ranks, data_universe and data endpoints already had this flag.

The most recent build is required. Run “pip install --upgrade p123api” to get latest the version.

Example:

import p123api
import pandas as pd

try:
    client = p123api.Client(api_id='<your id>', api_key='<your key>')

    data = client.screen_backtest(
        {
            'screen': 1094,  #P123 public Piotrosk screen
            'startDt': "2022-01-02",
        }, True)  # True = output to Pandas DataFrame, False = to JSON.

    pd.set_option('display.width', None)

    #Print the entire result.
    print(data)

    #Print them one section at a time.
    print("\nSTATS")
    print(pd.DataFrame(data['stats']))
    print("\nRESULTS")
    print(pd.DataFrame(data['results']))
    print("\nCHART")
    print(pd.DataFrame(data['chart']))

except p123api.ClientException as e:
    print(e)

When using this example and expanding it using this documentation here:

data = client.screen_backtest(
{
‘screen’: 1094, #P123 public Piotrosk screen
‘startDt’: “2022-01-02”,
‘endDt’:‘2022-12-14’,
‘frequency’: ‘Every Week’,
‘holdingPeriod’: 180,
‘slippage’:0.25,
‘pitMethod’:‘Complete’
}, True) # True = output to Pandas DataFrame, False = to JSON.

I get the following errors:

  1. Unrecognized parameter
  2. Unrecognized parameter
  3. The “True” parameter for the DataFrame output gives me this error.
    “Client.screen_backtest() takes 2 positional arguments but 3 were given”

I updated the P123API using:
pip install --upgrade p123api

Thanks
Tony

Hi Tony.

‘frequency’: ‘Every Week’,
Change to ‘rebalFreq’: ‘Every Week’,

holdingPeriod is not a valid parameter for Screen Backtest endppoint. This is only for Rolling Screen Backtest. So remove this → ‘holdingPeriod’: 180,
Valid parameters are shown in https://api.portfolio123.com:8443/docs/index.html#/Screen/_rolling_backtest

The True flag for the dataframe should work. I cant copy/paste your code since the formatting gets all messed up. Here is example. Dont forget the import pandas line.

import p123api
import pandas as pd

try:
    client = p123api.Client(api_id='insert yourid', api_key='yourkey')

    data = client.screen_backtest(
       {
           'screen': 1094,  # P123 public Piotrosk screen
           'startDt': '2020-12-05',
           'endDt': '2022-12-14',
           #'frequency': 'Every Week',
           'rebalFreq':  'Every Week',
           #'holdingPeriod': 180,  #na for Screen Backtest endppoint. This is only for Rolling Screen Backtest.
           'slippage':0.25,
           'pitMethod': 'Complete'
       }, True)  # True = output to Pandas DataFrame, False = to JSON.

    pd.set_option('display.max_rows', None)
    pd.set_option('display.max_columns', None)
    pd.set_option('display.width', None)
    print(data)

except p123api.ClientException as e:
    print(e)

Dan, sorry. I am doing a screen_rolling_backtest.

I still get the same errors even with: ‘rebalFreq’: ‘Every Week’,

Is there a way to test the P123API version in code?

import p123api
import pandas as pd

try:
    client = p123api.Client(api_id='xxx', api_key='xxx')

    data = client.screen_rolling_backtest(
       {
           'screen': 1094,  # P123 public Piotrosk screen
           'startDt': '2020-12-05',
           'endDt': '2022-12-14',
           'rebalFreq':  'Every Week',
           'holdingPeriod': 180,  #na for Screen Backtest endppoint. This is only for Rolling Screen Backtest.
           'slippage':0.25,
           'pitMethod': 'Complete'
       }, True)  # True = output to Pandas DataFrame, False = to JSON.

    pd.set_option('display.max_rows', None)
    pd.set_option('display.max_columns', None)
    pd.set_option('display.width', None)
    print(data)

except p123api.ClientException as e:
    print(e)

To see the info for the p123api package, run this at a command prompt: pip show p123api
Mine shows Version: 1.3.4. I have never ran that before and I dont know how often the version # is updated.

The issue with the latest version of the script you posted is that you need to switch it back to have ‘frequency’ instead of rebalFreq since this script is for screen_rolling_backtest. Each of the API endpoints have their own definitions and you need to check the API schema (link to it in my last post) since you have to use exactly what is written there.

Mine is the same:
Name: p123api
Version: 1.3.4
Summary: Portfolio123 API wrapper
Home-page: GitHub - portfolio-123/p123api-py: Portfolio123 python wrapper
Author: Portfolio123
Author-email: info@portfolio123.com

But with python, you are not necessarily using the same version in code that shows in the command prompt, such as when using virtual environments.

Any other suggestions I can try?
Does the last code block I posted work for you, error free?

Thanks
Tony

Yes, I did a copy/paste of your latest code and the only thing I had to change was to use ‘frequency’: ‘Every Week’. It ran with no errors.

I will check with the developer to see if there is another way to get the version. But you said you ran the pip install command, so I doubt it is a version issue.

Dan, I figured it out. It was a wrong version of p123api.

I have multiple versions of Python installed and if you run PIP in the wrong version, it will not work correctly even though the command line says its installed.

One other question.
If I want to run a screen rolling backtest, but I want to alter the ranking system each time via the API, I can use client.rank_update(data) to update the ApiRankingSystem and then reference that RS in the rolling backtest via this?

'ranking': 'Ranking Name',

Thanks
Tony

Yes. Your script would first use client.rank.update to change the ApiRankingSystem to whatever you want for that iteration.

In you example earlier, your screen_rolling_backtest script defined the screen using the id: ‘screen’: 1094,
If you do that, just set the ranking system in that screen (a screen that is not a p123 screen like 1094) to be use the ApiRankingSystem ranking system.

You could also define the screen inside the screen_rolling_backtest script. But that is more complicated, so only do it if you need to change other settings in the screen.

Dan, here is the rolling back test example in the docs. It is unclear to me how to specify a ranking system into this. There is no ranking system parameter listed here. Can you provide an example of this using the APIRankingSystem? Do I need to first use another API function to define the screen and its ranking system? Maybe something like screen_backtest() ?

p123api.Client().screen_rolling_backtest({
    'screen': id,
    'startDt': 'yyyy-mm-dd',
    #
    # Optional parameters
    'endDt': 'yyyy-mm-dd',
    'pitMethod': 'Prelim' | 'Complete',
    'precision': 2 | 3 | 4,
    'transPrice': 1, # 1 - Open | 3 - Avg of Hi and Low | 4 - Close
    'maxPosPct': 0,
    'slippage': 0.25,
    'longWeight': 100,
    'shortWeight': 100,
    'frequency': 'Every 4 Weeks' | 'Every Week',
    'holdingPeriod': 182
})

I moved this conversation with Tony to email. But this example could be helpful to others. It answers the question above and also has a section at the end that gives an example of how to handle the output from screen_rolling_backtest.

   data = client.screen_rolling_backtest(
       {
           #'screen': 1094,  #Could have used a screen id instead IF you don’t need to make any changes to the screen settings during the tests you are running.
           'screen': {
               'type': 'stock',
               "universe": 'SP500',
               'maxNumHoldings': 10, #0 for all
               'method': 'long',
               'benchmark': 'SPY',
               'rules': [{'formula': 'close(0) > 1'},
                         {'formula': 'Mktcap > 3000'},
                          ],
               'ranking': 'ApiRankingSystem'  #another section of the script would make changes to ApiRankingSystem
           },
           'startDt': "2021-01-03",
           # Optional parameters
           'endDt': '2022-01-01',
       }
       )

   # Run the line below to find the names of the keys to use in the code below.
   #print(data.keys())  # Returns: dict_keys(['cost', 'quotaRemaining', 'columns', 'rows', 'average', 'upMarkets', 'downMarkets'])

   # Output the column headers and the data:
   pandas.set_option('display.width', None)
   pandas.set_option('display.max_colwidth', 40)
   df1 = pandas.DataFrame(data['columns']).transpose()
   df2 = pandas.DataFrame(data['rows'])
   df3 = df1.append(df2, ignore_index = True)
   print(df3)

   df1 = pandas.DataFrame(data['columns']).transpose()
   df2 = pandas.DataFrame(data['average']).transpose()
   df3 = pandas.DataFrame(data['upMarkets']).transpose()
   df4 = pandas.DataFrame(data['downMarkets']).transpose()
   df5 = df1.append([df2,df3,df4], ignore_index = True)
   print(df5)