"""合成因子截面、分组与独立现金账;不使用历史行情。""" import json from math import isclose from statistics import correlation, mean def ranks(values): return [mean(i + 1 for i, x in enumerate(sorted(values)) if x == v) for v in values] def evaluate(rows): valid = [r for r in rows if r['score'] is not None and r['return'] is not None] if len(valid) < 5: return {'status': 'skip', 'n': len(valid)} ordered = sorted(valid, key=lambda r: (r['score'], r['symbol'])) groups = [ordered[i:i + 2] for i in range(0, len(ordered), 2)] score_ranks = ranks([r['score'] for r in valid]) return_ranks = ranks([r['return'] for r in valid]) ic = (correlation(score_ranks, return_ranks) if len(set(score_ranks)) > 1 and len(set(return_ranks)) > 1 else None) return {'status': 'ok', 'n': len(valid), 'ic': ic, 'groups': [[r['symbol'] for r in g] for g in groups], 'group_returns': [mean(r['return'] for r in g) for g in groups]} rows = [{'symbol': s, 'score': i, 'return': i / 100} for i, s in enumerate('ABCDEF', 1)] report = evaluate(rows) assert report['groups'] == [['A', 'B'], ['C', 'D'], ['E', 'F']] assert isclose(report['ic'], 1) cash = 1000 ledger = [] for symbol, exit_price in [('E', 10.5), ('F', 10.6)]: cash -= 40 * 10 + 1 ledger.append([symbol, 'buy', 40, 10, 1]) for symbol, exit_price in [('E', 10.5), ('F', 10.6)]: cash += 40 * exit_price - 1 ledger.append([symbol, 'sell', 40, exit_price, 1]) assert isclose(cash, 1040) assert evaluate(rows[:4]) == {'status': 'skip', 'n': 4} assert isclose(report['group_returns'][-1], .055) assert evaluate([dict(row, score=1) for row in rows])['ic'] is None assert evaluate(list(reversed(rows)))['groups'] == report['groups'] print(json.dumps({'factor': report, 'orders': ledger, 'ending_cash': cash, 'net_return': cash / 1000 - 1, 'four_rows': evaluate(rows[:4])}))