|
1 | 1 | try: |
2 | | - import argparse |
3 | | - import importlib |
4 | | - import pandas as pd |
5 | | - import akshare as ak |
6 | | - import streamlit as st |
7 | | - from pathlib import Path |
8 | | - from quool import ParquetManager |
9 | | - from quool import Broker as QBroker |
10 | | - import plotly.subplots as sp |
11 | | - import plotly.graph_objects as go |
| 2 | + import quool.app.main as main |
| 3 | + import quool.app.runner as runner |
| 4 | + import quool.app.monitor as monitor |
| 5 | + import quool.app.strategy as strategy |
| 6 | + import quool.app.transact as transact |
| 7 | + import quool.app.performance as performance |
| 8 | + from .main import layout |
| 9 | + from .tool import LOG_PATH, BROKER_PATH, STRATEGIES_PATH, ASSET_PATH |
12 | 10 |
|
13 | 11 | except ImportError as e: |
14 | 12 | print(e) |
15 | | - |
16 | | - |
17 | | -def parsearg(): |
18 | | - parser = argparse.ArgumentParser() |
19 | | - parser.add_argument('-r', "--refresh", type=int, default=60, help="refresh interval") |
20 | | - parser.add_argument('-p', "--root", type=str, default="app", help="root path") |
21 | | - parser.add_argument('-k', "--kline", type=int, default=60, help="how many klines kept in memory") |
22 | | - args = parser.parse_args() |
23 | | - return args |
24 | | - |
25 | | -args = parsearg() |
26 | | -REFRESH_INTERVAL = args.refresh |
27 | | -ASSET_PATH = Path(args.root) |
28 | | -TEMPLATE_PATH = Path(ASSET_PATH) / "template" |
29 | | -BROKER_PATH = Path(ASSET_PATH) / "broker" |
30 | | -STRATEGIES_PATH = Path(ASSET_PATH) / "strategy" |
31 | | -LOG_PATH = Path(ASSET_PATH) / "log" |
32 | | -KEEP_KLINE = args.kline |
33 | | - |
34 | | - |
35 | | -def is_trading_time(time = None): |
36 | | - time = pd.to_datetime(time or 'now') |
37 | | - if ( |
38 | | - time.date() in ak.tool_trade_date_hist_sina().squeeze().to_list() |
39 | | - and ( |
40 | | - (time.time() >= pd.to_datetime("09:30:00").time() and time.time() <= pd.to_datetime("11:30:00").time()) |
41 | | - or (time.time() >= pd.to_datetime("13:00:00").time() and time.time() <= pd.to_datetime("15:00:00").time()) |
42 | | - ) |
43 | | - ): |
44 | | - return True |
45 | | - return False |
46 | | - |
47 | | -def raw2ricequant(code: str): |
48 | | - if code.startswith("6"): |
49 | | - return code + ".XSHG" |
50 | | - else: |
51 | | - return code + ".XSHE" |
52 | | - |
53 | | -def fetch_realtime(code_styler: callable = None): |
54 | | - data = ak.stock_zh_a_spot_em().set_index("代码", drop=True).drop(columns="序号") |
55 | | - data["open"] = data["最新价"] |
56 | | - data["high"] = data["最新价"] |
57 | | - data["low"] = data["最新价"] |
58 | | - data["close"] = data["最新价"] |
59 | | - data["volume"] = data["成交量"] |
60 | | - if code_styler is not None: |
61 | | - data.index = pd.MultiIndex.from_product([ |
62 | | - [pd.to_datetime("now")], data.index.map(code_styler) |
63 | | - ], names=["datetime", "code"]) |
64 | | - return data |
65 | | - |
66 | | -def fetch_kline(symbol, format=True): |
67 | | - data = ak.stock_zh_a_hist(symbol=symbol) |
68 | | - if format: |
69 | | - data = data.drop(columns="股票代码") |
70 | | - data = data.rename(columns={ |
71 | | - "日期": "datetime", "开盘": "open", "最高": "high", |
72 | | - "最低": "low", "收盘": "close", "成交量": "volume" |
73 | | - }) |
74 | | - data["datetime"] = pd.to_datetime(data["datetime"]) |
75 | | - data.set_index("datetime", inplace=True) |
76 | | - return data |
77 | | - |
78 | | -def read_market(begin, end, backadj: bool = True, extra: str = None): |
79 | | - if not (begin is None and end is None): |
80 | | - begin = begin or pd.Timestamp("2015-01-01") |
81 | | - end = end or pd.Timestamp.now() |
82 | | - quotes_day = ParquetManager("D:/Documents/DataBase/quotes_day") |
83 | | - data = quotes_day.read( |
84 | | - date__ge=begin, date__le=end, index=["date", "code"], |
85 | | - columns=["open", "high", "low", "close", "volume"] + (extra.split(',') if extra else []) |
86 | | - ) |
87 | | - if backadj: |
88 | | - adj = quotes_day.read( |
89 | | - date__ge=begin, date__le=end, index=["date", "code"], columns=["adjfactor"] |
90 | | - ) |
91 | | - data.loc[:, ["open", "high", "low", "close"]] = data[["open", "high", "low", "close"]].mul(adj["adjfactor"], axis=0) |
92 | | - return data |
93 | | - else: |
94 | | - return None |
95 | | - |
96 | | -def update_strategy(name): |
97 | | - st.session_state.strategy = importlib.reload(importlib.import_module( |
98 | | - str(STRATEGIES_PATH / name).replace("/", ".").replace("\\", ".") |
99 | | - )) |
100 | | - params = getattr(st.session_state.strategy, "params", None) |
101 | | - update = getattr(st.session_state.strategy, "update", None) |
102 | | - init = getattr(st.session_state.strategy, "init", None) |
103 | | - if params is None or update is None or init is None: |
104 | | - raise ValueError("Strategy must have params, update and init functions") |
105 | | - |
106 | | -def update_market(): |
107 | | - timepoints = st.session_state.timepoints |
108 | | - market = fetch_realtime(st.session_state.styler) |
109 | | - if timepoints.size >= KEEP_KLINE: |
110 | | - st.session_state.market = pd.concat([ |
111 | | - st.session_state.market.loc[timepoints[-239]:, :], market |
112 | | - ]) |
113 | | - else: |
114 | | - st.session_state.market = pd.concat([st.session_state.market, market]) |
115 | | - st.session_state.timepoints = st.session_state.market.index.get_level_values(0).unique() |
116 | | - |
117 | | -@st.fragment(run_every=REFRESH_INTERVAL) |
118 | | -def update_broker(): |
119 | | - broker = st.session_state.get('broker') |
120 | | - strategy = st.session_state.get('strategy') |
121 | | - strategy_stop = st.session_state.get('strategy_stop', True) |
122 | | - strategy_args = st.session_state.get('strategy_args', {}) |
123 | | - if broker is not None and is_trading_time(): |
124 | | - update_market() |
125 | | - market = st.session_state.market |
126 | | - print(market) |
127 | | - timepoints = market.index.get_level_values(0).unique() |
128 | | - market_now = market.loc[timepoints[-1]].copy() |
129 | | - market_pre = market.loc[timepoints[-2]] |
130 | | - market_now["open"] = market_pre["close"] |
131 | | - market_now["high"] = market_now["high"].max(market_pre["high"]) |
132 | | - market_now["low"] = market_now["low"].min(market_pre["low"]) |
133 | | - market_now["volume"] = market_now["volume"] - market_pre["volume"] |
134 | | - if not strategy_stop: |
135 | | - module = importlib.import_module( |
136 | | - str(strategy).replace("/", ".").replace("\\", ".")[:-3] |
137 | | - ) |
138 | | - getattr(module, "update")(broker, pd.to_datetime('now'), **strategy_args) |
139 | | - broker.update(time=pd.to_datetime('now'), market=market_now) |
140 | | - broker.store(BROKER_PATH / f"{broker.brokid}.json") |
141 | | - elif broker is not None: |
142 | | - broker.update(time=pd.to_datetime('now'), market=pd.DataFrame()) |
143 | | - broker.store(BROKER_PATH / f"{broker.brokid}.json") |
144 | | - |
145 | | -@st.fragment(run_every=REFRESH_INTERVAL) |
146 | | -def display_realtime(): |
147 | | - st.header("Realtime") |
148 | | - market = st.session_state.market.loc[st.session_state.timepoints[-1]] |
149 | | - selection = st.dataframe(market, selection_mode="single-row", on_select='rerun') |
150 | | - if selection['selection']["rows"]: |
151 | | - code = market.index[selection['selection']["rows"][0]] |
152 | | - name = market.loc[code, "名称"] |
153 | | - kline =fetch_kline(symbol=code) |
154 | | - fig = sp.make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.8, 0.2]) |
155 | | - fig.add_trace(go.Candlestick( |
156 | | - x=kline.index, |
157 | | - open=kline.open, |
158 | | - high=kline.high, |
159 | | - low=kline.low, |
160 | | - close=kline.close, |
161 | | - name=name, |
162 | | - ), row=1, col=1) |
163 | | - fig.add_trace(go.Bar( |
164 | | - x=kline.index, y=kline.volume, name="volume" |
165 | | - ), row=2, col=1) |
166 | | - st.plotly_chart(fig) |
167 | | - |
168 | | -@st.dialog("Edit your strategy", width="large") |
169 | | -def display_editor(code): |
170 | | - height = max(len(code.split("\n")) * 20, 68) |
171 | | - code = st.text_area(label="*edit your strategy*", value=code, height=height) |
172 | | - if st.button("save", use_container_width=True): |
173 | | - (st.session_state.strategy.__file__).write_text(code) |
174 | | - st.rerun() |
0 commit comments