File size: 1,308 Bytes
590a501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""Portfolio construction from model scores."""

from __future__ import annotations

import pandas as pd


def top_k_equal_weight(scores: pd.DataFrame, k: int = 30) -> pd.DataFrame:
    """
    Build equal-weight long-only portfolio from cross-sectional scores.
    Input: MultiIndex (instrument, datetime) with score column or Series.
    """
    if isinstance(scores, pd.Series):
        scores = scores.to_frame("score")

    weights = []
    for dt, group in scores.groupby(level="datetime"):
        top = group.nlargest(k, "score")
        w = pd.Series(1.0 / len(top), index=top.index)
        weights.append(w)
    return pd.concat(weights).to_frame("weight")


def long_short_quantile(scores: pd.DataFrame, n_groups: int = 5) -> pd.DataFrame:
    weights = []
    for dt, group in scores.groupby(level="datetime"):
        group = group.copy()
        group["group"] = pd.qcut(group["score"].rank(method="first"), n_groups, labels=False)
        long = group[group["group"] == n_groups - 1]
        short = group[group["group"] == 0]
        w = pd.Series(0.0, index=group.index)
        if len(long):
            w.loc[long.index] = 0.5 / len(long)
        if len(short):
            w.loc[short.index] = -0.5 / len(short)
        weights.append(w.to_frame("weight"))
    return pd.concat(weights)