Update app.py
Browse files
app.py
CHANGED
|
@@ -10,6 +10,18 @@ import importlib
|
|
| 10 |
import random
|
| 11 |
from datetime import datetime
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
# ========== 路径修复与导入别名(修复 from 策略基类 import 策略接口) ==========
|
| 14 |
def _修复导入路径与别名():
|
| 15 |
root = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -212,39 +224,49 @@ except Exception:
|
|
| 212 |
配置模块 = None
|
| 213 |
|
| 214 |
# ========== Gradio 界面 ==========
|
| 215 |
-
with gr.Blocks(title="模拟实盘(OKX 公共WS + 多策略引擎)") as demo:
|
| 216 |
-
gr.
|
| 217 |
-
gr.Markdown("
|
| 218 |
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
with gr.Column(scale=1):
|
| 223 |
-
合约输入 = gr.Textbox(label="合约ID", value="UXLINK-USDT-SWAP", placeholder="例:BTC-USDT-SWAP / ETH-USDT-SWAP")
|
| 224 |
-
周期选择 = gr.Radio(label="周期(分钟)", choices=["30", "60", "120", "240"], value="60")
|
| 225 |
-
参数输入 = gr.Textbox(label="策略参数(JSON)", value=json.dumps(默认参数, ensure_ascii=False, indent=2), lines=12)
|
| 226 |
with gr.Row():
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
# 构建设置面板(来自配置.py),返回关键组件用于与运行页联动
|
| 240 |
-
构件 = 配置模块.构建设置面板(容器=设置面板, 默认参数=默认参数, 记录函数=_记录)
|
| 241 |
-
# 可选:从设置面板“一键回填到运行页”
|
| 242 |
-
if isinstance(构件, dict) and "参数编辑框" in 构件 and "复制按钮" in 构件:
|
| 243 |
-
构件["复制按钮"].click(lambda s: s, inputs=构件["参数编辑框"], outputs=参数输入)
|
| 244 |
-
else:
|
| 245 |
-
gr.Markdown("未找到 配置.py,无法加载设置面板。")
|
| 246 |
|
| 247 |
-
#
|
| 248 |
设置按钮.click(lambda: (gr.update(visible=False), gr.update(visible=True)), outputs=[运行面板, 设置面板])
|
| 249 |
返回按钮.click(lambda: (gr.update(visible=True), gr.update(visible=False)), outputs=[运行面板, 设置面板])
|
| 250 |
|
|
|
|
| 10 |
import random
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
+
# ========== 主题与样式 ==========
|
| 14 |
+
theme = gr.themes.Soft()
|
| 15 |
+
css = """
|
| 16 |
+
#topbar { display:flex; align-items:center; justify-content:space-between;
|
| 17 |
+
padding:10px 14px; background:linear-gradient(90deg,#0f172a,#1f2937); color:#fff;
|
| 18 |
+
border-radius:14px; margin-bottom:10px }
|
| 19 |
+
#topbar h1 { font-size:18px; margin:0; font-weight:700 }
|
| 20 |
+
#topbar .right { display:flex; gap:8px; align-items:center }
|
| 21 |
+
.badge { padding:2px 8px; border-radius:999px; background:#10b981; color:#052e22; font-weight:700 }
|
| 22 |
+
.footer-note { opacity:.7; font-size:12px; }
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
# ========== 路径修复与导入别名(修复 from 策略基类 import 策略接口) ==========
|
| 26 |
def _修复导入路径与别名():
|
| 27 |
root = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
| 224 |
配置模块 = None
|
| 225 |
|
| 226 |
# ========== Gradio 界面 ==========
|
| 227 |
+
with gr.Blocks(title="模拟实盘(OKX 公共WS + 多策略引擎)", theme=theme, css=css) as demo:
|
| 228 |
+
gr.HTML('<div id="topbar"><h1>📈 模拟实盘</h1><div class="right"><span class="badge">OKX Public WS</span></div></div>')
|
| 229 |
+
gr.Markdown("提示:点击“⚙️ 设置”进入配置页(上传CSV、参数加载/保存)。若后端模块缺失将自动进入演示引擎。")
|
| 230 |
|
| 231 |
+
with gr.Row():
|
| 232 |
+
with gr.Column(scale=7):
|
| 233 |
+
with gr.Group(visible=True) as 运行面板:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
with gr.Row():
|
| 235 |
+
with gr.Column(scale=1):
|
| 236 |
+
合约输入 = gr.Textbox(label="合约ID", value="UXLINK-USDT-SWAP", placeholder="例:BTC-USDT-SWAP / ETH-USDT-SWAP")
|
| 237 |
+
周期选择 = gr.Radio(label="周期(分钟)", choices=["30", "60", "120", "240"], value="60")
|
| 238 |
+
参数输入 = gr.Textbox(label="策略参数(JSON)", value=json.dumps(默认参数, ensure_ascii=False, indent=2), lines=12)
|
| 239 |
+
with gr.Row():
|
| 240 |
+
启动按钮 = gr.Button("🚀 启动模拟", variant="primary")
|
| 241 |
+
停止按钮 = gr.Button("⏹️ 停止模拟", variant="stop")
|
| 242 |
+
刷新按钮 = gr.Button("🔄 刷新状态")
|
| 243 |
+
设置按钮 = gr.Button("⚙️ 设置", variant="secondary")
|
| 244 |
+
with gr.Column(scale=1):
|
| 245 |
+
状态显示 = gr.Textbox(label="运行状态", lines=2, interactive=False)
|
| 246 |
+
日志显示 = gr.Textbox(label="实时日志(最近120行)", lines=22, interactive=False)
|
| 247 |
+
|
| 248 |
+
with gr.Group(visible=False) as 设置面板:
|
| 249 |
+
返回按钮 = gr.Button("← 返回运行页", variant="secondary")
|
| 250 |
+
gr.Markdown("## 设置")
|
| 251 |
+
if 配置模块 is not None:
|
| 252 |
+
构件 = 配置模块.构建设置面板(容器=设置面板, 默认参数=默认参数, 记录函数=_记录)
|
| 253 |
+
if isinstance(构件, dict):
|
| 254 |
+
# 参数回填
|
| 255 |
+
if "参数编辑框" in 构件 and ("复制按钮" in 构件 or "复制参数按钮" in 构件):
|
| 256 |
+
回填参数按钮 = 构件.get("复制按钮") or 构件.get("复制参数按钮")
|
| 257 |
+
回填参数按钮.click(lambda s: s, inputs=构件["参数编辑框"], outputs=参数输入)
|
| 258 |
+
# 合约回填
|
| 259 |
+
if "回填合约按钮" in 构件 and "合约ID框" in 构件:
|
| 260 |
+
构件["回填合约按钮"].click(lambda s: s.strip(), inputs=构件["合约ID框"], outputs=合约输入)
|
| 261 |
+
else:
|
| 262 |
+
gr.Markdown("未找到 配置.py,无法加载设置面板。")
|
| 263 |
|
| 264 |
+
with gr.Column(scale=5):
|
| 265 |
+
gr.Markdown("### 使用说明")
|
| 266 |
+
gr.Markdown("- 在设置页上传CSV并完成列映射与合约登记\n- 将参数与合约一键回填到运行页\n- 启动后可在右侧查看状态和日志\n- 空闲时 Space 会休眠,策略不会持续运行(演示环境)")
|
| 267 |
+
gr.Markdown("<div class='footer-note'>Tips: 持久化运行请在自有服务器部署引擎,Space 仅作前端/演示。</div>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
|
| 269 |
+
# 页面切换
|
| 270 |
设置按钮.click(lambda: (gr.update(visible=False), gr.update(visible=True)), outputs=[运行面板, 设置面板])
|
| 271 |
返回按钮.click(lambda: (gr.update(visible=True), gr.update(visible=False)), outputs=[运行面板, 设置面板])
|
| 272 |
|