Skip to content

Commit

Permalink
[Mod] 完善README.md等文件
Browse files Browse the repository at this point in the history
  • Loading branch information
vnpy committed Aug 15, 2021
1 parent 489252e commit 8b7688a
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 68 deletions.
46 changes: 14 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# vn.py框架的Web端管理服务器
# vn.py框架的Web服务模块

<p align="center">
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
</p>

<p align="center">
<img src ="https://img.shields.io/badge/version-1.0.0-blueviolet.svg"/>
<img src ="https://img.shields.io/badge/platform-linux|windows-yellow.svg"/>
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
<img src ="https://img.shields.io/badge/python-3.7-blue.svg" />
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
</p>

## 说明

vnpy框架的web端管理服务器
针对B-S架构需求设计的Web服务应用模块,实现了提供主动函数调用(REST)和被动数据推送(Websocket)的Web服务器。

目前仅提供了基础的交易和管理接口,用户根据自己的需求扩展支持其他vn.py应用模块的Web接口(如CTA策略自动交易等)。

## 安装

Expand All @@ -26,38 +27,19 @@ vnpy框架的web端管理服务器
python setup.py install
```

## 使用步骤

1. 运行script下的run.py
2. 点击功能下的网页端服务
3. 配置用户名、密码和SECRET_KEY,其余可默认(用户名和密码与CTP无关,可任意设定)
4. 点击启动
5. 程序会自动读取C:\Users\用户名\.vntrader\connect_ctp.json中的CTP配置
6. 打开浏览器,并访问http://127.0.0.1:8000/
7. 输入3中配置的用户名和密码,并登录
8. 后台启动完成后,会开始自动推送

## 文件功能

* tradeServer.py:基于vnpy.rpc模块实现的交易服务器,包含CTP接口模块
* webServer.py:基于Fastapi实现的Web服务器,内部通过vnpy.rpc客户端来访问交易服务器
* engine.py: 负责调用tradeServer和通过subprocess运行webServer
* run.py: 程序入口

## 架构设计
## 架构

* 基于Fastapi-Restful实现的主动函数调用功能,数据流程:
1. 用户点击浏览器中的某个按钮,发起Restful功能调用
2. Web服务器收到Restful请求,将其转化为RPC功能调用发送给交易服务器
3. 交易服务器收到RPC请求,执行具体的功能逻辑,并返回结果
4. Web服务器返回Restful请求的结果给浏览器
1. 用户点击浏览器中的某个按钮,发起Restful功能调用
2. Web服务器收到Restful请求,将其转化为RPC功能调用发送给交易服务器
3. 交易服务器收到RPC请求,执行具体的功能逻辑,并返回结果
4. Web服务器返回Restful请求的结果给浏览器

* 基于Fastapi-Websocket实现的被动数据推送功能,数据流程:
1. 交易服务器的事件引擎转发某个事件推送,并推送给RPC客户端(Web服务器)
2. Web服务器收到事件推送后,将其转化为json格式,并通过Websocket发出
3. 浏览器通过Websocket收到推送的数据,并渲染在Web前端界面上
1. 交易服务器的事件引擎转发某个事件推送,并推送给RPC客户端(Web服务器)
2. Web服务器收到事件推送后,将其转化为json格式,并通过Websocket发出
3. 浏览器通过Websocket收到推送的数据,并渲染在Web前端界面上

* 将程序分为两个进程的主要原因包括:
1. 交易服务器中的策略运行和数据计算的运算压力较大,需要保证尽可能保证低延时效率
2. Web服务器需要面对互联网访问,将交易相关的逻辑剥离能更好保证安全性

1. 交易服务器中的策略运行和数据计算的运算压力较大,需要保证尽可能保证低延时效率;
2. Web服务器需要面对互联网访问,将交易相关的逻辑剥离能更好保证安全性。
82 changes: 50 additions & 32 deletions script/test.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,32 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3f6aa394",
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"import json"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e0ea6a8b",
"metadata": {},
"outputs": [],
"source": [
"url = \"http://127.0.0.1:8000/\"\n",
"username = \"vnpy\"\n",
"password = \"vnpy\""
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "70160e37",
"metadata": {},
"outputs": [],
"source": [
"# 获取令牌\n",
"r = requests.post(\n",
Expand All @@ -32,13 +37,14 @@
" headers={\"accept\": \"application/json\"}\n",
")\n",
"token = r.json()[\"access_token\"]"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6f7379d",
"metadata": {},
"outputs": [],
"source": [
"# 查询函数\n",
"def query_test(name):\n",
Expand All @@ -48,40 +54,43 @@
" headers={\"Authorization\": \"Bearer \" + token}\n",
" )\n",
" return r.json()"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "729a6ef1",
"metadata": {},
"outputs": [],
"source": [
"# 行情订阅\n",
"r = requests.post(\n",
" url + \"tick/\" + \"cu2112.SHFE\",\n",
" headers={\"Authorization\": \"Bearer \" + token}\n",
")"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc7001e1",
"metadata": {},
"outputs": [],
"source": [
"# 批量查询\n",
"for name in [\"tick\", \"contract\", \"account\", \"position\", \"order\", \"trade\"]:\n",
" data = query_test(name)\n",
" print(name + \"-\" * 20)\n",
" if data:\n",
" print(data[0])"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "53b06a7c",
"metadata": {},
"outputs": [],
"source": [
"# 委托测试\n",
"req = {\n",
Expand All @@ -103,49 +112,58 @@
"vt_orderid = r.json()\n",
"\n",
"print(vt_orderid)"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3435c36e",
"metadata": {},
"outputs": [],
"source": [
"# 撤单测试\n",
"r = requests.delete(\n",
" url + \"order/\" + vt_orderid,\n",
" headers={\"Authorization\": \"Bearer \" + token}\n",
")"
],
"outputs": [],
"metadata": {}
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7341f278",
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# Weboscket测试\n",
"from websocket import create_connection\n",
"\n",
"ws = create_connection(\"ws://127.0.0.1:8000/ws/?token=\"+token)\n",
"ws = create_connection(\"ws://127.0.0.1:8000/ws/?token=\" + token)\n",
"\n",
"while True:\n",
" result = ws.recv()\n",
" print(\"Received '%s'\" % result)\n",
"\n",
"ws.close()"
],
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c06cdf3d",
"metadata": {},
"outputs": [],
"metadata": {
"scrolled": false
}
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"source": [],
"id": "3cb0a41d",
"metadata": {},
"outputs": [],
"metadata": {}
"source": []
}
],
"metadata": {
Expand All @@ -169,4 +187,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
5 changes: 4 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ url = https://www.vnpy.com
license = MIT
author = Xiaoyou Chen
author_email = [email protected]
description = web application for vnpy
description = Web trader application for vn.py quant trading framework.
long_description = file: README.md
long_description_content_type = text/markdown
keywords =
Expand Down Expand Up @@ -35,3 +35,6 @@ install_requires =
websockets
python-jose
python-multipart

[options.package_data]
* = *.ico
13 changes: 10 additions & 3 deletions vnpy_webtrader/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
# 实例化CryptContext用于处理哈希密码
pwd_context = CryptContext(schemes=["sha256_crypt"], deprecated="auto")

# fastapi授权
# FastAPI密码鉴权工具
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


# RPC客户端
rpc_client: RpcClient = None


Expand Down Expand Up @@ -107,6 +107,7 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):


async def get_access(token: str = Depends(oauth2_scheme)):
"""REST鉴权"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
Expand All @@ -119,8 +120,10 @@ async def get_access(token: str = Depends(oauth2_scheme)):
raise credentials_exception
except JWTError:
raise credentials_exception

if username != USERNAME:
raise credentials_exception

return True


Expand Down Expand Up @@ -274,9 +277,13 @@ def get_all_contracts(access: bool = Depends(get_access)) -> list:
return [to_dict(contract) for contract in contracts]


# 活动状态的Websocket连接
active_websockets: List[WebSocket] = []

# 全局事件循环
event_loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()


async def get_websocket_access(
websocket: WebSocket,
token: Optional[str] = Query(None)
Expand All @@ -300,7 +307,7 @@ async def websocket_endpoint(websocket: WebSocket, access: bool = Depends(get_we
"""Weboskcet连接处理"""
if not access:
return "Not authenticated"

await websocket.accept()
active_websockets.append(websocket)

Expand Down

0 comments on commit 8b7688a

Please sign in to comment.