Compare commits
2 Commits
master
...
5554abc245
| Author | SHA1 | Date | |
|---|---|---|---|
| 5554abc245 | |||
| 9759720fec |
7
.idea/dataSources.xml
generated
7
.idea/dataSources.xml
generated
@@ -8,12 +8,5 @@
|
||||
<jdbc-url>jdbc:postgresql://81.71.3.24:6785/leopard_dev</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="leopard.sqlite" uuid="c9e16f8e-81be-45cf-847c-47a6750eeee2">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/Documents/leopard_data/leopard.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/data_source_mapping.xml
generated
7
.idea/data_source_mapping.xml
generated
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourcePerFileMappings">
|
||||
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/bd7b5f2a-eb99-4aad-81ec-1fec76b3d7fc/console.sql" value="bd7b5f2a-eb99-4aad-81ec-1fec76b3d7fc" />
|
||||
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/c9e16f8e-81be-45cf-847c-47a6750eeee2/console.sql" value="c9e16f8e-81be-45cf-847c-47a6750eeee2" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/db-forest-config.xml
generated
6
.idea/db-forest-config.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="db-tree-configuration">
|
||||
<option name="data" value="" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/sqldialects.xml
generated
1
.idea/sqldialects.xml
generated
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/sql/initial.sql" dialect="SQLite" />
|
||||
<file url="PROJECT" dialect="PostgreSQL" />
|
||||
</component>
|
||||
</project>
|
||||
136
data.py
136
data.py
@@ -1,136 +0,0 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from time import sleep
|
||||
|
||||
from sqlalchemy import Column, Double, Integer, String, create_engine
|
||||
from sqlalchemy.orm import DeclarativeBase, Session
|
||||
from tushare import pro_api
|
||||
|
||||
TUSHARE_API_KEY = '64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e'
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class Stock(Base):
|
||||
__tablename__ = 'stock'
|
||||
|
||||
code = Column(String, primary_key=True, comment="代码")
|
||||
name = Column(String, comment="名称")
|
||||
fullname = Column(String, comment="全名")
|
||||
market = Column(String, comment="市场")
|
||||
exchange = Column(String, comment="交易所")
|
||||
industry = Column(String, comment="行业")
|
||||
list_date = Column(String, comment="上市日期")
|
||||
|
||||
|
||||
class Daily(Base):
|
||||
__tablename__ = 'daily'
|
||||
|
||||
code = Column(String, primary_key=True)
|
||||
trade_date = Column(String, primary_key=True)
|
||||
open = Column(Double)
|
||||
close = Column(Double)
|
||||
high = Column(Double)
|
||||
low = Column(Double)
|
||||
previous_close = Column(Double)
|
||||
turnover = Column(Double)
|
||||
volume = Column(Integer)
|
||||
price_change_amount = Column(Double)
|
||||
factor = Column(Double)
|
||||
|
||||
|
||||
def main():
|
||||
print("开始更新数据")
|
||||
|
||||
engine = create_engine(f"sqlite:////Users/lanyuanxiaoyao/Documents/leopard_data/leopard.sqlite")
|
||||
try:
|
||||
Stock.metadata.create_all(engine, checkfirst=True)
|
||||
Daily.metadata.create_all(engine, checkfirst=True)
|
||||
pro = pro_api(TUSHARE_API_KEY)
|
||||
# with engine.connect() as connection:
|
||||
# stocks = pro.stock_basic(list_status="L", market="主板", fields="ts_code,name,fullname,market,exchange,industry,list_date")
|
||||
# for row in stocks.itertuples():
|
||||
# stmt = insert(Stock).values(
|
||||
# code=row.ts_code,
|
||||
# name=row.name,
|
||||
# fullname=row.fullname,
|
||||
# market=row.market,
|
||||
# exchange=row.exchange,
|
||||
# industry=row.industry,
|
||||
# list_date=row.list_date,
|
||||
# )
|
||||
# stmt = stmt.on_conflict_do_update(
|
||||
# index_elements=["code"],
|
||||
# set_={
|
||||
# "name": stmt.excluded.name,
|
||||
# "fullname": stmt.excluded.fullname,
|
||||
# "market": stmt.excluded.market,
|
||||
# "exchange": stmt.excluded.exchange,
|
||||
# "industry": stmt.excluded.industry,
|
||||
# "list_date": stmt.excluded.list_date,
|
||||
# },
|
||||
# )
|
||||
# print(stmt)
|
||||
# connection.execute(stmt)
|
||||
# connection.commit()
|
||||
#
|
||||
# print("清理行情数据")
|
||||
# connection.execute(text("delete from daily where code not in (select distinct code from stock)"))
|
||||
# connection.commit()
|
||||
#
|
||||
# print("清理财务数据")
|
||||
# connection.execute(text("delete from finance_indicator where code not in (select distinct code from stock)"))
|
||||
# connection.commit()
|
||||
|
||||
with Session(engine) as session:
|
||||
stock_codes = [row[0] for row in session.query(Stock.code).all()]
|
||||
|
||||
latest_date = session.query(Daily.trade_date).order_by(Daily.trade_date.desc()).first()
|
||||
if latest_date is None:
|
||||
latest_date = '1990-12-19'
|
||||
else:
|
||||
latest_date = latest_date.trade_date
|
||||
latest_date = datetime.strptime(latest_date, '%Y-%m-%d').date()
|
||||
current_date = date.today() - timedelta(days=1)
|
||||
delta = (current_date - latest_date).days
|
||||
print(f"最新数据日期:{latest_date},当前日期:{current_date},待更新天数:{delta}")
|
||||
if delta > 0:
|
||||
update_dates = []
|
||||
for i in range(delta):
|
||||
latest_date = latest_date + timedelta(days=1)
|
||||
update_dates.append(latest_date.strftime('%Y%m%d'))
|
||||
for target_date in update_dates:
|
||||
print(f"正在采集:{target_date}")
|
||||
dailies = pro.daily(trade_date=target_date)
|
||||
dailies.set_index("ts_code", inplace=True)
|
||||
factors = pro.adj_factor(trade_date=target_date)
|
||||
factors.set_index("ts_code", inplace=True)
|
||||
results = dailies.join(factors, lsuffix="_daily", rsuffix="_factor", how="left")
|
||||
rows = []
|
||||
for row in results.itertuples():
|
||||
if row.Index in stock_codes:
|
||||
rows.append(
|
||||
Daily(
|
||||
code=row.Index,
|
||||
trade_date=datetime.strptime(target_date, '%Y%m%d').strftime("%Y-%m-%d"),
|
||||
open=row.open,
|
||||
close=row.close,
|
||||
high=row.high,
|
||||
low=row.low,
|
||||
previous_close=row.pre_close,
|
||||
turnover=row.amount,
|
||||
volume=row.vol,
|
||||
price_change_amount=row.pct_chg,
|
||||
factor=row.adj_factor,
|
||||
)
|
||||
)
|
||||
session.add_all(rows)
|
||||
session.commit()
|
||||
sleep(1)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
337
notebook/__marimo__/backtest.ipynb
Normal file
337
notebook/__marimo__/backtest.ipynb
Normal file
File diff suppressed because one or more lines are too long
222
notebook/__marimo__/indicator.ipynb
Normal file
222
notebook/__marimo__/indicator.ipynb
Normal file
File diff suppressed because one or more lines are too long
182
notebook/__marimo__/session/backtest.py.json
Normal file
182
notebook/__marimo__/session/backtest.py.json
Normal file
File diff suppressed because one or more lines are too long
111
notebook/__marimo__/session/indicator.py.json
Normal file
111
notebook/__marimo__/session/indicator.py.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
184
notebook/backtest.py
Normal file
184
notebook/backtest.py
Normal file
@@ -0,0 +1,184 @@
|
||||
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()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,455 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2026-01-30T05:41:51.291397Z",
|
||||
"start_time": "2026-01-30T04:34:22.917761Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import urllib.parse\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"import sqlalchemy\n",
|
||||
"from sqlalchemy import text\n",
|
||||
"from sqlalchemy.orm import DeclarativeBase, Session\n",
|
||||
"\n",
|
||||
"postgresql_engin = sqlalchemy.create_engine(\n",
|
||||
" f\"postgresql://leopard:{urllib.parse.quote_plus(\"9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X\")}@81.71.3.24:6785/leopard\"\n",
|
||||
")\n",
|
||||
"sqlite_engine = sqlalchemy.create_engine(f\"sqlite:////Users/lanyuanxiaoyao/Documents/leopard_data/leopard.sqlite\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Base(DeclarativeBase):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Daily(Base):\n",
|
||||
" __tablename__ = 'daily'\n",
|
||||
"\n",
|
||||
" code = sqlalchemy.Column(sqlalchemy.String, primary_key=True)\n",
|
||||
" trade_date = sqlalchemy.Column(sqlalchemy.Date, primary_key=True)\n",
|
||||
" open = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" close = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" high = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" low = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" previous_close = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" turnover = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" volume = sqlalchemy.Column(sqlalchemy.Integer)\n",
|
||||
" price_change_amount = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
" factor = sqlalchemy.Column(sqlalchemy.Double)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" with Session(postgresql_engin) as pg_session:\n",
|
||||
" results = pg_session.execute(text(\"select distinct trade_date from leopard_daily\")).fetchall()\n",
|
||||
" results = list(map(lambda x: x[0].strftime(\"%Y-%m-%d\"), results))\n",
|
||||
" dates = [results[i: i + 30] for i in range(0, len(results), 30)]\n",
|
||||
"\n",
|
||||
" for index, date in enumerate(dates):\n",
|
||||
" print(date)\n",
|
||||
" daily_df = pd.read_sql(\n",
|
||||
" f\"\"\"\n",
|
||||
" select code,\n",
|
||||
" trade_date,\n",
|
||||
" open,\n",
|
||||
" close,\n",
|
||||
" high,\n",
|
||||
" low,\n",
|
||||
" previous_close,\n",
|
||||
" turnover,\n",
|
||||
" volume,\n",
|
||||
" price_change_amount,\n",
|
||||
" factor\n",
|
||||
" from leopard_daily d\n",
|
||||
" left join leopard_stock s on d.stock_id = s.id\n",
|
||||
" where d.trade_date in ('{\"','\".join(date)}')\n",
|
||||
" \"\"\",\n",
|
||||
" postgresql_engin\n",
|
||||
" )\n",
|
||||
" with Session(sqlite_engine) as session:\n",
|
||||
" rows = []\n",
|
||||
" for _, row in daily_df.iterrows():\n",
|
||||
" rows.append(\n",
|
||||
" Daily(\n",
|
||||
" code=row[\"code\"],\n",
|
||||
" trade_date=row[\"trade_date\"],\n",
|
||||
" open=row[\"open\"],\n",
|
||||
" close=row[\"close\"],\n",
|
||||
" high=row[\"high\"],\n",
|
||||
" low=row[\"low\"],\n",
|
||||
" previous_close=row[\"previous_close\"],\n",
|
||||
" turnover=row[\"turnover\"],\n",
|
||||
" volume=row[\"volume\"],\n",
|
||||
" price_change_amount=row[\"price_change_amount\"],\n",
|
||||
" factor=row[\"factor\"]\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" session.add_all(rows)\n",
|
||||
" session.commit()\n",
|
||||
"finally:\n",
|
||||
" postgresql_engin.dispose()\n",
|
||||
" sqlite_engine.dispose()"
|
||||
],
|
||||
"id": "48821306efc640a1",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['2025-12-25', '2025-12-26', '2025-12-29', '2025-12-30', '2025-12-31', '2026-01-05', '2026-01-06', '2026-01-07', '2026-01-08', '2026-01-09']\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"execution_count": 22
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2026-01-30T09:24:09.859231Z",
|
||||
"start_time": "2026-01-30T09:24:09.746912Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import tushare as ts\n",
|
||||
"\n",
|
||||
"pro = ts.pro_api(\"64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e\")\n",
|
||||
"# stocks = pro.stock_basic(ts_code=\"600200.SH\", list_status=\"D\", fields=\"ts_code,name,fullname,market,exchange,industry,list_date,delist_date\")\n",
|
||||
"# stocks"
|
||||
],
|
||||
"id": "ed58a1faaf2cdb8e",
|
||||
"outputs": [],
|
||||
"execution_count": 34
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2026-01-30T07:14:29.897120Z",
|
||||
"start_time": "2026-01-30T07:14:29.664124Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": "# stocks.to_csv(\"dlist.csv\")",
|
||||
"id": "3c8c0a38d6b2992e",
|
||||
"outputs": [],
|
||||
"execution_count": 24
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2026-01-30T09:46:34.808300Z",
|
||||
"start_time": "2026-01-30T09:46:34.129412Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"daily_df = pro.daily(trade_date=\"20251231\")\n",
|
||||
"daily_df.set_index(\"ts_code\", inplace=True)\n",
|
||||
"factor_df = pro.adj_factor(trade_date=\"20251231\")\n",
|
||||
"factor_df.set_index(\"ts_code\", inplace=True)"
|
||||
],
|
||||
"id": "c052a945869aa329",
|
||||
"outputs": [],
|
||||
"execution_count": 50
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2026-01-30T09:46:36.697015Z",
|
||||
"start_time": "2026-01-30T09:46:36.642975Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"result_df = daily_df.join(factor_df, lsuffix=\"_daily\", rsuffix=\"_factor\", how=\"left\")\n",
|
||||
"result_df\n",
|
||||
"# factor_df"
|
||||
],
|
||||
"id": "d61ee80d2cd9f06b",
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
" trade_date_daily open high low close pre_close change \\\n",
|
||||
"ts_code \n",
|
||||
"000001.SZ 20251231 11.48 11.49 11.40 11.41 11.48 -0.07 \n",
|
||||
"000002.SZ 20251231 4.66 4.68 4.62 4.65 4.62 0.03 \n",
|
||||
"000004.SZ 20251231 11.30 11.35 11.07 11.08 11.27 -0.19 \n",
|
||||
"000006.SZ 20251231 9.95 10.03 9.69 9.95 9.86 0.09 \n",
|
||||
"000007.SZ 20251231 11.72 11.75 11.28 11.44 11.62 -0.18 \n",
|
||||
"... ... ... ... ... ... ... ... \n",
|
||||
"920978.BJ 20251231 37.64 38.39 36.88 36.90 37.78 -0.88 \n",
|
||||
"920981.BJ 20251231 32.20 32.29 31.75 31.96 32.07 -0.11 \n",
|
||||
"920982.BJ 20251231 233.00 238.49 232.10 233.70 234.80 -1.10 \n",
|
||||
"920985.BJ 20251231 7.32 7.35 7.17 7.19 7.30 -0.11 \n",
|
||||
"920992.BJ 20251231 17.33 17.60 17.29 17.39 17.38 0.01 \n",
|
||||
"\n",
|
||||
" pct_chg vol amount trade_date_factor adj_factor \n",
|
||||
"ts_code \n",
|
||||
"000001.SZ -0.6098 590620.37 675457.357 20251231 134.5794 \n",
|
||||
"000002.SZ 0.6494 1075561.25 499883.113 20251231 181.7040 \n",
|
||||
"000004.SZ -1.6859 18056.00 20248.567 20251231 4.0640 \n",
|
||||
"000006.SZ 0.9128 270369.08 267758.676 20251231 39.7400 \n",
|
||||
"000007.SZ -1.5491 80556.00 92109.366 20251231 8.2840 \n",
|
||||
"... ... ... ... ... ... \n",
|
||||
"920978.BJ -2.3293 33945.04 126954.937 20251231 1.2885 \n",
|
||||
"920981.BJ -0.3430 8237.16 26301.206 20251231 1.4343 \n",
|
||||
"920982.BJ -0.4685 5210.09 122452.646 20251231 4.2831 \n",
|
||||
"920985.BJ -1.5068 35174.30 25350.257 20251231 1.6280 \n",
|
||||
"920992.BJ 0.0575 6991.87 12193.445 20251231 1.4932 \n",
|
||||
"\n",
|
||||
"[5458 rows x 12 columns]"
|
||||
],
|
||||
"text/html": [
|
||||
"<div>\n",
|
||||
"<style scoped>\n",
|
||||
" .dataframe tbody tr th:only-of-type {\n",
|
||||
" vertical-align: middle;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe tbody tr th {\n",
|
||||
" vertical-align: top;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe thead th {\n",
|
||||
" text-align: right;\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>trade_date_daily</th>\n",
|
||||
" <th>open</th>\n",
|
||||
" <th>high</th>\n",
|
||||
" <th>low</th>\n",
|
||||
" <th>close</th>\n",
|
||||
" <th>pre_close</th>\n",
|
||||
" <th>change</th>\n",
|
||||
" <th>pct_chg</th>\n",
|
||||
" <th>vol</th>\n",
|
||||
" <th>amount</th>\n",
|
||||
" <th>trade_date_factor</th>\n",
|
||||
" <th>adj_factor</th>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>ts_code</th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>000001.SZ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>11.48</td>\n",
|
||||
" <td>11.49</td>\n",
|
||||
" <td>11.40</td>\n",
|
||||
" <td>11.41</td>\n",
|
||||
" <td>11.48</td>\n",
|
||||
" <td>-0.07</td>\n",
|
||||
" <td>-0.6098</td>\n",
|
||||
" <td>590620.37</td>\n",
|
||||
" <td>675457.357</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>134.5794</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>000002.SZ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>4.66</td>\n",
|
||||
" <td>4.68</td>\n",
|
||||
" <td>4.62</td>\n",
|
||||
" <td>4.65</td>\n",
|
||||
" <td>4.62</td>\n",
|
||||
" <td>0.03</td>\n",
|
||||
" <td>0.6494</td>\n",
|
||||
" <td>1075561.25</td>\n",
|
||||
" <td>499883.113</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>181.7040</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>000004.SZ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>11.30</td>\n",
|
||||
" <td>11.35</td>\n",
|
||||
" <td>11.07</td>\n",
|
||||
" <td>11.08</td>\n",
|
||||
" <td>11.27</td>\n",
|
||||
" <td>-0.19</td>\n",
|
||||
" <td>-1.6859</td>\n",
|
||||
" <td>18056.00</td>\n",
|
||||
" <td>20248.567</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>4.0640</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>000006.SZ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>9.95</td>\n",
|
||||
" <td>10.03</td>\n",
|
||||
" <td>9.69</td>\n",
|
||||
" <td>9.95</td>\n",
|
||||
" <td>9.86</td>\n",
|
||||
" <td>0.09</td>\n",
|
||||
" <td>0.9128</td>\n",
|
||||
" <td>270369.08</td>\n",
|
||||
" <td>267758.676</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>39.7400</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>000007.SZ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>11.72</td>\n",
|
||||
" <td>11.75</td>\n",
|
||||
" <td>11.28</td>\n",
|
||||
" <td>11.44</td>\n",
|
||||
" <td>11.62</td>\n",
|
||||
" <td>-0.18</td>\n",
|
||||
" <td>-1.5491</td>\n",
|
||||
" <td>80556.00</td>\n",
|
||||
" <td>92109.366</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>8.2840</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>...</th>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" <td>...</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>920978.BJ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>37.64</td>\n",
|
||||
" <td>38.39</td>\n",
|
||||
" <td>36.88</td>\n",
|
||||
" <td>36.90</td>\n",
|
||||
" <td>37.78</td>\n",
|
||||
" <td>-0.88</td>\n",
|
||||
" <td>-2.3293</td>\n",
|
||||
" <td>33945.04</td>\n",
|
||||
" <td>126954.937</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>1.2885</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>920981.BJ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>32.20</td>\n",
|
||||
" <td>32.29</td>\n",
|
||||
" <td>31.75</td>\n",
|
||||
" <td>31.96</td>\n",
|
||||
" <td>32.07</td>\n",
|
||||
" <td>-0.11</td>\n",
|
||||
" <td>-0.3430</td>\n",
|
||||
" <td>8237.16</td>\n",
|
||||
" <td>26301.206</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>1.4343</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>920982.BJ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>233.00</td>\n",
|
||||
" <td>238.49</td>\n",
|
||||
" <td>232.10</td>\n",
|
||||
" <td>233.70</td>\n",
|
||||
" <td>234.80</td>\n",
|
||||
" <td>-1.10</td>\n",
|
||||
" <td>-0.4685</td>\n",
|
||||
" <td>5210.09</td>\n",
|
||||
" <td>122452.646</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>4.2831</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>920985.BJ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>7.32</td>\n",
|
||||
" <td>7.35</td>\n",
|
||||
" <td>7.17</td>\n",
|
||||
" <td>7.19</td>\n",
|
||||
" <td>7.30</td>\n",
|
||||
" <td>-0.11</td>\n",
|
||||
" <td>-1.5068</td>\n",
|
||||
" <td>35174.30</td>\n",
|
||||
" <td>25350.257</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>1.6280</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>920992.BJ</th>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>17.33</td>\n",
|
||||
" <td>17.60</td>\n",
|
||||
" <td>17.29</td>\n",
|
||||
" <td>17.39</td>\n",
|
||||
" <td>17.38</td>\n",
|
||||
" <td>0.01</td>\n",
|
||||
" <td>0.0575</td>\n",
|
||||
" <td>6991.87</td>\n",
|
||||
" <td>12193.445</td>\n",
|
||||
" <td>20251231</td>\n",
|
||||
" <td>1.4932</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"<p>5458 rows × 12 columns</p>\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
"execution_count": 51,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"execution_count": 51
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
131
notebook/indicator.py
Normal file
131
notebook/indicator.py
Normal file
@@ -0,0 +1,131 @@
|
||||
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["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['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)
|
||||
|
||||
macd, signal, hist = talib.MACD(dailies_df["Close"], fastperiod=10, slowperiod=20, signalperiod=9)
|
||||
dailies_df['macd'] = macd
|
||||
dailies_df['signal'] = signal
|
||||
dailies_df['hist'] = hist
|
||||
|
||||
target_dailies_df = dailies_df.loc['2025-01-01':'2025-12-31']
|
||||
target_dailies_df
|
||||
return (target_dailies_df,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(target_dailies_df):
|
||||
target_dailies_df.reset_index(inplace=True)
|
||||
target_dailies_df_inc = target_dailies_df[target_dailies_df["Close"] > target_dailies_df["Open"]]
|
||||
target_dailies_df_dec = target_dailies_df[target_dailies_df["Close"] < target_dailies_df["Open"]]
|
||||
return target_dailies_df_dec, target_dailies_df_inc
|
||||
|
||||
|
||||
@app.cell
|
||||
def daily_chart(
|
||||
target_dailies_df,
|
||||
target_dailies_df_dec,
|
||||
target_dailies_df_inc,
|
||||
):
|
||||
from bokeh.io import show, output_notebook
|
||||
from bokeh.layouts import column
|
||||
from bokeh.plotting import figure
|
||||
|
||||
output_notebook()
|
||||
|
||||
width = 1200
|
||||
up_color = "black"
|
||||
down_color = "grey"
|
||||
|
||||
price_figure = figure(width=width, height=400, tools='pan,wheel_zoom,box_zoom,reset')
|
||||
|
||||
price_figure.min_border = 0
|
||||
|
||||
x_padding = 1
|
||||
y_padding = 10
|
||||
price_figure.x_range.bounds = (min(target_dailies_df.index) - x_padding, max(target_dailies_df.index) + x_padding)
|
||||
price_figure.y_range.bounds = (min(target_dailies_df['Low']) - y_padding, max(target_dailies_df['High']) + y_padding)
|
||||
price_figure.xaxis.major_label_overrides = {i: date.strftime('%Y-%m-%d') for i, date in zip(target_dailies_df.index, target_dailies_df['trade_date'])}
|
||||
price_figure.segment(target_dailies_df.index, target_dailies_df['High'], target_dailies_df.index, target_dailies_df['Low'], color='black')
|
||||
price_figure.vbar(target_dailies_df_inc.index, 0.6, target_dailies_df_inc['Open'], target_dailies_df_inc['Close'], color=up_color)
|
||||
price_figure.vbar(target_dailies_df_dec.index, 0.6, target_dailies_df_dec['Open'], target_dailies_df_dec['Close'], color=down_color)
|
||||
price_figure.line(target_dailies_df.index, target_dailies_df['sma30'], color='orange')
|
||||
price_figure.line(target_dailies_df.index, target_dailies_df['sma60'], color='red')
|
||||
# 控制图表只能放大不能缩小
|
||||
macd_figure = figure(width=width, height=200, tools='pan,wheel_zoom,box_zoom,reset')
|
||||
macd_figure.x_range.bounds = (min(target_dailies_df.index) - x_padding, max(target_dailies_df.index) + x_padding)
|
||||
macd_figure.y_range.bounds = (min(target_dailies_df['macd']) - y_padding, max(target_dailies_df['macd']) + y_padding)
|
||||
macd_figure.line(target_dailies_df.index, target_dailies_df['macd'], color='orange')
|
||||
macd_figure.line(target_dailies_df.index, target_dailies_df['signal'], color='red')
|
||||
# Add MACD histogram bars for positive and negative values
|
||||
macd_positive = target_dailies_df[target_dailies_df['macd'] > 0]
|
||||
macd_negative = target_dailies_df[target_dailies_df['macd'] < 0]
|
||||
|
||||
macd_figure.vbar(macd_positive.index, 0.6, 0, macd_positive['macd'], color=up_color)
|
||||
macd_figure.vbar(macd_negative.index, 0.6, 0, macd_negative['macd'], color=down_color)
|
||||
|
||||
# Add zero line
|
||||
macd_figure.line(target_dailies_df.index, [0] * len(target_dailies_df), color='black', line_dash='dashed')
|
||||
# show(price_figure)
|
||||
# show(macd_figure)
|
||||
show(column(price_figure, macd_figure))
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -4,13 +4,10 @@ version = "0.1.0"
|
||||
description = "Stock analysis"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"adata>=2.9.5",
|
||||
"akshare>=1.18.20",
|
||||
"backtesting~=0.6.5",
|
||||
"baostock>=0.8.9",
|
||||
"duckdb>=1.4.4",
|
||||
"jupyter~=1.1.1",
|
||||
"jupyter-bokeh>=4.0.5",
|
||||
"marimo>=0.19.6",
|
||||
"matplotlib~=3.10.8",
|
||||
"mplfinance>=0.12.10b0",
|
||||
"pandas~=2.3.3",
|
||||
@@ -21,5 +18,13 @@ dependencies = [
|
||||
"ta-lib>=0.6.8",
|
||||
"tabulate>=0.9.0",
|
||||
"tqdm>=4.67.1",
|
||||
"tushare>=1.4.24",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"mcp>=1",
|
||||
"openai>=2.16.0",
|
||||
"pydantic>=2",
|
||||
"pydantic-ai>=1.48.0",
|
||||
"ruff>=0.14.14",
|
||||
]
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
CREATE TABLE stock
|
||||
(
|
||||
code varchar not null,
|
||||
name varchar not null,
|
||||
fullname varchar,
|
||||
industry varchar,
|
||||
listed_date date,
|
||||
market varchar,
|
||||
exchange varchar,
|
||||
primary key (code)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE daily
|
||||
(
|
||||
code varchar not null,
|
||||
trade_date date not null,
|
||||
open double,
|
||||
close double,
|
||||
high double,
|
||||
low double,
|
||||
previous_close double,
|
||||
turnover double,
|
||||
volume integer,
|
||||
price_change_amount double,
|
||||
factor double,
|
||||
primary key (code, trade_date)
|
||||
);
|
||||
|
||||
CREATE TABLE finance_indicator
|
||||
(
|
||||
code varchar not null,
|
||||
year integer not null,
|
||||
accounts_payable double,
|
||||
accounts_payable_to_total_assets_ratio double,
|
||||
accounts_receivable double,
|
||||
accounts_receivable_to_total_assets_ratio double,
|
||||
accounts_receivable_turnover double,
|
||||
capital_surplus double,
|
||||
cash_and_cash_equivalents double,
|
||||
cash_and_cash_equivalents_to_total_assets_ratio double,
|
||||
cash_flow_adequacy_ratio double,
|
||||
cash_flow_from_financing_activities double,
|
||||
cash_flow_from_investing_activities double,
|
||||
cash_flow_from_operating_activities double,
|
||||
cash_flow_ratio double,
|
||||
cash_reinvestment_ratio double,
|
||||
current_assets double,
|
||||
current_assets_to_total_assets_ratio double,
|
||||
current_liabilities double,
|
||||
current_liabilities_to_total_assets_ratio double,
|
||||
current_liabilities_to_total_liabilities_ratio double,
|
||||
current_ratio double,
|
||||
days_accounts_receivable_turnover double,
|
||||
days_fixed_assets_turnover double,
|
||||
days_inventory_turnover double,
|
||||
days_total_assets_turnover double,
|
||||
earnings_per_share double,
|
||||
fixed_assets double,
|
||||
fixed_assets_to_total_assets_ratio double,
|
||||
fixed_assets_turnover double,
|
||||
goodwill double,
|
||||
goodwill_to_total_assets_ratio double,
|
||||
inventory double,
|
||||
inventory_to_total_assets_ratio double,
|
||||
inventory_turnover double,
|
||||
liabilities_to_total_assets_ratio double,
|
||||
long_term_funds_to_fixed_assets_ratio double,
|
||||
long_term_liabilities double,
|
||||
long_term_liabilities_to_total_assets_ratio double,
|
||||
long_term_liabilities_to_total_liabilities_ratio double,
|
||||
net_cash_flow_from_operating_activities double,
|
||||
net_profit double,
|
||||
net_profit_margin double,
|
||||
operating_cost double,
|
||||
operating_expenses double,
|
||||
operating_gross_profit_margin double,
|
||||
operating_profit double,
|
||||
operating_profit_margin double,
|
||||
operating_revenue double,
|
||||
operating_safety_margin_ratio double,
|
||||
quick_ratio double,
|
||||
return_on_assets double,
|
||||
return_on_equity double,
|
||||
shareholders_equity double,
|
||||
shareholders_equity_to_total_assets_ratio double,
|
||||
surplus_reserve double,
|
||||
total_assets double,
|
||||
total_assets_turnover double,
|
||||
total_liabilities double,
|
||||
total_share_capital double,
|
||||
undistributed_profit double,
|
||||
primary key (code, year)
|
||||
)
|
||||
Reference in New Issue
Block a user