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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| import numpy as np from scipy.optimize import linprog
signal = np.array([1.0, 0.8, 0.5, 0.2, 0.1, 0.0]) industry_x = np.array([True, True, True, False, False, False]) previous = np.repeat(1 / 6, 6) benchmark = np.repeat(1 / 6, 6) future_return = np.array([0.03, 0.01, -0.01, 0.02, 0.00, -0.02])
stock_cap = 0.35 industry_limit = 0.10 turnover_limit = 0.30
n = len(signal) c = np.r_[-signal, np.zeros(n)] a_ub = [] b_ub = [] for i in range(n): row_positive = np.zeros(2 * n) row_positive[i] = 1 row_positive[n + i] = -1 a_ub.append(row_positive) b_ub.append(previous[i])
row_negative = np.zeros(2 * n) row_negative[i] = -1 row_negative[n + i] = -1 a_ub.append(row_negative) b_ub.append(-previous[i])
turnover_row = np.r_[np.zeros(n), 0.5 * np.ones(n)] a_ub.append(turnover_row) b_ub.append(turnover_limit)
industry_row = np.r_[industry_x.astype(float), np.zeros(n)] a_ub.extend([industry_row, -industry_row]) b_ub.extend([0.5 + industry_limit, -(0.5 - industry_limit)])
a_eq = np.r_[np.ones(n), np.zeros(n)].reshape(1, -1) result = linprog( c, A_ub=np.asarray(a_ub), b_ub=np.asarray(b_ub), A_eq=a_eq, b_eq=np.array([1.0]), bounds=[(0.0, stock_cap)] * n + [(0.0, None)] * n, method="highs", ) weights = result.x[:n]
trade = weights - previous buy = np.clip(trade, 0, None).sum() sell = np.clip(-trade, 0, None).sum() one_way_turnover = 0.5 * np.abs(trade).sum()
commission = 0.0003 stamp_tax = 0.0005 slippage = 0.0010 cost = ( commission * (buy + sell) + stamp_tax * sell + slippage * (buy + sell) ) gross = weights @ future_return net = gross - cost
tol = 1e-8 industry_x_weight = weights[industry_x].sum() assert result.success assert np.isclose(weights.sum(), 1.0, atol=tol) assert np.all(weights >= -tol) assert np.all(weights <= stock_cap + tol) assert 0.5 - industry_limit - tol <= industry_x_weight assert industry_x_weight <= 0.5 + industry_limit + tol assert one_way_turnover <= turnover_limit + tol assert net <= gross + tol
print(weights.round(6)) print(gross, cost, net, one_way_turnover, industry_x_weight)
|