1
0
Files
leopard-analysis/notebook/backtest.py

185 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import marimo
__generated_with = "0.19.6"
app = marimo.App(width="full", auto_download=["ipynb"], sql_output="pandas")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _():
import urllib
import sqlalchemy
host = "81.71.3.24"
port = 6785
username = "leopard"
password = urllib.parse.quote_plus("9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X")
database = "leopard_dev"
engine = sqlalchemy.create_engine(f"postgresql://{username}:{password}@{host}:{port}/{database}")
return (engine,)
@app.cell
def _(engine, mo):
dailies_df = mo.sql(
f"""
select trade_date,
open * factor as Open,
close * factor as Close,
high * factor as High,
low * factor as Low,
volume as Volume,
coalesce(factor, 1.0) as factor
from leopard_daily daily
left join leopard_stock stock on stock.id = daily.stock_id
where stock.code = '000001.SZ'
and daily.trade_date between '2024-01-01 00:00:00' and '2025-12-31 23:59:59'
order by daily.trade_date
""",
engine=engine
)
return (dailies_df,)
@app.cell
def _(dailies_df):
import pandas as pd
dailies_df.rename(
columns={'open': 'Open', 'close': 'Close', 'high': 'High', 'low': 'Low', 'volume': 'Volume'}, inplace=True
)
dailies_df['trade_date'] = pd.to_datetime(dailies_df['trade_date'], format='%Y-%m-%d')
dailies_df.set_index('trade_date', inplace=True)
dailies_df
return
@app.cell
def _(dailies_df):
import talib
dailies_df['sma10'] = talib.SMA(dailies_df['Close'], timeperiod=10)
dailies_df['sma30'] = talib.SMA(dailies_df['Close'], timeperiod=30)
dailies_df['sma60'] = talib.SMA(dailies_df['Close'], timeperiod=60)
dailies_df['sma120'] = talib.SMA(dailies_df['Close'], timeperiod=120)
dailies_df
return
@app.cell
def _(dailies_df):
# 指标计算完成后截取后面指标的完整的部份使用
target_dailies_df = dailies_df.loc['2025-01-01':'2025-12-31']
target_dailies_df
return (target_dailies_df,)
@app.cell
def _():
from backtesting import Strategy
from backtesting.lib import crossover
class SmaCross(Strategy):
def init(self):
self.sma10 = self.I(lambda x: x, self.data.sma10)
self.sma30 = self.I(lambda x: x, self.data.sma30)
self.sma60 = self.I(lambda x: x, self.data.sma60)
# self.sma120 = self.I(lambda x: x, self.data.sma120)
def next(self):
if self.sma60 > 0 and crossover(self.data.sma10, self.data.sma30):
self.buy()
elif self.position.size > 0 and crossover(self.data.sma30, self.data.sma10):
self.position.close()
return (SmaCross,)
@app.function
def stats_print(stats):
indicator_name_mapping = {
# 'Start': '回测开始时间',
# 'End': '回测结束时间',
# 'Duration': '回测持续时长',
# 'Exposure Time [%]': '持仓时间占比(%',
'Equity Final [$]': '最终收益',
'Equity Peak [$]': '峰值收益',
'Return [%]': '总收益率(%',
'Buy & Hold Return [%]': '买入并持有收益率(%',
'Return (Ann.) [%]': '年化收益率(%',
'Volatility (Ann.) [%]': '年化波动率(%',
# 'CAGR [%]': '复合年均增长率(%',
# 'Sharpe Ratio': '夏普比率',
'Sortino Ratio': '索提诺比率',
'Calmar Ratio': '卡尔玛比率',
# 'Alpha [%]': '阿尔法系数(%',
# 'Beta': '贝塔系数',
'Max. Drawdown [%]': '最大回撤(%',
'Avg. Drawdown [%]': '平均回撤(%',
'Max. Drawdown Duration': '最大回撤持续时长',
'Avg. Drawdown Duration': '平均回撤持续时长',
'# Trades': '总交易次数',
'Win Rate [%]': '胜率(%',
# 'Best Trade [%]': '最佳单笔交易收益率(%',
# 'Worst Trade [%]': '最差单笔交易收益率(%',
# 'Avg. Trade [%]': '平均单笔交易收益率(%',
# 'Max. Trade Duration': '单笔交易最长持有时长',
# 'Avg. Trade Duration': '单笔交易平均持有时长',
# 'Profit Factor': '盈利因子',
# 'Expectancy [%]': '期望收益(%',
'SQN': '系统质量数',
# 'Kelly Criterion': '凯利准则',
}
for k, v in stats.items():
if k in indicator_name_mapping:
cn_name = indicator_name_mapping.get(k, k)
if isinstance(v, (int, float)):
if "%" in cn_name or k in ['Sharpe Ratio', 'Sortino Ratio', 'Calmar Ratio', 'Profit Factor']:
formatted_value = f"{v:.2f}"
elif "$" in cn_name:
formatted_value = f"{v:.2f}"
elif "次数" in cn_name:
formatted_value = f"{v:.0f}"
else:
formatted_value = f"{v:.4f}"
else:
formatted_value = str(v)
print(f'{cn_name}: {formatted_value}')
@app.cell
def _(SmaCross, target_dailies_df):
from backtesting import Backtest
import backtesting._plotting as plotting
from bokeh.colors.named import tomato, lime
plotting.BULL_COLOR = tomato
plotting.BEAR_COLOR = lime
bt = Backtest(target_dailies_df, SmaCross, cash=100000, commission=.002, finalize_trades=True)
stats = bt.run()
stats_print(stats)
return bt, stats
@app.cell
def _(stats):
stats._trades
# stats._equity_curve
return
@app.cell
def _(bt):
bt.plot()
return
if __name__ == "__main__":
app.run()