lex_by_file( FileName ) :-
get_input_from_file( FileName, TokenList ),
lex( TokenList, OutputList ),
%write_output( OutputList ), !.
maplist(writeln,OutputList),!.
%% atom_number(atom, number)
lex([],[]).
lex(['int' | T], ['TYPE: int' | R]) :- lex(T, R).
lex(['bool' | T], ['TYPE: bool'] | R) :- lex(T, R).
lex([',' | T], ['COMMA: ,'] | R) :- lex(T, R).
lex([X | T], [X | R]) :- lex(T, R).
bGV4X2J5X2ZpbGUoIEZpbGVOYW1lICkgOi0KCWdldF9pbnB1dF9mcm9tX2ZpbGUoIEZpbGVOYW1lLCBUb2tlbkxpc3QgKSwKCWxleCggVG9rZW5MaXN0LCBPdXRwdXRMaXN0ICksCgkld3JpdGVfb3V0cHV0KCBPdXRwdXRMaXN0ICksICEuCgltYXBsaXN0KHdyaXRlbG4sT3V0cHV0TGlzdCksIS4KCiUlIGF0b21fbnVtYmVyKGF0b20sIG51bWJlcikKbGV4KFtdLFtdKS4KbGV4KFsnaW50JyB8IFRdLCBbJ1RZUEU6IGludCcgfCBSXSkgOi0gIGxleChULCBSKS4KbGV4KFsnYm9vbCcgfCBUXSwgWydUWVBFOiBib29sJ10gfCBSKSA6LSBsZXgoVCwgUikuCmxleChbJywnIHwgVF0sIFsnQ09NTUE6ICwnXSB8IFIpIDotIGxleChULCBSKS4KbGV4KFtYIHwgVF0sIFtYIHwgUl0pIDotIGxleChULCBSKS4K
aW1wb3J0IHlmaW5hbmNlIGFzIHlmCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKCiMgMS4g5Z+656GA6K6+572uCnRpY2tlcnMgPSBbJ1NQWScsICdFV0gnLCAnQVNIUicsICdCVEMtVVNEJywgJ0dMRCcsICdBR0cnXQojIOS9oOeahOagh+WHhumFjee9riAoQmFzZSBXZWlnaHRzKQpiYXNlX3dlaWdodHMgPSB7CiAgICAnU1BZJzogMC4zMCwgJ0VXSCc6IDAuMjUsICdBU0hSJzogMC4xMCwgCiAgICAnQlRDLVVTRCc6IDAuMDUsICdHTEQnOiAwLjEwLCAnQUdHJzogMC4yMAp9CmluaXRpYWxfY2FwaXRhbCA9IDcwMDAwMDAKCiMgMi4g6I635Y+W5pWw5o2uICjov4fljrsgMTAg5bm0KQpwcmludCgmcXVvdDvmraPlnKjkuIvovb3mlbDmja7lubbmnoTlu7rnvZfnm5guLi4mcXVvdDspCmRhdGEgPSB5Zi5kb3dubG9hZCh0aWNrZXJzLCBzdGFydD0mcXVvdDsyMDE0LTAxLTAxJnF1b3Q7LCBlbmQ9JnF1b3Q7MjAyNS0wMS0wMSZxdW90OylbJ0FkaiBDbG9zZSddLmRyb3BuYSgpCgojIDMuIOiuoeeul+aguOW/g+aMh+aghyAo572X55uY55qE5oyH6ZKIKQojIERhbGlvIEF4aXM6IOi2i+WKvyAoTUEyMDApCm1hMjAwID0gZGF0YS5yb2xsaW5nKHdpbmRvdz0yMDApLm1lYW4oKQoKIyBNYXJrcyBBeGlzOiDmg4Xnu6ogKFJTSSAxNCkKZGVsdGEgPSBkYXRhLmRpZmYoKQpnYWluID0gZGVsdGEud2hlcmUoZGVsdGEgJmd0OyAwLCAwKS5yb2xsaW5nKHdpbmRvdz0xNCkubWVhbigpCmxvc3MgPSAtZGVsdGEud2hlcmUoZGVsdGEgJmx0OyAwLCAwKS5yb2xsaW5nKHdpbmRvdz0xNCkubWVhbigpCnJzID0gZ2FpbiAvIGxvc3MKcnNpID0gMTAwIC0gKDEwMCAvICgxICsgcnMpKQpyc2kgPSByc2kuZmlsbG5hKDUwKSAgIyDloavlhYUgTmFOCgojIOmHjeaWsOe0ouW8leW5tuWJjeWQkeWhq+WFhe+8jOehruS/neS4jiBkYXRhIOWvuem9kAptYTIwMCA9IG1hMjAwLnJlaW5kZXgoZGF0YS5pbmRleCkuZmZpbGwoKQpyc2kgPSByc2kucmVpbmRleChkYXRhLmluZGV4KS5mZmlsbCgpCgojIDQuIOWbnua1i+W+queOrwpwb3J0Zm9saW9fdmFsdWUgPSBbXQpjYXNoID0gaW5pdGlhbF9jYXBpdGFsCnNoYXJlcyA9IHt0OiAwIGZvciB0IGluIHRpY2tlcnN9CgojIOiOt+WPluaciOacq+aXpeacnwptb250aGx5X2RhdGVzID0gZGF0YS5yZXNhbXBsZSgnTScpLmxhc3QoKS5pbmRleC5ub3JtYWxpemUoKQpkYXRhX2luZGV4X25vcm1hbGl6ZWQgPSBkYXRhLmluZGV4Lm5vcm1hbGl6ZSgpCgpmb3IgZGF0ZSBpbiBkYXRhLmluZGV4OgogICAgIyDlvZPliY3mipXotYTnu4TlkIjku7flgLwKICAgIGN1cnJlbnRfdmFsdWUgPSBjYXNoICsgc3VtKHNoYXJlc1t0XSAqIGRhdGEubG9jW2RhdGUsIHRdIGZvciB0IGluIHRpY2tlcnMpCiAgICBwb3J0Zm9saW9fdmFsdWUuYXBwZW5kKGN1cnJlbnRfdmFsdWUpCgogICAgIyDku4XlnKjmnIjlupXov5vooYznvZfnm5jmo4Dmn6XlkozosIPku5MKICAgIGlmIGRhdGUubm9ybWFsaXplKCkgaW4gbW9udGhseV9kYXRlczoKICAgICAgICB0YXJnZXRfd2VpZ2h0cyA9IGJhc2Vfd2VpZ2h0cy5jb3B5KCkKCiAgICAgICAgZm9yIHQgaW4gWydTUFknLCAnRVdIJywgJ0FTSFInLCAnQlRDLVVTRCddOiAgIyDku4Xpkojlr7npo47pmanotYTkuqcKICAgICAgICAgICAgaWYgZGF0ZSBub3QgaW4gbWEyMDAuaW5kZXggb3IgZGF0ZSBub3QgaW4gcnNpLmluZGV4OgogICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgcHJpY2UgPSBkYXRhLmxvY1tkYXRlLCB0XQogICAgICAgICAgICBtYSA9IG1hMjAwLmxvY1tkYXRlLCB0XQogICAgICAgICAgICByX3ZhbCA9IHJzaS5sb2NbZGF0ZSwgdF0KCiAgICAgICAgICAgICMg6Lez6L+HIE5hTgogICAgICAgICAgICBpZiBwZC5pc25hKG1hKSBvciBwZC5pc25hKHJfdmFsKToKICAgICAgICAgICAgICAgIHRhcmdldF93ZWlnaHRzW3RdID0gYmFzZV93ZWlnaHRzW3RdCiAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgaXNfZXhwYW5zaW9uID0gcHJpY2UgJmd0OyBtYQogICAgICAgICAgICBpc19mZWFyID0gcl92YWwgJmx0OyAzNQogICAgICAgICAgICBpc19ncmVlZCA9IHJfdmFsICZndDsgNzUKCiAgICAgICAgICAgICMgUTI6IOe7neacm+WMuiAo5oqE5bqVKQogICAgICAgICAgICBpZiBub3QgaXNfZXhwYW5zaW9uIGFuZCBpc19mZWFyOgogICAgICAgICAgICAgICAgdGFyZ2V0X3dlaWdodHNbdF0gPSBiYXNlX3dlaWdodHNbdF0gKiAxLjIKICAgICAgICAgICAgIyBRMTog5rOh5rKr5Yy6ICjmraLnm4gpCiAgICAgICAgICAgIGVsaWYgaXNfZXhwYW5zaW9uIGFuZCBpc19ncmVlZDoKICAgICAgICAgICAgICAgIHRhcmdldF93ZWlnaHRzW3RdID0gYmFzZV93ZWlnaHRzW3RdICogMC41CiAgICAgICAgICAgICMgUTQ6IOW0qea6gy/lhqzol4/ljLogKOatouaNnykKICAgICAgICAgICAgZWxpZiBub3QgaXNfZXhwYW5zaW9uIGFuZCBub3QgaXNfZmVhcjoKICAgICAgICAgICAgICAgIHRhcmdldF93ZWlnaHRzW3RdID0gMC4wCiAgICAgICAgICAgICMgUTM6IOi2i+WKv+WMuiAo5oyB5pyJKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgdGFyZ2V0X3dlaWdodHNbdF0gPSBiYXNlX3dlaWdodHNbdF0KCiAgICAgICAgIyDlj6/pgInvvJrmo4Dmn6XmgLvmnYPph43mmK/lkKbotoXov4cgMS4w77yM6L+Z6YeM5oiR5Lus5YWB6K64546w6YeR5ZC45pS2CiAgICAgICAgdG90YWxfYXNzZXRzID0gY2FzaCArIHN1bShzaGFyZXNbdF0gKiBkYXRhLmxvY1tkYXRlLCB0XSBmb3IgdCBpbiB0aWNrZXJzKQogICAgICAgIGZvciB0IGluIHRpY2tlcnM6CiAgICAgICAgICAgIHRhcmdldF9hbXQgPSB0b3RhbF9hc3NldHMgKiB0YXJnZXRfd2VpZ2h0cy5nZXQodCwgYmFzZV93ZWlnaHRzW3RdKQogICAgICAgICAgICBzaGFyZXNbdF0gPSB0YXJnZXRfYW10IC8gZGF0YS5sb2NbZGF0ZSwgdF0KCiAgICAgICAgIyDmm7TmlrDnjrDph5EKICAgICAgICBpbnZlc3RlZCA9IHN1bShzaGFyZXNbdF0gKiBkYXRhLmxvY1tkYXRlLCB0XSBmb3IgdCBpbiB0aWNrZXJzKQogICAgICAgIGNhc2ggPSB0b3RhbF9hc3NldHMgLSBpbnZlc3RlZAoKIyA1LiDnu5PmnpzorqHnrpfkuI7lsZXnpLoKcG9ydGZvbGlvX3NlcmllcyA9IHBkLlNlcmllcyhwb3J0Zm9saW9fdmFsdWUsIGluZGV4PWRhdGEuaW5kZXgpCnRvdGFsX3llYXJzID0gKHBvcnRmb2xpb19zZXJpZXMuaW5kZXhbLTFdIC0gcG9ydGZvbGlvX3Nlcmllcy5pbmRleFswXSkuZGF5cyAvIDM2NS4yNQpjYWdyID0gKHBvcnRmb2xpb19zZXJpZXMuaWxvY1stMV0gLyBpbml0aWFsX2NhcGl0YWwpICoqICgxIC8gdG90YWxfeWVhcnMpIC0gMQpkcmF3ZG93biA9IHBvcnRmb2xpb19zZXJpZXMgLyBwb3J0Zm9saW9fc2VyaWVzLmN1bW1heCgpIC0gMQptYXhfZGQgPSBkcmF3ZG93bi5taW4oKQoKcHJpbnQoZiZxdW90O+OAkOWPjOWboOWtkOe9l+ebmOetlueVpSAoRGFsaW8geCBNYXJrcynjgJEmcXVvdDspCnByaW50KGYmcXVvdDvlubTljJbmlLbnm4rnjocgKENBR1IpOiB7Y2FncjouMiV9JnF1b3Q7KQpwcmludChmJnF1b3Q75pyA5aSn5Zue5pKkIChNYXggRHJhd2Rvd24pOiB7bWF4X2RkOi4yJX0mcXVvdDspCnByaW50KGYmcXVvdDvmnJ/mnKvmgLvotYTkuqc6IHtwb3J0Zm9saW9fc2VyaWVzLmlsb2NbLTFdOiwuMGZ9JnF1b3Q7KQoKIyDnu5jlm74KcGx0LmZpZ3VyZShmaWdzaXplPSgxMiwgNikpCnBvcnRmb2xpb19zZXJpZXMucGxvdCh0aXRsZT0nRHluYW1pYyBDeWNsZSBTdHJhdGVneSAoRGFsaW8gKyBNYXJrcyknKQpwbHQueWxhYmVsKCdQb3J0Zm9saW8gVmFsdWUnKQpwbHQueGxhYmVsKCdEYXRlJykKcGx0LmdyaWQoVHJ1ZSkKcGx0LnNob3coKQ==
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 1. 基础设置
tickers = ['SPY', 'EWH', 'ASHR', 'BTC-USD', 'GLD', 'AGG']
# 你的标准配置 (Base Weights)
base_weights = {
'SPY': 0.30, 'EWH': 0.25, 'ASHR': 0.10,
'BTC-USD': 0.05, 'GLD': 0.10, 'AGG': 0.20
}
initial_capital = 7000000
# 2. 获取数据 (过去 10 年)
print("正在下载数据并构建罗盘...")
data = yf.download(tickers, start="2014-01-01", end="2025-01-01")['Adj Close'].dropna()
# 3. 计算核心指标 (罗盘的指针)
# Dalio Axis: 趋势 (MA200)
ma200 = data.rolling(window=200).mean()
# Marks Axis: 情绪 (RSI 14)
delta = data.diff()
gain = delta.where(delta > 0, 0).rolling(window=14).mean()
loss = -delta.where(delta < 0, 0).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi = rsi.fillna(50) # 填充 NaN
# 重新索引并前向填充,确保与 data 对齐
ma200 = ma200.reindex(data.index).ffill()
rsi = rsi.reindex(data.index).ffill()
# 4. 回测循环
portfolio_value = []
cash = initial_capital
shares = {t: 0 for t in tickers}
# 获取月末日期
monthly_dates = data.resample('M').last().index.normalize()
data_index_normalized = data.index.normalize()
for date in data.index:
# 当前投资组合价值
current_value = cash + sum(shares[t] * data.loc[date, t] for t in tickers)
portfolio_value.append(current_value)
# 仅在月底进行罗盘检查和调仓
if date.normalize() in monthly_dates:
target_weights = base_weights.copy()
for t in ['SPY', 'EWH', 'ASHR', 'BTC-USD']: # 仅针对风险资产
if date not in ma200.index or date not in rsi.index:
continue
price = data.loc[date, t]
ma = ma200.loc[date, t]
r_val = rsi.loc[date, t]
# 跳过 NaN
if pd.isna(ma) or pd.isna(r_val):
target_weights[t] = base_weights[t]
continue
is_expansion = price > ma
is_fear = r_val < 35
is_greed = r_val > 75
# Q2: 绝望区 (抄底)
if not is_expansion and is_fear:
target_weights[t] = base_weights[t] * 1.2
# Q1: 泡沫区 (止盈)
elif is_expansion and is_greed:
target_weights[t] = base_weights[t] * 0.5
# Q4: 崩溃/冬藏区 (止损)
elif not is_expansion and not is_fear:
target_weights[t] = 0.0
# Q3: 趋势区 (持有)
else:
target_weights[t] = base_weights[t]
# 可选:检查总权重是否超过 1.0,这里我们允许现金吸收
total_assets = cash + sum(shares[t] * data.loc[date, t] for t in tickers)
for t in tickers:
target_amt = total_assets * target_weights.get(t, base_weights[t])
shares[t] = target_amt / data.loc[date, t]
# 更新现金
invested = sum(shares[t] * data.loc[date, t] for t in tickers)
cash = total_assets - invested
# 5. 结果计算与展示
portfolio_series = pd.Series(portfolio_value, index=data.index)
total_years = (portfolio_series.index[-1] - portfolio_series.index[0]).days / 365.25
cagr = (portfolio_series.iloc[-1] / initial_capital) ** (1 / total_years) - 1
drawdown = portfolio_series / portfolio_series.cummax() - 1
max_dd = drawdown.min()
print(f"【双因子罗盘策略 (Dalio x Marks)】")
print(f"年化收益率 (CAGR): {cagr:.2%}")
print(f"最大回撤 (Max Drawdown): {max_dd:.2%}")
print(f"期末总资产: {portfolio_series.iloc[-1]:,.0f}")
# 绘图
plt.figure(figsize=(12, 6))
portfolio_series.plot(title='Dynamic Cycle Strategy (Dalio + Marks)')
plt.ylabel('Portfolio Value')
plt.xlabel('Date')
plt.grid(True)
plt.show()
ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: Can't ignore goal at this port
ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: Can't ignore goal at this port
ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: Can't ignore goal at this port
ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: Can't ignore goal at this port
ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: '$runtoplevel'/0: Undefined procedure: program/0
Exception: (3) program ? ERROR: Unknown option (h for help)
Exception: (3) program ? ERROR: Unknown option (h for help)
Exception: (3) program ? ERROR: Unknown option (h for help)
Exception: (3) program ? % Break level 1
ERROR: Syntax error: Illegal start of term
ERROR: 'SPY': 0.30, 'EWH': 0.25, 'ASHR': 0.10,
'BTC-USD': 0.05, 'GLD': 0.10, 'AGG': 0.20
ERROR: ** here **
ERROR:
}
initial_capital = 7000000
# 2 .
ERROR: Syntax error: Operator expected
ERROR: 获取数据
ERROR: ** here **
ERROR: (过去 10 年)
print("正在下载数据并构建罗盘...")
data = yf.download(tickers, start="2014-01-01", end="2025-01-01")['Adj Close'].dropna()
# 3 .
ERROR: Syntax error: Operator expected
ERROR: 计算核心指标
ERROR: ** here **
ERROR: (罗盘的指针)
# Dalio Axis: 趋势 (MA200)
ma200 = data.rolling(window=200).mean()
# Marks Axis: 情绪 (RSI 14)
delta = data.diff()
gain = delta.where(delta > 0, 0).rolling(window=14).mean()
loss = -delta.where(delta < 0, 0).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi = rsi.fillna(50) # 填充 NaN
# 重新索引并前向填充,确保与 data 对齐
ma200 = ma200.reindex(data.index).ffill()
rsi = rsi.reindex(data.index).ffill()
# 4 .
ERROR: Syntax error: Operator expected
ERROR: 回测循环
ERROR: ** here **
ERROR:
portfolio_value = []
cash = initial_capital
shares = {t: 0 for t in tickers}
# 获取月末日期
monthly_dates = data.resample('M').last().index.normalize()
data_index_normalized = data.index.normalize()
for date in data.index:
# 当前投资组合价值
current_value = cash + sum(shares[t] * data.loc[date, t] for t in tickers)
portfolio_value.append(current_value)
# 仅在月底进行罗盘检查和调仓
if date.normalize() in monthly_dates:
target_weights = base_weights.copy()
for t in ['SPY', 'EWH', 'ASHR', 'BTC-USD']: # 仅针对风险资产
if date not in ma200.index or date not in rsi.index:
continue
price = data.loc[date, t]
ma = ma200.loc[date, t]
r_val = rsi.loc[date, t]
# 跳过 NaN
if pd.isna(ma) or pd.isna(r_val):
target_weights[t] = base_weights[t]
continue
is_expansion = price > ma
is_fear = r_val < 35
is_greed = r_val > 75
# Q2: 绝望区 (抄底)
if not is_expansion and is_fear:
target_weights[t] = base_weights[t] * 1.2
# Q1: 泡沫区 (止盈)
elif is_expansion and is_greed:
target_weights[t] = base_weights[t] * 0.5
# Q4: 崩溃/冬藏区 (止损)
elif not is_expansion and not is_fear:
target_weights[t] = 0.0
# Q3: 趋势区 (持有)
else:
target_weights[t] = base_weights[t]
# 可选:检查总权重是否超过 1.0,这里我们允许现金吸收
total_assets = cash + sum(shares[t] * data.loc[date, t] for t in tickers)
for t in tickers:
target_amt = total_assets * target_weights.get(t, base_weights[t])
shares[t] = target_amt / data.loc[date, t]
# 更新现金
invested = sum(shares[t] * data.loc[date, t] for t in tickers)
cash = total_assets - invested
# 5 .
ERROR: Stream user_input:225:9 Syntax error: Unexpected end of file
% Exit break level 1
Exception: (3) program ? EOF: exit