Portfolio123 (P123)
Portfolio123 is a web platform for systematic equity research: screening, multi-factor ranking
systems, strategy simulation/backtesting, and a REST API. Its formula language (not Python) is
used in screen rules, ranking-system nodes, buy/sell rules, and API data pulls.
Everything in this skill was extracted from the official P123 Factor Reference on 2026-06-09
(4,463 factors and 465 functions across 13 categories, plus 473 constants/series-IDs/operators)
and from the official OpenAPI spec and p123api wrapper source. Never invent a factor or
function name - if a name is not in these reference files, assume it does not exist and look
up the correct one.
Which reference file to read
Read only what the task needs:
| Task |
Read |
REST API or p123api Python calls (auth, screen_run, backtests, rank_ranks, data_universe, uploads, AI Factor) |
references/api.md |
| Valuation ratios (PE, EV/EBITDA, price-to-X), margins, growth rates, quality scores (Piotroski), financial-strength and per-share ratios |
references/ratios-statistics.md |
| Financial-statement line items (income statement, balance sheet, cash flow) and their factor variants; Compustat/FactSet vendor mapping |
references/financials.md |
| Company metadata, prices/dividends/splits as fundamentals, share stats, insider/institutional ownership, short interest, actuals |
references/fundamentals.md |
| Analyst estimates: consensus EPS/sales (CurFY/NextFY/CurQ families), revisions, surprises, recommendations, long-term growth |
references/estimates.md |
| Price/volume indicators: SMA/EMA, RSI, MACD, ADX/DMI, Bollinger, returns (Ret%Chg), 52-week stats, volatility, beta |
references/technical.md |
| Cross-sectional and time-series tools: FRank, FHist, FHistAvg, Aggregate, FMedian/FSum/FCount, Loop functions, linear regression, conditionals |
references/advanced-functions.md |
| Simulated-strategy context: buy/sell rule factors (Rank, RankPos, portfolio state), rule patterns, rebalance idioms |
references/strategy.md |
| Universe-wide aggregates: UnivAvg, UnivCnt, UnivMedian, UnivSum, ... |
references/universe-operations.md |
| Universe definition filters: UnivExclude, UnivSubset, UnivRBICS |
references/universe-filters.md |
| Benchmark series access: BenchClose |
references/benchmark-functions.md |
| Sector/industry classification (RBICS): Sector, IndCode, SubIndustry, sector/industry composites |
references/industry-sector.md |
| ETF taxonomy vocabularies (ETF contexts): asset class, country, region, sector sets |
references/taxonomy.md |
| Math/set/date utilities, InList, GetSeries, macro series IDs (##CPI, FRED mappings), country and universe ID constants, operators |
references/misc.md |
| Replicating a named academic factor (Piotroski F-Score, Novy-Marx gross profitability, Sloan accruals, Beneish M-Score, 12-1 momentum, betting-against-beta, asset growth, net share issuance): exact implementation, node direction and scope, translation traps |
references/factor-replication.md |
| Generating or editing ranking-system XML (mandatory read, see below) |
references/ranking-system-xml.md |
| Running the bundled example scripts (setup, env vars, safety model) |
scripts/README.md |
Formula language essentials
// Variables: SetVar ALWAYS returns TRUE (1), never the assigned value
SetVar(@cheap, PEExclXorTTM < 15) // own rule, then reuse @cheap in later rules
// Returning 1 is what lets a definition be chained into the formula that uses it -
// the only option where there is no next rule (ranking nodes, API formulas):
SetVar(@r, Ret%Chg(252, 21)) * @r * Abs(@r) // 1 * @r * Abs(@r) == @r * Abs(@r)
// A bare SetVar sell rule always fires the sell - negate it: !SetVar(@x, ...).
// @var:expr differs - it RETURNS the value (may be 0/NA) and adds a report column:
MktCap > @med:FMedian("MktCap")
// Math: + - * / and ^ (power). ^ outranks every other operator, so 2*x^3 is 2*(x^3).
// x^y and Pow(x, y) are the same operation - write x^y. There is NO Sqrt: a square
// root is x^0.5, an n-th root x^(1/n) (parens required, since x^1/3 means (x^1)/3).
MktCap^0.5 // square root of market cap; NOT Sqrt(MktCap)
// Conditional: Eval(condition, value_if_true, value_if_false)
Eval(PEExclXorTTM = NA, Pr2SalesTTM < 2, PEExclXorTTM < 20)
// NA handling: IsNA(expr1, expr2) is a REPLACEMENT function (returns expr2 when
// expr1 is NA). It is NOT a one-argument boolean test - test NA with "= NA".
IsNA(DivPSTTM, 0) // dividend per share, 0 when missing
LastSellPrice = NA // boolean: never sold before
// Cross-sectional percentile rank (0-100). Defaults: scope #All, sort #DESC, NAs #InclNA
FRank("PEExclXorTTM", #All, #ASC) > 80 // #ASC: low PE ranks high
// Point-in-time history: value of a formula N weeks ago / averaged samples
FHist("ROE%TTM", 52)
FHistAvg("ROE%TTM", 4, 13)
// Technical: bars are trading days; constants #Year, #Month, #Week are bar counts
Close(0) > SMA(200, 0)
RSI(14) < 30
Ret%Chg(252, 21) // total return over 252 bars, ending 21 bars ago
// Scope aggregates and counterparts
Aggregate("Pr2SalesTTM", #Industry) // methods: #Avg (default), #CapAvg
FMedian("Pr2SalesTTM", #Industry) // median; also FSum, FCount
UnivCnt("PEExclXorTTM < 10") // universe-wide count
// Macro/index series in price functions via series IDs
Close(0, ##CPI)
Close(0, GetSeries("$SP500"))
Line-item pattern (Financials). Every statement line item is one function plus prebuilt
factor variants: Sales(offset, type[, NAHandling]) with type = QTR/ANN/TTM and
NAHandling = FALLBACK (default), KEEPNA, ZERONA; prebuilt variants append period
suffixes to the same base - SalesQ, SalesPQ, SalesPYQ, SalesTTM, SalesPTM, SalesA,
SalesPY, growth SalesGr%TTM/SalesGr%A, per-share SalesPSQ, averages Sales5YAvg,
regressions SalesRegGr%TTM. The same suffix system drives most of the 4,463 factors - see
references/financials.md.
Screens vs rankings. Screen rules are boolean tests evaluated per stock. Ranking systems
combine weighted factor nodes into a 0–100 rank; in a ranking node, "lower is better" controls
direction (e.g. PE), while in FRank the equivalent is the #ASC sort argument.
Critical: do not hallucinate names
The single most common failure mode is inventing plausible-looking factor names. Top
cross-category traps (every "correct" name verified against the extraction dictionary;
per-category tables live in each reference file's Common Mistakes section):
| Wrong (do not use) |
Correct |
Note |
IsNA(x) as boolean |
x = NA |
IsNA(expr1, expr2) is two-argument replacement |
Eval(IsNA(x), a, b) |
Eval(x = NA, a, b) |
same trap inside Eval |
Eval(SetVar(@x, f), A, NA) |
SetVar(@x, f) * A |
SetVar returns TRUE; the NA branch is dead code |
PiotroskiF |
PiotFScore |
Piotroski F-Score |
EstEPSCY / EstEPSCQ |
CurFYEPSMean / CurQEPSMean |
legacy Est... estimate family does not exist |
Revenue |
Sales |
revenue line item |
NetIncome |
NetIncBXor |
net income before extraordinaries |
TotalAssets |
AstTot |
total assets |
FreeCashFlow |
FCF |
free cash flow |
MarketCap |
MktCap |
market capitalization |
EnterpriseVal |
EV |
enterprise value (per-share: EVPS) |
GrossMargin |
GMgn% |
gross margin (e.g. GMgn%TTM) |
CurrentRatio |
CurRatio |
current ratio |
DivYield%TTM |
Yield |
dividend yield takes no period suffix |
EarnYield%TTM |
EarnYield |
yields are current-price figures, no suffix |
PEG |
PEGLT / PEGST |
PEG ratio variants |
AltmanZ |
AltmanZOrig |
also AltmanZPriv, AltmanZNonManu |
LatestRank |
Rank |
current rank in buy/sell rules |
SectorCount |
SecCount |
sector position count |
UnivCount |
UnivCnt |
universe count |
BenchmarkClose |
BenchClose |
benchmark close |
Average / Ln |
Avg / LN |
math function spellings |
Power / Pow(x, y) |
x^y |
^ is the power operator; Pow is the verbose equivalent |
Sqrt(x) / Exp(x) |
x^0.5 / 2.718281828^x |
no root or exp function; ^ is the power operator |
PlusDI / MinusDI |
DMIPlus / DMIMinus |
directional indicators |
IndustryCode |
IndCode |
classification factor |
When unsure about any name, grep the relevant reference file before writing it.
Ranking-system XML: always read the reference first
ALWAYS read references/ranking-system-xml.md before
generating or editing any ranking-system XML. The correct schema is NOT guessable, and
earlier versions of this skill shipped a broken one. That file contains the validated schema,
RankType direction guidance, node-weight semantics (including when Weight="0" is legal), a
worked Penman & Pope example, and a known-formula-errors table. In particular: Weight="0" and an
omitted Weight on a ranking node are legal, never an error to fix - read that file for how the
weight is then split.
API quick start
Credentials come from P123 Account Settings → API (paying subscription required). The bundled
scripts read them from the P123_API_ID / P123_API_KEY environment variables.
import p123api
with p123api.Client(api_id='your api id', api_key='your api key') as client:
try:
# Screen by definition. Long-only screens: rules are plain formulas -
# do NOT add a per-rule 'type' (it is rejected for method 'long').
df = client.screen_run({
'screen': {
'type': 'stock',
'universe': 'SP500',
'method': 'long',
'maxNumHoldings': 25,
'ranking': {'formula': 'PEExclXorTTM', 'lowerIsBetter': True},
'rules': [
{'formula': 'MktCap > 1000'},
{'formula': 'ROE%TTM > 10'},
],
},
'asOfDt': '2026-01-05',
}, to_pandas=True)
# Bulk factor download for research/ML
df2 = client.data_universe({
'universe': 'SP500',
'asOfDts': ['2026-01-05'],
'formulas': ['PEExclXorTTM', 'ROE%TTM', 'MktCap', 'Ret%Chg(252, 21)'],
'includeNames': True,
'precision': 4,
}, to_pandas=True)
except p123api.ClientException as e:
print(e)
Responses carry cost and quotaRemaining - track them; see api.md → Quotas & Costs.
For endpoint-by-endpoint docs, the 44-method wrapper map, AI Factor usage (historical asOfDt
must be a Saturday), and known pitfalls (deprecated includeNodeDetails → nodeDetails,
upload payloads via data=, the per-rule type bug), read
references/api.md.
Runnable examples
scripts/ contains 9 CLI examples built on p123_helpers.py (install:
pip install "p123api[pandas]" - since p123api 3.0 pandas is an extra, not a dependency).
All are read-only except 09_strategy_rebalance_dryrun.py, which only mutates with an explicit
--execute flag plus typed confirmation. Start with 01_auth_check.py, then
02_screen_run.py, 07_price_history.py (defaults to IBM, one of the three tickers named by the
spec's POST /data licence waiver).
Full table and safety model: scripts/README.md.
Verified factor starting points by style
| Style |
Verified factors/functions |
| Value |
PEExclXorTTM, Pr2BookQ, Pr2SalesTTM, Pr2FrCashFlTTM, EV2EBITDATTM, EarnYield, FCFYield |
| Momentum |
Ret%Chg(252, 21), Ret%Chg(231, 21) (12-1), Pr52W%Chg, Pr52WRel%Chg, RSI(14) |
| Quality |
ROE%TTM, ROA%TTM, GMgn%TTM, OpMgn%TTM, PiotFScore |
| Low volatility |
TRSD1YD, PctDev(52, 5), Beta1Y (= BetaFunc(5, 52, 0)) |
| Size / liquidity |
MktCap, AvgDailyTot(63) |
| Growth |
SalesGr%TTM, EBITDAGr%TTM, CurFYEPSMean vs NextFYEPSMean trends |
These are starting points, not constructions. When the user names a published factor - Piotroski,
Novy-Marx gross profitability, Sloan accruals, Beneish, 12-1 momentum, betting-against-beta, asset
growth, net share issuance - read
references/factor-replication.md first: it gives the exact
implementation, the node direction and scope, and the traps that silently build a different factor
(Ret%Chg(252, 21) spans thirteen months, not 12-1; a price multiple ranked lower-is-better
promotes loss-makers to the top).
Working rules for this skill
- Verify every factor/function name in the reference files before using it; never extrapolate
from one name family to another (suffix rules differ by family).
- Read ranking-system-xml.md before any ranking XML work - no exceptions.
- For API parameter names, api.md reflects the wrapper source where the spec disagrees
(e.g. pass
api_id/api_key as strings even though the spec types apiId as integer).
- Period suffixes:
Q, PQ, PYQ, TTM, PTM, A, PY are the core family; growth and
statistical suffixes (Gr%..., RSD%..., RegEst..., ...3YAvg) exist only where a
category file lists them.
- Mutating API operations (rebalance commits, uploads, deletes) require explicit user intent;
default to dry runs (see scripts/README.md safety model).
Developed and maintained by Quant Solvings, a boutique quantitative
practice in factor investing for equities, run by Carlos Morales, a Verified Portfolio123 Coach
and Consultant.
1---2name: portfolio1233description: Comprehensive, extraction-verified reference for Portfolio123 (P123), the systematic equity research platform: factor investing, stock screening, ranking systems, backtesting, and the REST API with the p123api Python wrapper. Use this skill whenever the user mentions Portfolio123, P123, p123api, P123 screens or screen rules, ranking system XML, simulated strategies, buy/sell rules, or any P123 formula syntax such as Close(0), FRank, FHist, MktCap, PEExclXorTTM, ROE%TTM, SetVar, or Eval. Also use it when the user wants to write or debug P123 formulas, build or fix ranking systems, construct screens or universes, replicate academic factor strategies (value, momentum, quality, low volatility) on P123, or pull P123 data programmatically (screen_run, screen_backtest, rank_ranks, data_universe, AI Factor predictions). Covers all 4,463 factors and 465 functions of the official Factor Reference in 13 category files, plus the full REST API (39 operations) and 9 runnable example scripts.4license: MIT5---67<!-- name-whitelist: NAHandling P123_API_ID P123_API_KEY quotaRemaining asOfDt8includeNodeDetails nodeDetails apiId Q PQ PYQ PTM A PY screen_run screen_backtest9rank_ranks data_universe aifactor_predict to_pandas api_id api_key ClientException -->10# Portfolio123 (P123)1112Portfolio123 is a web platform for systematic equity research: screening, multi-factor ranking13systems, strategy simulation/backtesting, and a REST API. Its formula language (not Python) is14used in screen rules, ranking-system nodes, buy/sell rules, and API data pulls.1516Everything in this skill was extracted from the official P123 Factor Reference on 2026-06-0917(4,463 factors and 465 functions across 13 categories, plus 473 constants/series-IDs/operators)18and from the official OpenAPI spec and `p123api` wrapper source. **Never invent a factor or19function name** - if a name is not in these reference files, assume it does not exist and look20up the correct one.2122## Which reference file to read2324Read only what the task needs:2526| Task | Read |27|---|---|28| REST API or `p123api` Python calls (auth, screen_run, backtests, rank_ranks, data_universe, uploads, AI Factor) | [references/api.md](references/api.md) |29| Valuation ratios (PE, EV/EBITDA, price-to-X), margins, growth rates, quality scores (Piotroski), financial-strength and per-share ratios | [references/ratios-statistics.md](references/ratios-statistics.md) |30| Financial-statement line items (income statement, balance sheet, cash flow) and their factor variants; Compustat/FactSet vendor mapping | [references/financials.md](references/financials.md) |31| Company metadata, prices/dividends/splits as fundamentals, share stats, insider/institutional ownership, short interest, actuals | [references/fundamentals.md](references/fundamentals.md) |32| Analyst estimates: consensus EPS/sales (CurFY/NextFY/CurQ families), revisions, surprises, recommendations, long-term growth | [references/estimates.md](references/estimates.md) |33| Price/volume indicators: SMA/EMA, RSI, MACD, ADX/DMI, Bollinger, returns (Ret%Chg), 52-week stats, volatility, beta | [references/technical.md](references/technical.md) |34| Cross-sectional and time-series tools: FRank, FHist, FHistAvg, Aggregate, FMedian/FSum/FCount, Loop functions, linear regression, conditionals | [references/advanced-functions.md](references/advanced-functions.md) |35| Simulated-strategy context: buy/sell rule factors (Rank, RankPos, portfolio state), rule patterns, rebalance idioms | [references/strategy.md](references/strategy.md) |36| Universe-wide aggregates: UnivAvg, UnivCnt, UnivMedian, UnivSum, ... | [references/universe-operations.md](references/universe-operations.md) |37| Universe definition filters: UnivExclude, UnivSubset, UnivRBICS | [references/universe-filters.md](references/universe-filters.md) |38| Benchmark series access: BenchClose | [references/benchmark-functions.md](references/benchmark-functions.md) |39| Sector/industry classification (RBICS): Sector, IndCode, SubIndustry, sector/industry composites | [references/industry-sector.md](references/industry-sector.md) |40| ETF taxonomy vocabularies (ETF contexts): asset class, country, region, sector sets | [references/taxonomy.md](references/taxonomy.md) |41| Math/set/date utilities, InList, GetSeries, macro series IDs (##CPI, FRED mappings), country and universe ID constants, operators | [references/misc.md](references/misc.md) |42| Replicating a named academic factor (Piotroski F-Score, Novy-Marx gross profitability, Sloan accruals, Beneish M-Score, 12-1 momentum, betting-against-beta, asset growth, net share issuance): exact implementation, node direction and scope, translation traps | [references/factor-replication.md](references/factor-replication.md) |43| **Generating or editing ranking-system XML** (mandatory read, see below) | [references/ranking-system-xml.md](references/ranking-system-xml.md) |44| Running the bundled example scripts (setup, env vars, safety model) | [scripts/README.md](scripts/README.md) |4546## Formula language essentials4748```p12349// Variables: SetVar ALWAYS returns TRUE (1), never the assigned value50SetVar(@cheap, PEExclXorTTM < 15) // own rule, then reuse @cheap in later rules51// Returning 1 is what lets a definition be chained into the formula that uses it -52// the only option where there is no next rule (ranking nodes, API formulas):53SetVar(@r, Ret%Chg(252, 21)) * @r * Abs(@r) // 1 * @r * Abs(@r) == @r * Abs(@r)54// A bare SetVar sell rule always fires the sell - negate it: !SetVar(@x, ...).55// @var:expr differs - it RETURNS the value (may be 0/NA) and adds a report column:56MktCap > @med:FMedian("MktCap")5758// Math: + - * / and ^ (power). ^ outranks every other operator, so 2*x^3 is 2*(x^3).59// x^y and Pow(x, y) are the same operation - write x^y. There is NO Sqrt: a square60// root is x^0.5, an n-th root x^(1/n) (parens required, since x^1/3 means (x^1)/3).61MktCap^0.5 // square root of market cap; NOT Sqrt(MktCap)6263// Conditional: Eval(condition, value_if_true, value_if_false)64Eval(PEExclXorTTM = NA, Pr2SalesTTM < 2, PEExclXorTTM < 20)6566// NA handling: IsNA(expr1, expr2) is a REPLACEMENT function (returns expr2 when67// expr1 is NA). It is NOT a one-argument boolean test - test NA with "= NA".68IsNA(DivPSTTM, 0) // dividend per share, 0 when missing69LastSellPrice = NA // boolean: never sold before7071// Cross-sectional percentile rank (0-100). Defaults: scope #All, sort #DESC, NAs #InclNA72FRank("PEExclXorTTM", #All, #ASC) > 80 // #ASC: low PE ranks high7374// Point-in-time history: value of a formula N weeks ago / averaged samples75FHist("ROE%TTM", 52)76FHistAvg("ROE%TTM", 4, 13)7778// Technical: bars are trading days; constants #Year, #Month, #Week are bar counts79Close(0) > SMA(200, 0)80RSI(14) < 3081Ret%Chg(252, 21) // total return over 252 bars, ending 21 bars ago8283// Scope aggregates and counterparts84Aggregate("Pr2SalesTTM", #Industry) // methods: #Avg (default), #CapAvg85FMedian("Pr2SalesTTM", #Industry) // median; also FSum, FCount86UnivCnt("PEExclXorTTM < 10") // universe-wide count8788// Macro/index series in price functions via series IDs89Close(0, ##CPI)90Close(0, GetSeries("$SP500"))91```9293**Line-item pattern (Financials).** Every statement line item is one function plus prebuilt94factor variants: `Sales(offset, type[, NAHandling])` with `type` = `QTR`/`ANN`/`TTM` and95`NAHandling` = `FALLBACK` (default), `KEEPNA`, `ZERONA`; prebuilt variants append period96suffixes to the same base - `SalesQ`, `SalesPQ`, `SalesPYQ`, `SalesTTM`, `SalesPTM`, `SalesA`,97`SalesPY`, growth `SalesGr%TTM`/`SalesGr%A`, per-share `SalesPSQ`, averages `Sales5YAvg`,98regressions `SalesRegGr%TTM`. The same suffix system drives most of the 4,463 factors - see99[references/financials.md](references/financials.md).100101**Screens vs rankings.** Screen rules are boolean tests evaluated per stock. Ranking systems102combine weighted factor nodes into a 0–100 rank; in a ranking node, "lower is better" controls103direction (e.g. PE), while in `FRank` the equivalent is the `#ASC` sort argument.104105## Critical: do not hallucinate names106107The single most common failure mode is inventing plausible-looking factor names. Top108cross-category traps (every "correct" name verified against the extraction dictionary;109per-category tables live in each reference file's Common Mistakes section):110111| Wrong (do not use) | Correct | Note |112|---|---|---|113| `IsNA(x)` as boolean | `x = NA` | `IsNA(expr1, expr2)` is two-argument replacement |114| `Eval(IsNA(x), a, b)` | `Eval(x = NA, a, b)` | same trap inside Eval |115| `Eval(SetVar(@x, f), A, NA)` | `SetVar(@x, f) * A` | `SetVar` returns TRUE; the `NA` branch is dead code |116| `PiotroskiF` | `PiotFScore` | Piotroski F-Score |117| `EstEPSCY` / `EstEPSCQ` | `CurFYEPSMean` / `CurQEPSMean` | legacy Est... estimate family does not exist |118| `Revenue` | `Sales` | revenue line item |119| `NetIncome` | `NetIncBXor` | net income before extraordinaries |120| `TotalAssets` | `AstTot` | total assets |121| `FreeCashFlow` | `FCF` | free cash flow |122| `MarketCap` | `MktCap` | market capitalization |123| `EnterpriseVal` | `EV` | enterprise value (per-share: `EVPS`) |124| `GrossMargin` | `GMgn%` | gross margin (e.g. `GMgn%TTM`) |125| `CurrentRatio` | `CurRatio` | current ratio |126| `DivYield%TTM` | `Yield` | dividend yield takes no period suffix |127| `EarnYield%TTM` | `EarnYield` | yields are current-price figures, no suffix |128| `PEG` | `PEGLT` / `PEGST` | PEG ratio variants |129| `AltmanZ` | `AltmanZOrig` | also `AltmanZPriv`, `AltmanZNonManu` |130| `LatestRank` | `Rank` | current rank in buy/sell rules |131| `SectorCount` | `SecCount` | sector position count |132| `UnivCount` | `UnivCnt` | universe count |133| `BenchmarkClose` | `BenchClose` | benchmark close |134| `Average` / `Ln` | `Avg` / `LN` | math function spellings |135| `Power` / `Pow(x, y)` | `x^y` | `^` is the power operator; `Pow` is the verbose equivalent |136| `Sqrt(x)` / `Exp(x)` | `x^0.5` / `2.718281828^x` | no root or exp function; `^` is the power operator |137| `PlusDI` / `MinusDI` | `DMIPlus` / `DMIMinus` | directional indicators |138| `IndustryCode` | `IndCode` | classification factor |139140When unsure about any name, grep the relevant reference file before writing it.141142## Ranking-system XML: always read the reference first143144**ALWAYS read [references/ranking-system-xml.md](references/ranking-system-xml.md) before145generating or editing any ranking-system XML.** The correct schema is NOT guessable, and146earlier versions of this skill shipped a broken one. That file contains the validated schema,147RankType direction guidance, node-weight semantics (including when `Weight="0"` is legal), a148worked Penman & Pope example, and a known-formula-errors table. In particular: `Weight="0"` and an149omitted `Weight` on a ranking node are legal, never an error to fix - read that file for how the150weight is then split.151152## API quick start153154Credentials come from P123 Account Settings → API (paying subscription required). The bundled155scripts read them from the `P123_API_ID` / `P123_API_KEY` environment variables.156157```python158import p123api159160with p123api.Client(api_id='your api id', api_key='your api key') as client:161 try:162 # Screen by definition. Long-only screens: rules are plain formulas -163 # do NOT add a per-rule 'type' (it is rejected for method 'long').164 df = client.screen_run({165 'screen': {166 'type': 'stock',167 'universe': 'SP500',168 'method': 'long',169 'maxNumHoldings': 25,170 'ranking': {'formula': 'PEExclXorTTM', 'lowerIsBetter': True},171 'rules': [172 {'formula': 'MktCap > 1000'},173 {'formula': 'ROE%TTM > 10'},174 ],175 },176 'asOfDt': '2026-01-05',177 }, to_pandas=True)178179 # Bulk factor download for research/ML180 df2 = client.data_universe({181 'universe': 'SP500',182 'asOfDts': ['2026-01-05'],183 'formulas': ['PEExclXorTTM', 'ROE%TTM', 'MktCap', 'Ret%Chg(252, 21)'],184 'includeNames': True,185 'precision': 4,186 }, to_pandas=True)187 except p123api.ClientException as e:188 print(e)189```190191Responses carry `cost` and `quotaRemaining` - track them; see api.md → Quotas & Costs.192For endpoint-by-endpoint docs, the 44-method wrapper map, AI Factor usage (historical `asOfDt`193must be a Saturday), and known pitfalls (deprecated `includeNodeDetails` → `nodeDetails`,194upload payloads via `data=`, the per-rule `type` bug), read195[references/api.md](references/api.md).196197## Runnable examples198199`scripts/` contains 9 CLI examples built on `p123_helpers.py` (install:200`pip install "p123api[pandas]"` - since p123api 3.0 pandas is an extra, not a dependency).201All are read-only except `09_strategy_rebalance_dryrun.py`, which only mutates with an explicit202`--execute` flag plus typed confirmation. Start with `01_auth_check.py`, then203`02_screen_run.py`, `07_price_history.py` (defaults to IBM, one of the three tickers named by the204spec's `POST /data` licence waiver).205Full table and safety model: [scripts/README.md](scripts/README.md).206207## Verified factor starting points by style208209| Style | Verified factors/functions |210|---|---|211| Value | `PEExclXorTTM`, `Pr2BookQ`, `Pr2SalesTTM`, `Pr2FrCashFlTTM`, `EV2EBITDATTM`, `EarnYield`, `FCFYield` |212| Momentum | `Ret%Chg(252, 21)`, `Ret%Chg(231, 21)` (12-1), `Pr52W%Chg`, `Pr52WRel%Chg`, `RSI(14)` |213| Quality | `ROE%TTM`, `ROA%TTM`, `GMgn%TTM`, `OpMgn%TTM`, `PiotFScore` |214| Low volatility | `TRSD1YD`, `PctDev(52, 5)`, `Beta1Y` (= `BetaFunc(5, 52, 0)`) |215| Size / liquidity | `MktCap`, `AvgDailyTot(63)` |216| Growth | `SalesGr%TTM`, `EBITDAGr%TTM`, `CurFYEPSMean` vs `NextFYEPSMean` trends |217218These are starting points, not constructions. When the user names a published factor - Piotroski,219Novy-Marx gross profitability, Sloan accruals, Beneish, 12-1 momentum, betting-against-beta, asset220growth, net share issuance - read221[references/factor-replication.md](references/factor-replication.md) first: it gives the exact222implementation, the node direction and scope, and the traps that silently build a different factor223(`Ret%Chg(252, 21)` spans thirteen months, not 12-1; a price multiple ranked lower-is-better224promotes loss-makers to the top).225226## Working rules for this skill2272281. Verify every factor/function name in the reference files before using it; never extrapolate229 from one name family to another (suffix rules differ by family).2302. Read ranking-system-xml.md before any ranking XML work - no exceptions.2313. For API parameter names, api.md reflects the wrapper source where the spec disagrees232 (e.g. pass `api_id`/`api_key` as strings even though the spec types `apiId` as integer).2334. Period suffixes: `Q`, `PQ`, `PYQ`, `TTM`, `PTM`, `A`, `PY` are the core family; growth and234 statistical suffixes (`Gr%...`, `RSD%...`, `RegEst...`, `...3YAvg`) exist only where a235 category file lists them.2365. Mutating API operations (rebalance commits, uploads, deletes) require explicit user intent;237 default to dry runs (see scripts/README.md safety model).238239---240241Developed and maintained by [Quant Solvings](https://quantsolvings.com), a boutique quantitative242practice in factor investing for equities, run by Carlos Morales, a Verified Portfolio123 Coach243and Consultant.