# 完整示例
双均线策略:
def init(context):
context.stock = "000001.SZ"
set_benchmark("000300.SH")
set_commission(open_commission=0.0003, close_commission=0.0003, close_tax=0.001)
run_daily(trade, time_rule="every_bar")
def trade(context, bar_dict):
hist = attribute_history(context.stock, 20, "1d", ["close"])
if len(hist) < 20:
return
close = hist["close"]
ma5 = close.tail(5).mean()
ma20 = close.tail(20).mean()
price = bar_dict[context.stock].close
has_position = context.stock in context.portfolio.positions
if ma5 > ma20 and not has_position:
order_value(context.stock, context.portfolio.available_cash)
log.info("buy %s price=%s", context.stock, price)
elif ma5 < ma20 and has_position:
order_target(context.stock, 0)
log.info("sell %s price=%s", context.stock, price)
record(price=price, ma5=ma5, ma20=ma20)
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
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
因子轮动策略:
FACTOR_IDS = ["momentum_20d", "volume_ratio"]
DEFAULT_INDEXES = ["000300.SH"]
TOP_N = 10
LOOKBACK = 320
REBALANCE_DAYS = 20
BENCHMARK = "000300.SH"
def _n(x):
try:
y = float(x)
if y == y and abs(y) < 1e100:
return y
except Exception:
pass
return None
def _v(f, c):
try:
raw = f[c].tolist()
except Exception:
try:
raw = list(f[c])
except Exception:
raw = []
return [_n(x) for x in raw if _n(x) is not None]
def _ma(a, n):
return sum(a[-n:]) / float(n) if len(a) >= n else None
def _ret(c, n):
return c[-1] / c[-n - 1] - 1.0 if len(c) > n and c[-n - 1] else None
def _div(a, b):
return None if a is None or b in (None, 0) else a / b
def _universe():
r, s = [], set()
for idx in DEFAULT_INDEXES:
try:
items = get_stock_list_in_sector(idx, block_type=0)
except Exception:
items = []
for x in items or []:
y = str(x)
if y and y not in s:
s.add(y)
r.append(y)
return r or ["000300.SH"]
def _calc(fid, c, v):
if not c:
return None
if fid.startswith("momentum_") and fid.endswith("d"):
return _ret(c, int(fid.split("_")[1][:-1]))
if fid == "volume_ratio":
return _div(v[-1] if v else None, _ma(v, 20))
return None
def _rank(rows):
for fid in FACTOR_IDS:
vv = [x for x in rows if x.get(fid) is not None]
vv.sort(key=lambda x: x.get(fid))
n = len(vv)
for i, x in enumerate(vv, 1):
x[fid + "_rank"] = i / float(n)
for x in rows:
vals = [x.get(fid + "_rank") for fid in FACTOR_IDS if x.get(fid + "_rank") is not None]
x["factor_score"] = sum(vals) / float(len(vals)) if vals else None
def init(context):
context.rebalance_counter = 0
context.current_targets = []
context.stock = _universe()
run_daily(check, time_rule="every_bar")
set_benchmark(BENCHMARK)
def check(context, bar_dict):
context.rebalance_counter += 1
if context.rebalance_counter % REBALANCE_DAYS != 1:
return
universe = list(context.stock) if context.stock else _universe()
if not universe:
return
data = get_bars_batch(universe, count=LOOKBACK, fields=["close", "volume"], frequency="1d")
rows = []
for sym, frame in data.items():
c = _v(frame, "close")
v = _v(frame, "volume")
row = {"symbol": sym, "close": c[-1] if c else None}
for fid in FACTOR_IDS:
row[fid] = _calc(fid, c, v)
rows.append(row)
_rank(rows)
rows.sort(key=lambda x: x.get("factor_score") if x.get("factor_score") is not None else -1e100, reverse=True)
selected = [x.get("symbol") for x in rows[:TOP_N] if x.get("symbol")]
w = 1.0 / float(len(selected)) if selected else 0.0
# ── 先卖:不在新列表里的旧持仓 ──
for old in context.current_targets:
if old not in selected:
order_target_percent(old, 0)
log.info(f"【卖出】{old}")
# ── 再买:新列表 ──
for sym in selected:
order_target_percent(sym, w)
log.info(f"【买入】{sym} 目标权重={w:.2%}")
context.current_targets = selected
def after_trading(context):
log.info("盘后运行结束")
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125