diff --git a/backtest.ipynb b/backtest.ipynb
index f8fea5e..fa5dc0a 100644
--- a/backtest.ipynb
+++ b/backtest.ipynb
@@ -2,404 +2,702 @@
"cells": [
{
"metadata": {
+ "SqlCellData": {
+ "data_source_name": "leopard_dev@81.71.3.24",
+ "variableName$1": "dailies_df"
+ },
"ExecuteTime": {
- "end_time": "2026-01-19T10:00:35.820002Z",
- "start_time": "2026-01-19T10:00:34.354216Z"
+ "end_time": "2026-01-20T02:26:30.134545Z",
+ "start_time": "2026-01-20T02:26:22.978651Z"
+ }
+ },
+ "cell_type": "code",
+ "source": [
+ "%%sql\n",
+ "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\n",
+ "from leopard_daily daily\n",
+ " left join leopard_stock stock on stock.id = daily.stock_id\n",
+ "where stock.code = '000001.SZ'\n",
+ " and daily.trade_date between '2025-01-01 00:00:00' and '2025-12-31 23:59:59'\n",
+ "order by daily.trade_date"
+ ],
+ "id": "93e66142c88e4d2f",
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ " trade_date open close high low \\\n",
+ "0 2025-01-02 1498.907493 1460.572263 1504.018857 1455.460899 \n",
+ "1 2025-01-03 1461.850104 1454.183058 1474.628514 1451.627376 \n",
+ "2 2025-01-06 1454.183058 1461.850104 1466.961468 1433.737602 \n",
+ "3 2025-01-07 1459.294422 1470.794991 1473.350673 1452.905217 \n",
+ "4 2025-01-08 1469.517150 1469.517150 1486.129083 1456.738740 \n",
+ ".. ... ... ... ... ... \n",
+ "183 2025-10-09 1493.155774 1502.380920 1503.698798 1485.248506 \n",
+ "184 2025-10-10 1498.427286 1506.334554 1514.241822 1497.109408 \n",
+ "185 2025-10-13 1491.837896 1502.380920 1510.288188 1486.566384 \n",
+ "186 2025-10-14 1501.063042 1524.784846 1528.738480 1497.109408 \n",
+ "187 2025-10-15 1526.130396 1534.205160 1536.896748 1515.364044 \n",
+ "\n",
+ " volume factor \n",
+ "0 1819596.99 127.7841 \n",
+ "1 1154680.44 127.7841 \n",
+ "2 1085536.30 127.7841 \n",
+ "3 747862.88 127.7841 \n",
+ "4 1062386.01 127.7841 \n",
+ ".. ... ... \n",
+ "183 1047469.06 131.7878 \n",
+ "184 1087947.75 131.7878 \n",
+ "185 1168801.73 131.7878 \n",
+ "186 1843428.36 131.7878 \n",
+ "187 1271061.03 134.5794 \n",
+ "\n",
+ "[188 rows x 7 columns]"
+ ],
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " trade_date | \n",
+ " open | \n",
+ " close | \n",
+ " high | \n",
+ " low | \n",
+ " volume | \n",
+ " factor | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 2025-01-02 | \n",
+ " 1498.907493 | \n",
+ " 1460.572263 | \n",
+ " 1504.018857 | \n",
+ " 1455.460899 | \n",
+ " 1819596.99 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 2025-01-03 | \n",
+ " 1461.850104 | \n",
+ " 1454.183058 | \n",
+ " 1474.628514 | \n",
+ " 1451.627376 | \n",
+ " 1154680.44 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2025-01-06 | \n",
+ " 1454.183058 | \n",
+ " 1461.850104 | \n",
+ " 1466.961468 | \n",
+ " 1433.737602 | \n",
+ " 1085536.30 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2025-01-07 | \n",
+ " 1459.294422 | \n",
+ " 1470.794991 | \n",
+ " 1473.350673 | \n",
+ " 1452.905217 | \n",
+ " 747862.88 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2025-01-08 | \n",
+ " 1469.517150 | \n",
+ " 1469.517150 | \n",
+ " 1486.129083 | \n",
+ " 1456.738740 | \n",
+ " 1062386.01 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 183 | \n",
+ " 2025-10-09 | \n",
+ " 1493.155774 | \n",
+ " 1502.380920 | \n",
+ " 1503.698798 | \n",
+ " 1485.248506 | \n",
+ " 1047469.06 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 184 | \n",
+ " 2025-10-10 | \n",
+ " 1498.427286 | \n",
+ " 1506.334554 | \n",
+ " 1514.241822 | \n",
+ " 1497.109408 | \n",
+ " 1087947.75 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 185 | \n",
+ " 2025-10-13 | \n",
+ " 1491.837896 | \n",
+ " 1502.380920 | \n",
+ " 1510.288188 | \n",
+ " 1486.566384 | \n",
+ " 1168801.73 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 186 | \n",
+ " 2025-10-14 | \n",
+ " 1501.063042 | \n",
+ " 1524.784846 | \n",
+ " 1528.738480 | \n",
+ " 1497.109408 | \n",
+ " 1843428.36 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 187 | \n",
+ " 2025-10-15 | \n",
+ " 1526.130396 | \n",
+ " 1534.205160 | \n",
+ " 1536.896748 | \n",
+ " 1515.364044 | \n",
+ " 1271061.03 | \n",
+ " 134.5794 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
188 rows × 7 columns
\n",
+ "
"
+ ]
+ },
+ "execution_count": 87,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "execution_count": 87
+ },
+ {
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2026-01-20T02:26:30.368092Z",
+ "start_time": "2026-01-20T02:26:30.241161Z"
}
},
"cell_type": "code",
"source": [
"import pandas as pd\n",
"\n",
- "host = '81.71.3.24'\n",
- "port = 6785\n",
- "database = 'leopard_dev'\n",
- "username = 'leopard'\n",
- "password = '9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X'"
+ "dailies_df.rename(\n",
+ " columns={'open': 'Open', 'close': 'Close', 'high': 'High', 'low': 'Low', 'volume': 'Volume'}, inplace=True\n",
+ ")\n",
+ "dailies_df['trade_date'] = pd.to_datetime(dailies_df['trade_date'], format='%Y-%m-%d')\n",
+ "dailies_df.set_index('trade_date', inplace=True)\n",
+ "dailies_df"
],
- "id": "d815a3591c4f9463",
+ "id": "c0ed078ffd5b57fd",
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ " Open Close High Low Volume \\\n",
+ "trade_date \n",
+ "2025-01-02 1498.907493 1460.572263 1504.018857 1455.460899 1819596.99 \n",
+ "2025-01-03 1461.850104 1454.183058 1474.628514 1451.627376 1154680.44 \n",
+ "2025-01-06 1454.183058 1461.850104 1466.961468 1433.737602 1085536.30 \n",
+ "2025-01-07 1459.294422 1470.794991 1473.350673 1452.905217 747862.88 \n",
+ "2025-01-08 1469.517150 1469.517150 1486.129083 1456.738740 1062386.01 \n",
+ "... ... ... ... ... ... \n",
+ "2025-10-09 1493.155774 1502.380920 1503.698798 1485.248506 1047469.06 \n",
+ "2025-10-10 1498.427286 1506.334554 1514.241822 1497.109408 1087947.75 \n",
+ "2025-10-13 1491.837896 1502.380920 1510.288188 1486.566384 1168801.73 \n",
+ "2025-10-14 1501.063042 1524.784846 1528.738480 1497.109408 1843428.36 \n",
+ "2025-10-15 1526.130396 1534.205160 1536.896748 1515.364044 1271061.03 \n",
+ "\n",
+ " factor \n",
+ "trade_date \n",
+ "2025-01-02 127.7841 \n",
+ "2025-01-03 127.7841 \n",
+ "2025-01-06 127.7841 \n",
+ "2025-01-07 127.7841 \n",
+ "2025-01-08 127.7841 \n",
+ "... ... \n",
+ "2025-10-09 131.7878 \n",
+ "2025-10-10 131.7878 \n",
+ "2025-10-13 131.7878 \n",
+ "2025-10-14 131.7878 \n",
+ "2025-10-15 134.5794 \n",
+ "\n",
+ "[188 rows x 6 columns]"
+ ],
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Open | \n",
+ " Close | \n",
+ " High | \n",
+ " Low | \n",
+ " Volume | \n",
+ " factor | \n",
+ "
\n",
+ " \n",
+ " | trade_date | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2025-01-02 | \n",
+ " 1498.907493 | \n",
+ " 1460.572263 | \n",
+ " 1504.018857 | \n",
+ " 1455.460899 | \n",
+ " 1819596.99 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 2025-01-03 | \n",
+ " 1461.850104 | \n",
+ " 1454.183058 | \n",
+ " 1474.628514 | \n",
+ " 1451.627376 | \n",
+ " 1154680.44 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 2025-01-06 | \n",
+ " 1454.183058 | \n",
+ " 1461.850104 | \n",
+ " 1466.961468 | \n",
+ " 1433.737602 | \n",
+ " 1085536.30 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 2025-01-07 | \n",
+ " 1459.294422 | \n",
+ " 1470.794991 | \n",
+ " 1473.350673 | \n",
+ " 1452.905217 | \n",
+ " 747862.88 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | 2025-01-08 | \n",
+ " 1469.517150 | \n",
+ " 1469.517150 | \n",
+ " 1486.129083 | \n",
+ " 1456.738740 | \n",
+ " 1062386.01 | \n",
+ " 127.7841 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-09 | \n",
+ " 1493.155774 | \n",
+ " 1502.380920 | \n",
+ " 1503.698798 | \n",
+ " 1485.248506 | \n",
+ " 1047469.06 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-10 | \n",
+ " 1498.427286 | \n",
+ " 1506.334554 | \n",
+ " 1514.241822 | \n",
+ " 1497.109408 | \n",
+ " 1087947.75 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-13 | \n",
+ " 1491.837896 | \n",
+ " 1502.380920 | \n",
+ " 1510.288188 | \n",
+ " 1486.566384 | \n",
+ " 1168801.73 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-14 | \n",
+ " 1501.063042 | \n",
+ " 1524.784846 | \n",
+ " 1528.738480 | \n",
+ " 1497.109408 | \n",
+ " 1843428.36 | \n",
+ " 131.7878 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-15 | \n",
+ " 1526.130396 | \n",
+ " 1534.205160 | \n",
+ " 1536.896748 | \n",
+ " 1515.364044 | \n",
+ " 1271061.03 | \n",
+ " 134.5794 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
188 rows × 6 columns
\n",
+ "
"
+ ]
+ },
+ "execution_count": 88,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "execution_count": 88
+ },
+ {
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2026-01-20T02:28:38.147008Z",
+ "start_time": "2026-01-20T02:28:38.094513Z"
+ }
+ },
+ "cell_type": "code",
+ "source": [
+ "dailies_df['sma30'] = dailies_df['Close'].rolling(10).mean()\n",
+ "dailies_df['sma60'] = dailies_df['Close'].rolling(30).mean()\n",
+ "dailies_df.tail()"
+ ],
+ "id": "c558d68773d228c1",
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ " Open Close High Low Volume \\\n",
+ "trade_date \n",
+ "2025-10-09 1493.155774 1502.380920 1503.698798 1485.248506 1047469.06 \n",
+ "2025-10-10 1498.427286 1506.334554 1514.241822 1497.109408 1087947.75 \n",
+ "2025-10-13 1491.837896 1502.380920 1510.288188 1486.566384 1168801.73 \n",
+ "2025-10-14 1501.063042 1524.784846 1528.738480 1497.109408 1843428.36 \n",
+ "2025-10-15 1526.130396 1534.205160 1536.896748 1515.364044 1271061.03 \n",
+ "\n",
+ " factor sma30 sma60 \n",
+ "trade_date \n",
+ "2025-10-09 131.7878 1504.094161 1546.925196 \n",
+ "2025-10-10 131.7878 1504.357737 1543.762289 \n",
+ "2025-10-13 131.7878 1503.698798 1540.862958 \n",
+ "2025-10-14 131.7878 1506.202766 1536.997182 \n",
+ "2025-10-15 134.5794 1507.803737 1533.840781 "
+ ],
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Open | \n",
+ " Close | \n",
+ " High | \n",
+ " Low | \n",
+ " Volume | \n",
+ " factor | \n",
+ " sma30 | \n",
+ " sma60 | \n",
+ "
\n",
+ " \n",
+ " | trade_date | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2025-10-09 | \n",
+ " 1493.155774 | \n",
+ " 1502.380920 | \n",
+ " 1503.698798 | \n",
+ " 1485.248506 | \n",
+ " 1047469.06 | \n",
+ " 131.7878 | \n",
+ " 1504.094161 | \n",
+ " 1546.925196 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-10 | \n",
+ " 1498.427286 | \n",
+ " 1506.334554 | \n",
+ " 1514.241822 | \n",
+ " 1497.109408 | \n",
+ " 1087947.75 | \n",
+ " 131.7878 | \n",
+ " 1504.357737 | \n",
+ " 1543.762289 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-13 | \n",
+ " 1491.837896 | \n",
+ " 1502.380920 | \n",
+ " 1510.288188 | \n",
+ " 1486.566384 | \n",
+ " 1168801.73 | \n",
+ " 131.7878 | \n",
+ " 1503.698798 | \n",
+ " 1540.862958 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-14 | \n",
+ " 1501.063042 | \n",
+ " 1524.784846 | \n",
+ " 1528.738480 | \n",
+ " 1497.109408 | \n",
+ " 1843428.36 | \n",
+ " 131.7878 | \n",
+ " 1506.202766 | \n",
+ " 1536.997182 | \n",
+ "
\n",
+ " \n",
+ " | 2025-10-15 | \n",
+ " 1526.130396 | \n",
+ " 1534.205160 | \n",
+ " 1536.896748 | \n",
+ " 1515.364044 | \n",
+ " 1271061.03 | \n",
+ " 134.5794 | \n",
+ " 1507.803737 | \n",
+ " 1533.840781 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ]
+ },
+ "execution_count": 97,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "execution_count": 97
+ },
+ {
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2026-01-20T02:28:09.232747Z",
+ "start_time": "2026-01-20T02:28:09.215190Z"
+ }
+ },
+ "cell_type": "code",
+ "source": [
+ "from backtesting import Strategy\n",
+ "from backtesting.lib import crossover\n",
+ "\n",
+ "\n",
+ "class SmaCross(Strategy):\n",
+ " def init(self):\n",
+ " pass\n",
+ "\n",
+ " def next(self):\n",
+ " if crossover(self.data.sma30, self.data.sma60):\n",
+ " self.position.close()\n",
+ " self.buy()\n",
+ " elif crossover(self.data.sma60, self.data.sma30):\n",
+ " self.position.close()\n",
+ " self.sell()"
+ ],
+ "id": "b56aaaf4cad7bc7d",
"outputs": [],
- "execution_count": 2
- },
- {
- "cell_type": "code",
- "id": "initial_id",
- "metadata": {
- "collapsed": true,
- "ExecuteTime": {
- "end_time": "2026-01-19T10:00:42.739695Z",
- "start_time": "2026-01-19T10:00:35.820726Z"
- }
- },
- "source": [
- "import psycopg2\n",
- "\n",
- "dailies_df = pd.DataFrame()\n",
- "\n",
- "with psycopg2.connect(host=host, port=port, database=database, user=username, password=password) as connection:\n",
- " with connection.cursor() as cursor:\n",
- " # language=PostgreSQL\n",
- " cursor.execute(\n",
- " \"\"\"select trade_date, open, close, high, low, factor\n",
- "from leopard_daily daily\n",
- " left join leopard_stock stock on stock.id = daily.stock_id\n",
- "where stock.code = '000001.SZ'\n",
- " and daily.trade_date between '2025-01-01 00:00:00' and '2025-12-31 23:59:59'\n",
- "order by daily.trade_date\"\"\"\n",
- " )\n",
- " rows = cursor.fetchall()\n",
- "\n",
- " dailies_df = pd.DataFrame.from_records(rows, columns=['trade_date', 'open', 'close', 'high', 'low', 'factor'])\n",
- "\n",
- "dailies_df"
- ],
- "outputs": [
- {
- "data": {
- "text/plain": [
- " trade_date open close high low factor\n",
- "0 2025-01-02 11.73 11.43 11.77 11.39 127.7841\n",
- "1 2025-01-03 11.44 11.38 11.54 11.36 127.7841\n",
- "2 2025-01-06 11.38 11.44 11.48 11.22 127.7841\n",
- "3 2025-01-07 11.42 11.51 11.53 11.37 127.7841\n",
- "4 2025-01-08 11.50 11.50 11.63 11.40 127.7841\n",
- ".. ... ... ... ... ... ...\n",
- "183 2025-10-09 11.33 11.40 11.41 11.27 131.7878\n",
- "184 2025-10-10 11.37 11.43 11.49 11.36 131.7878\n",
- "185 2025-10-13 11.32 11.40 11.46 11.28 131.7878\n",
- "186 2025-10-14 11.39 11.57 11.60 11.36 131.7878\n",
- "187 2025-10-15 11.34 11.40 11.42 11.26 134.5794\n",
- "\n",
- "[188 rows x 6 columns]"
- ],
- "text/html": [
- "\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " trade_date | \n",
- " open | \n",
- " close | \n",
- " high | \n",
- " low | \n",
- " factor | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " | 0 | \n",
- " 2025-01-02 | \n",
- " 11.73 | \n",
- " 11.43 | \n",
- " 11.77 | \n",
- " 11.39 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 1 | \n",
- " 2025-01-03 | \n",
- " 11.44 | \n",
- " 11.38 | \n",
- " 11.54 | \n",
- " 11.36 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 2 | \n",
- " 2025-01-06 | \n",
- " 11.38 | \n",
- " 11.44 | \n",
- " 11.48 | \n",
- " 11.22 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 3 | \n",
- " 2025-01-07 | \n",
- " 11.42 | \n",
- " 11.51 | \n",
- " 11.53 | \n",
- " 11.37 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 4 | \n",
- " 2025-01-08 | \n",
- " 11.50 | \n",
- " 11.50 | \n",
- " 11.63 | \n",
- " 11.40 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- "
\n",
- " \n",
- " | 183 | \n",
- " 2025-10-09 | \n",
- " 11.33 | \n",
- " 11.40 | \n",
- " 11.41 | \n",
- " 11.27 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 184 | \n",
- " 2025-10-10 | \n",
- " 11.37 | \n",
- " 11.43 | \n",
- " 11.49 | \n",
- " 11.36 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 185 | \n",
- " 2025-10-13 | \n",
- " 11.32 | \n",
- " 11.40 | \n",
- " 11.46 | \n",
- " 11.28 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 186 | \n",
- " 2025-10-14 | \n",
- " 11.39 | \n",
- " 11.57 | \n",
- " 11.60 | \n",
- " 11.36 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 187 | \n",
- " 2025-10-15 | \n",
- " 11.34 | \n",
- " 11.40 | \n",
- " 11.42 | \n",
- " 11.26 | \n",
- " 134.5794 | \n",
- "
\n",
- " \n",
- "
\n",
- "
188 rows × 6 columns
\n",
- "
"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "execution_count": 3
+ "execution_count": 94
},
{
"metadata": {
"ExecuteTime": {
- "end_time": "2026-01-19T10:01:46.133661Z",
- "start_time": "2026-01-19T10:01:46.044719Z"
+ "end_time": "2026-01-20T02:28:12.350117Z",
+ "start_time": "2026-01-20T02:28:12.330024Z"
}
},
"cell_type": "code",
"source": [
- "dailies_df['factor'] = dailies_df['factor'].fillna(1.0)\n",
- "dailies_df['open'] = dailies_df['open'] * dailies_df['factor']\n",
- "dailies_df['close'] = dailies_df['close'] * dailies_df['factor']\n",
- "dailies_df['high'] = dailies_df['high'] * dailies_df['factor']\n",
- "dailies_df['low'] = dailies_df['low'] * dailies_df['factor']\n",
- "dailies_df"
+ "def stats_print(stats):\n",
+ " indicator_name_mapping = {\n",
+ " 'Start': '回测开始时间',\n",
+ " 'End': '回测结束时间',\n",
+ " 'Duration': '回测持续时长',\n",
+ " 'Exposure Time [%]': '持仓时间占比(%)',\n",
+ " 'Equity Final [$]': '最终权益',\n",
+ " 'Equity Peak [$]': '权益峰值',\n",
+ " 'Return [%]': '总收益率(%)',\n",
+ " 'Buy & Hold Return [%]': '买入并持有收益率(%)',\n",
+ " 'Return (Ann.) [%]': '年化收益率(%)',\n",
+ " 'Volatility (Ann.) [%]': '年化波动率(%)',\n",
+ " 'CAGR [%]': '复合年均增长率(%)',\n",
+ " 'Sharpe Ratio': '夏普比率',\n",
+ " 'Sortino Ratio': '索提诺比率',\n",
+ " 'Calmar Ratio': '卡尔玛比率',\n",
+ " 'Alpha [%]': '阿尔法系数(%)',\n",
+ " 'Beta': '贝塔系数',\n",
+ " 'Max. Drawdown [%]': '最大回撤(%)',\n",
+ " 'Avg. Drawdown [%]': '平均回撤(%)',\n",
+ " 'Max. Drawdown Duration': '最大回撤持续时长',\n",
+ " 'Avg. Drawdown Duration': '平均回撤持续时长',\n",
+ " '# Trades': '交易总次数',\n",
+ " 'Win Rate [%]': '胜率(%)',\n",
+ " 'Best Trade [%]': '最佳单笔交易收益率(%)',\n",
+ " 'Worst Trade [%]': '最差单笔交易收益率(%)',\n",
+ " 'Avg. Trade [%]': '平均单笔交易收益率(%)',\n",
+ " 'Max. Trade Duration': '单笔交易最长持有时长',\n",
+ " 'Avg. Trade Duration': '单笔交易平均持有时长',\n",
+ " 'Profit Factor': '盈利因子',\n",
+ " 'Expectancy [%]': '期望收益(%)',\n",
+ " 'SQN': '系统质量数',\n",
+ " 'Kelly Criterion': '凯利准则',\n",
+ " }\n",
+ " for k, v in stats.items():\n",
+ " if k in indicator_name_mapping:\n",
+ " cn_name = indicator_name_mapping.get(k, k)\n",
+ " if isinstance(v, (int, float)):\n",
+ " if \"%\" in cn_name or k in ['Sharpe Ratio', 'Sortino Ratio', 'Calmar Ratio', 'Profit Factor']:\n",
+ " formatted_value = f\"{v:.2f}\"\n",
+ " elif \"$\" in cn_name:\n",
+ " formatted_value = f\"{v:.2f}\"\n",
+ " elif \"次数\" in cn_name:\n",
+ " formatted_value = f\"{v:.0f}\"\n",
+ " else:\n",
+ " formatted_value = f\"{v:.4f}\"\n",
+ " else:\n",
+ " formatted_value = str(v)\n",
+ " print(f'{cn_name}: {formatted_value}')"
],
- "id": "29226cf1c6fe9f94",
+ "id": "a23e811212958477",
+ "outputs": [],
+ "execution_count": 95
+ },
+ {
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2026-01-20T02:28:43.128279Z",
+ "start_time": "2026-01-20T02:28:43.044156Z"
+ }
+ },
+ "cell_type": "code",
+ "source": [
+ "from backtesting import Backtest\n",
+ "\n",
+ "bt = Backtest(dailies_df, SmaCross, cash=100000, commission=.002, finalize_trades=True)\n",
+ "stats = bt.run()\n",
+ "stats_print(stats)"
+ ],
+ "id": "afc750727129ff2e",
"outputs": [
{
- "data": {
- "text/plain": [
- " trade_date open close high low \\\n",
- "0 2025-01-02 191536.544976 186637.912112 192189.696025 185984.761064 \n",
- "1 2025-01-03 186801.199875 185821.473302 188434.077496 185494.897778 \n",
- "2 2025-01-06 185821.473302 186801.199875 187454.350923 183208.869108 \n",
- "3 2025-01-07 186474.624350 187944.214209 188270.789734 185658.185540 \n",
- "4 2025-01-08 187780.926447 187780.926447 189903.667355 186148.048826 \n",
- ".. ... ... ... ... ... \n",
- "183 2025-10-09 196779.714513 197995.476209 198169.156451 195737.633059 \n",
- "184 2025-10-10 197474.435482 198516.516936 199558.598389 197300.755240 \n",
- "185 2025-10-13 196606.034270 197995.476209 199037.557663 195911.313301 \n",
- "186 2025-10-14 197821.795966 200948.040328 201469.081055 197300.755240 \n",
- "187 2025-10-15 205385.713015 206472.409910 206834.642208 203936.783823 \n",
- "\n",
- " factor \n",
- "0 127.7841 \n",
- "1 127.7841 \n",
- "2 127.7841 \n",
- "3 127.7841 \n",
- "4 127.7841 \n",
- ".. ... \n",
- "183 131.7878 \n",
- "184 131.7878 \n",
- "185 131.7878 \n",
- "186 131.7878 \n",
- "187 134.5794 \n",
- "\n",
- "[188 rows x 6 columns]"
- ],
- "text/html": [
- "\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " trade_date | \n",
- " open | \n",
- " close | \n",
- " high | \n",
- " low | \n",
- " factor | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " | 0 | \n",
- " 2025-01-02 | \n",
- " 191536.544976 | \n",
- " 186637.912112 | \n",
- " 192189.696025 | \n",
- " 185984.761064 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 1 | \n",
- " 2025-01-03 | \n",
- " 186801.199875 | \n",
- " 185821.473302 | \n",
- " 188434.077496 | \n",
- " 185494.897778 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 2 | \n",
- " 2025-01-06 | \n",
- " 185821.473302 | \n",
- " 186801.199875 | \n",
- " 187454.350923 | \n",
- " 183208.869108 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 3 | \n",
- " 2025-01-07 | \n",
- " 186474.624350 | \n",
- " 187944.214209 | \n",
- " 188270.789734 | \n",
- " 185658.185540 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | 4 | \n",
- " 2025-01-08 | \n",
- " 187780.926447 | \n",
- " 187780.926447 | \n",
- " 189903.667355 | \n",
- " 186148.048826 | \n",
- " 127.7841 | \n",
- "
\n",
- " \n",
- " | ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- "
\n",
- " \n",
- " | 183 | \n",
- " 2025-10-09 | \n",
- " 196779.714513 | \n",
- " 197995.476209 | \n",
- " 198169.156451 | \n",
- " 195737.633059 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 184 | \n",
- " 2025-10-10 | \n",
- " 197474.435482 | \n",
- " 198516.516936 | \n",
- " 199558.598389 | \n",
- " 197300.755240 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 185 | \n",
- " 2025-10-13 | \n",
- " 196606.034270 | \n",
- " 197995.476209 | \n",
- " 199037.557663 | \n",
- " 195911.313301 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 186 | \n",
- " 2025-10-14 | \n",
- " 197821.795966 | \n",
- " 200948.040328 | \n",
- " 201469.081055 | \n",
- " 197300.755240 | \n",
- " 131.7878 | \n",
- "
\n",
- " \n",
- " | 187 | \n",
- " 2025-10-15 | \n",
- " 205385.713015 | \n",
- " 206472.409910 | \n",
- " 206834.642208 | \n",
- " 203936.783823 | \n",
- " 134.5794 | \n",
- "
\n",
- " \n",
- "
\n",
- "
188 rows × 6 columns
\n",
- "
"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "回测开始时间: 2025-01-02 00:00:00\n",
+ "回测结束时间: 2025-10-15 00:00:00\n",
+ "回测持续时长: 286 days 00:00:00\n",
+ "持仓时间占比(%): 71.81\n",
+ "最终权益: 114727.4805\n",
+ "权益峰值: 117838.4813\n",
+ "总收益率(%): 14.73\n",
+ "买入并持有收益率(%): 5.04\n",
+ "年化收益率(%): 20.22\n",
+ "年化波动率(%): 17.17\n",
+ "复合年均增长率(%): 12.87\n",
+ "夏普比率: 1.18\n",
+ "索提诺比率: 2.45\n",
+ "卡尔玛比率: 2.21\n",
+ "阿尔法系数(%): 14.96\n",
+ "贝塔系数: -0.0456\n",
+ "最大回撤(%): -9.13\n",
+ "平均回撤(%): -3.40\n",
+ "最大回撤持续时长: 97 days 00:00:00\n",
+ "平均回撤持续时长: 33 days 00:00:00\n",
+ "交易总次数: 3\n",
+ "胜率(%): 66.67\n",
+ "最佳单笔交易收益率(%): 10.02\n",
+ "最差单笔交易收益率(%): -0.49\n",
+ "平均单笔交易收益率(%): 4.76\n",
+ "单笔交易最长持有时长: 78 days 00:00:00\n",
+ "单笔交易平均持有时长: 68 days 00:00:00\n",
+ "盈利因子: 30.80\n",
+ "期望收益(%): 4.84\n",
+ "系统质量数: 1.6469\n",
+ "凯利准则: 0.6455\n"
+ ]
}
],
- "execution_count": 5
+ "execution_count": 98
}
],
"metadata": {
diff --git a/note.md b/note.md
new file mode 100644
index 0000000..16494b6
--- /dev/null
+++ b/note.md
@@ -0,0 +1,126 @@
+# 基础时间与权益指标
+
+## Exposure Time [%] - 持仓时间占比(%)
+
+衡量策略在统计周期内实际持有头寸的时间占总时间的百分比。反映策略的“活跃度”(如占比100%=全程持仓,占比低=多数时间空仓),辅助判断交易频率和资金使用效率。
+
+## Equity Final [$] - 最终权益
+
+策略运行结束时的账户总资金(本金+盈亏),是最基础的收益结果指标,直接体现最终资金规模。
+
+## Equity Peak [$] - 权益峰值
+
+统计周期内账户权益的最大值。对比最终权益可判断策略是否从峰值回落,结合回撤指标能评估收益稳定性。
+
+# 收益率类指标(核心收益衡量)
+
+## Return [%] - 总收益率(%)
+
+周期内总收益占初始本金的百分比(`(最终权益-初始本金)/初始本金×100%`),直观反映整体盈利/亏损程度,但未考虑时间因素。
+
+## Buy & Hold Return [%] - 买入并持有收益率(%)
+
+作为“基准收益率”,模拟“买入标的后长期持有不操作”的收益。用于对比策略收益,判断策略是否跑赢简单持有,是评估策略有效性的核心参照。
+
+## Return (Ann.) [%] - 年化收益率(%)
+
+将周期总收益折算为按年计算的收益(`(1+总收益率)^(365/持仓天数) - 1`),消除时间周期差异,是跨周期对比不同策略收益的关键指标。
+
+## CAGR [%] - 复合年均增长率(%)
+
+即“年均复合收益率”(`(最终权益/初始本金)^(1/年数) - 1`),衡量资金长期复利增长速度,更贴合多年期策略的收益评估,避免短期高收益误导。
+
+# 风险类指标(衡量亏损与波动风险)
+
+## Volatility (Ann.) [%] - 年化波动率(%)
+
+收益波动程度的年化标准差,反映收益稳定性。波动率越高,收益起伏越大,策略“震荡风险”越高;波动率低则收益更平稳。
+
+## Max. Drawdown [%] - 最大回撤(%)
+
+权益从峰值回落至谷值的最大幅度(`(峰值-谷值)/峰值×100%`),核心风险指标。比如最大回撤20%,说明策略曾从最高点亏掉20%,直接体现抗风险能力。
+
+## Avg. Drawdown [%] - 平均回撤(%)
+
+所有回撤的平均幅度。若最大回撤远高于平均回撤,说明策略曾出现极端亏损;两者接近则回撤风险更均匀。
+
+## Max. Drawdown Duration - 最大回撤持续时长
+
+最大回撤从峰值回落至恢复峰值的时间(天/月),反映策略从最大亏损中回血的速度,时长越长则恢复能力越弱。
+
+## Avg. Drawdown Duration - 平均回撤持续时长
+
+所有回撤周期的平均时间,辅助判断策略亏损后的恢复效率。
+
+# 风险收益比指标(性价比核心)
+
+## Sharpe Ratio - 夏普比率
+
+衡量“每承担1单位风险的超额收益”(`(年化收益率-无风险利率)/年化波动率`
+),无风险利率通常取国债利率(3%左右)。比率越高性价比越高(>1较好,>2优秀)。
+
+## Sortino Ratio - 索提诺比率
+
+改进版夏普比率,仅用“下行波动率”(亏损方向波动)计算风险,更贴合投资者对“亏损风险”的关注,适合评估稳健型策略。
+
+## Calmar Ratio - 卡尔玛比率
+
+`年化收益率/最大回撤(绝对值)`,直接反映“收益能否覆盖最大亏损”,比率越高,策略用小回撤换高收益的能力越强。
+
+# 资产定价系数(市场相关性)
+
+## Alpha [%] - 阿尔法系数(%)
+
+衡量策略“超额收益”(超出市场基准的收益),Alpha为正说明策略能跑赢市场,反映主动管理能力;为负则跑输市场。
+
+## Beta - 贝塔系数
+
+衡量策略与市场基准的波动同步性。Beta=1=和市场波动一致;Beta>1=策略更激进(波动更大);Beta<1=策略更稳健;Beta<0=与市场反向波动。
+
+# 交易行为与质量指标
+
+## Trades - 交易总次数
+
+统计周期内总交易笔数,反映策略交易频率(高频/低频),结合胜率可判断策略有效性。
+
+## Win Rate [%] - 胜率(%)
+
+盈利交易次数/总次数×100%,直观反映“赚钱交易的比例”,但需结合盈亏比(平均盈利/平均亏损)使用(如胜率50%+盈亏比3:1仍盈利)。
+
+## Best Trade [%] - 最佳单笔交易收益率(%)
+
+单次盈利幅度最大值,反映策略盈利上限,但需警惕是否由偶然高收益拉高整体表现。
+
+## Worst Trade [%] - 最差单笔交易收益率(%)
+
+单次亏损幅度最大值(通常为负),反映单笔交易的最大亏损风险。
+
+## Avg. Trade [%] - 平均单笔交易收益率(%)
+
+所有交易收益率的平均值,反映单笔交易的平均盈利能力,为正则单笔交易平均赚钱。
+
+## Max. Trade Duration - 单笔交易最长持有时长
+
+单次持仓的最长时间,定义策略风格(日内/短线/中长线)。
+
+## Avg. Trade Duration - 单笔交易平均持有时长
+
+所有交易持仓时长的平均值,辅助判断资金周转效率。
+
+# 综合绩效指标(策略可持续性)
+
+## Profit Factor - 盈利因子
+
+`总盈利金额/总亏损金额(绝对值)`,核心指标。>1说明总盈利>总亏损,数值越高盈利能力越强(如盈利因子2=每亏1元赚2元)。
+
+## Expectancy [%] - 期望收益(%)
+
+每笔交易的预期收益(`胜率×平均盈利 - (1-胜率)×平均亏损`),为正则策略长期可盈利,数值越高预期收益越好。
+
+## SQN - 系统质量数
+
+衡量策略收益的“统计显著性”(`√交易次数 × 期望收益 / 交易收益标准差`)。SQN>1.6合格,>2.5优秀,>3.0卓越,判断收益是否为偶然结果。
+
+## Kelly Criterion - 凯利准则
+
+计算“最优仓位比例”(`(胜率×(盈亏比+1)-1)/盈亏比`),确定每笔交易的最佳资金投入比例,最大化长期复利收益,同时控制破产风险(实际应用中通常减半使用)。
diff --git a/pyproject.toml b/pyproject.toml
index f4ccb4b..37f6a56 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,8 +5,10 @@ description = "Stock analysis"
requires-python = ">=3.14"
dependencies = [
"backtesting~=0.6.5",
+ "duckdb>=1.4.3",
"jupyter~=1.1.1",
"matplotlib~=3.10.8",
+ "mplfinance>=0.12.10b0",
"pandas~=2.3.3",
"pandas-stubs~=2.3.3",
"peewee~=3.19.0",
diff --git a/sql2dataframe.ipynb b/sql2dataframe.ipynb
new file mode 100644
index 0000000..87ad9a0
--- /dev/null
+++ b/sql2dataframe.ipynb
@@ -0,0 +1,72 @@
+{
+ "cells": [
+ {
+ "metadata": {},
+ "cell_type": "code",
+ "outputs": [],
+ "execution_count": null,
+ "source": [
+ "import pandas as pd\n",
+ "\n",
+ "host = '81.71.3.24'\n",
+ "port = 6785\n",
+ "database = 'leopard_dev'\n",
+ "username = 'leopard'\n",
+ "password = '9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X'"
+ ],
+ "id": "1e8d815ee9b8c936"
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "initial_id",
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "import psycopg2\n",
+ "\n",
+ "dailies_df = pd.DataFrame()\n",
+ "\n",
+ "with psycopg2.connect(host=host, port=port, database=database, user=username, password=password) as connection:\n",
+ " with connection.cursor() as cursor:\n",
+ " # language=PostgreSQL\n",
+ " cursor.execute(\n",
+ " \"\"\"select trade_date, open, close, high, low, factor\n",
+ "from leopard_daily daily\n",
+ " left join leopard_stock stock on stock.id = daily.stock_id\n",
+ "where stock.code = '000001.SZ'\n",
+ " and daily.trade_date between '2025-01-01 00:00:00' and '2025-12-31 23:59:59'\n",
+ "order by daily.trade_date\"\"\"\n",
+ " )\n",
+ " rows = cursor.fetchall()\n",
+ "\n",
+ " dailies_df = pd.DataFrame.from_records(rows, columns=['trade_date', 'open', 'close', 'high', 'low', 'factor'])\n",
+ "\n",
+ "dailies_df"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/uv.lock b/uv.lock
index 350e2e3..49a0887 100644
--- a/uv.lock
+++ b/uv.lock
@@ -338,6 +338,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" },
]
+[[package]]
+name = "duckdb"
+version = "1.4.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/da/17c3eb5458af69d54dedc8d18e4a32ceaa8ce4d4c699d45d6d8287e790c3/duckdb-1.4.3.tar.gz", hash = "sha256:fea43e03604c713e25a25211ada87d30cd2a044d8f27afab5deba26ac49e5268", size = 18478418, upload-time = "2025-12-09T10:59:22.945Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/f4/a38651e478fa41eeb8e43a0a9c0d4cd8633adea856e3ac5ac95124b0fdbf/duckdb-1.4.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:316711a9e852bcfe1ed6241a5f654983f67e909e290495f3562cccdf43be8180", size = 29042272, upload-time = "2025-12-09T10:58:51.826Z" },
+ { url = "https://files.pythonhosted.org/packages/16/de/2cf171a66098ce5aeeb7371511bd2b3d7b73a2090603b0b9df39f8aaf814/duckdb-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9e625b2b4d52bafa1fd0ebdb0990c3961dac8bb00e30d327185de95b68202131", size = 15419343, upload-time = "2025-12-09T10:58:54.439Z" },
+ { url = "https://files.pythonhosted.org/packages/35/28/6b0a7830828d4e9a37420d87e80fe6171d2869a9d3d960bf5d7c3b8c7ee4/duckdb-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:130c6760f6c573f9c9fe9aba56adba0fab48811a4871b7b8fd667318b4a3e8da", size = 13748905, upload-time = "2025-12-09T10:58:56.656Z" },
+ { url = "https://files.pythonhosted.org/packages/15/4d/778628e194d63967870873b9581c8a6b4626974aa4fbe09f32708a2d3d3a/duckdb-1.4.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20c88effaa557a11267706b01419c542fe42f893dee66e5a6daa5974ea2d4a46", size = 18487261, upload-time = "2025-12-09T10:58:58.866Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/5f/87e43af2e4a0135f9675449563e7c2f9b6f1fe6a2d1691c96b091f3904dd/duckdb-1.4.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b35491db98ccd11d151165497c084a9d29d3dc42fc80abea2715a6c861ca43d", size = 20497138, upload-time = "2025-12-09T10:59:01.241Z" },
+ { url = "https://files.pythonhosted.org/packages/94/41/abec537cc7c519121a2a83b9a6f180af8915fabb433777dc147744513e74/duckdb-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:23b12854032c1a58d0452e2b212afa908d4ce64171862f3792ba9a596ba7c765", size = 12836056, upload-time = "2025-12-09T10:59:03.388Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5a/8af5b96ce5622b6168854f479ce846cf7fb589813dcc7d8724233c37ded3/duckdb-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:90f241f25cffe7241bf9f376754a5845c74775e00e1c5731119dc88cd71e0cb2", size = 13527759, upload-time = "2025-12-09T10:59:05.496Z" },
+]
+
[[package]]
name = "executing"
version = "2.2.1"
@@ -850,8 +865,10 @@ version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "backtesting" },
+ { name = "duckdb" },
{ name = "jupyter" },
{ name = "matplotlib" },
+ { name = "mplfinance" },
{ name = "pandas" },
{ name = "pandas-stubs" },
{ name = "peewee" },
@@ -861,8 +878,10 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "backtesting", specifier = "~=0.6.5" },
+ { name = "duckdb", specifier = ">=1.4.3" },
{ name = "jupyter", specifier = "~=1.1.1" },
{ name = "matplotlib", specifier = "~=3.10.8" },
+ { name = "mplfinance", specifier = ">=0.12.10b0" },
{ name = "pandas", specifier = "~=2.3.3" },
{ name = "pandas-stubs", specifier = "~=2.3.3" },
{ name = "peewee", specifier = "~=3.19.0" },
@@ -953,6 +972,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" },
]
+[[package]]
+name = "mplfinance"
+version = "0.12.10b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "matplotlib" },
+ { name = "pandas" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6c/a9/34e7998d02fb58fae04f750444ce4e95e75f3a08dad17fb2d32098a97834/mplfinance-0.12.10b0.tar.gz", hash = "sha256:7da150b5851aa5119ad6e06b55e48338b619bb6773f1b85df5de67a5ffd917bf", size = 70117, upload-time = "2023-08-02T15:13:53.829Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d7/d9/31c436ea7673c21a5bf3fc747bc7f63377582dfe845c3004d3e46f9deee0/mplfinance-0.12.10b0-py3-none-any.whl", hash = "sha256:76d3b095f05ff35de730751649de063bea4064d0c49b21b6182c82997a7f52bb", size = 75016, upload-time = "2023-08-02T15:13:52.022Z" },
+]
+
[[package]]
name = "narwhals"
version = "2.15.0"