Name | emutrader JSON |
Version |
0.1.4
JSON |
| download |
home_page | https://gitee.com/xledoo/emutrader |
Summary | A Python library for quantitative trading simulation and backtesting |
upload_time | 2025-09-12 21:21:39 |
maintainer | None |
docs_url | None |
author | xledoo |
requires_python | >=3.8 |
license | MIT License
Copyright (c) 2024 EmuTrader
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
|
keywords |
quantitative trading
backtesting
simulation
finance
algorithmic trading
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
<<<<<<< HEAD
# EmuTrader
[](https://python.org)
[](https://opensource.org/licenses/MIT)
[](https://badge.fury.io/py/emutrader)
**EmuTrader** 是一个专为量化交易设计的Python独立账户管理库,提供强大的模拟交易和回测功能。提供100%兼容JoinQuant API的统一接口,通过模拟真实的交易环境,帮助交易者和开发者测试交易策略,而无需承担实际资金风险。支持多平台部署和高性能数据持久化。
## ✨ 核心特性
- 🔄 **JoinQuant完全兼容** - 现有策略零修改迁移,API 100%兼容
- 🏗️ **适配器架构** - 统一接口支持QMT、聚宽等多平台无缝集成
- 💾 **数据持久化** - SQLite可靠存储 + 智能缓存优化
- ⚡ **高性能优化** - LRU缓存 + 批量操作,查询响应 < 10ms
- 🔧 **多账户类型** - STOCK/FUTURE/CREDIT/OPTION等完整支持
- 🛡️ **线程安全** - 支持多策略并发运行
- 📊 **风险管理** - 内置风险控制和性能追踪
## 主要特性
- 🎯 **模拟交易环境** - 完全模拟真实交易场景
- 📊 **策略回测** - 支持历史数据回测分析
- 💰 **资金管理** - 灵活的账户资金管理
- 📈 **实时监控** - 实时跟踪交易表现
- 🔧 **易于扩展** - 模块化设计,便于自定义扩展
- 📝 **详细日志** - 完整的交易记录和日志
## 快速开始
### 安装
# EmuTrader - 专业的量化交易账户管理库
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
[](tests/)
**EmuTrader** 是一个专为量化交易设计的Python账户管理库,专注于提供高性能的账户状态管理和实时盈亏计算功能。项目核心特色是**100%兼容JoinQuant API**,为QSM等策略系统提供强大的账户管理能力。
## 🎯 重新定位:专业账户管理库
### ✅ 为策略系统服务
- **专注账户管理**:Portfolio、SubPortfolio、Position状态管理
- **实时盈亏计算**:基于tick数据的内存实时计算
- **QSM集成接口**:价格更新、交易执行、数据持久化
- **JQ API兼容**:`context.portfolio` = EmuTrader的Portfolio对象
### 🚀 高性能架构
- **内存计算**:价格更新和盈亏计算在内存中完成,目标响应时间 < 10ms
- **批量操作**:支持批量价格更新,减少函数调用开销
- **数据闭环**:DB加载 → 内存实时更新 → 定期保存
- **轻量级设计**:内存占用 < 50MB
### 📊 完整的账户体系
- **股票账户** (STOCK):A股标准交易
- **期货账户** (FUTURE):期货合约交易
- **信用账户** (CREDIT):融资融券
- **金融期货** (INDEX_FUTURE):股指期货
## 🏗️ 新架构设计
### 核心定位
```
QSM策略系统 EmuTrader账户管理库
├── StrategyContext ├── AccountContext
│ ├── current_dt │ ├── portfolio (投资组合)
│ ├── run_params │ └── subportfolios (子账户)
│ └── emutrader ────────────┼── EmuTrader主类
├── 行情数据管理 │ ├── 价格更新接口
├── 策略逻辑 │ ├── 交易执行接口
└── 交易决策 │ └── 数据持久化接口
└── Portfolio/Position核心对象
```
### 数据流设计
```
1. 初始化: DB → EmuTrader.load_from_db() → 内存账户状态
2. 运行时: QSM推送tick → update_market_price() → 实时盈亏计算
3. 交易时: QSM策略决策 → execute_trade() → 账户状态更新
4. 持久化: 内存状态 → save_to_db() → DB文件更新
```
## 🚀 快速开始
### 安装
```bash
pip install emutrader
```
### QSM策略系统集成
```python
from emutrader import get_jq_account
# 1. QSM创建EmuTrader实例
emutrader = get_jq_account("my_strategy", 100000)
emutrader.load_from_db("account.db") # 从DB加载初始状态
# 2. QSM创建自己的策略上下文
class QSMStrategyContext:
def __init__(self, emutrader):
self.current_dt = datetime.now() # QSM管理时间
self.run_params = {} # QSM管理策略参数
self._emutrader = emutrader # 引用账户管理器
@property
def portfolio(self):
"""QSM的context.portfolio = EmuTrader的Portfolio对象"""
return self._emutrader.get_portfolio()
@property
def subportfolios(self):
"""QSM的context.subportfolios = EmuTrader的SubPortfolio列表"""
return self._emutrader.get_subportfolios()
# 3. QSM运行时集成
context = QSMStrategyContext(emutrader)
# 订阅行情
securities = emutrader.get_all_securities() # ['000001.SZ', '000002.SZ']
market_data.subscribe(securities)
# 处理tick数据
def on_tick(security, price):
emutrader.update_market_price(security, price) # 实时更新价格
# 策略交易
def qsm_order_shares(security, amount):
price = get_current_price(security) # QSM获取价格
return emutrader.execute_trade(security, amount, price)
# 定期保存
emutrader.save_to_db() # QSM控制保存时机
```
### 100% JoinQuant兼容使用
```python
from emutrader import get_jq_account, order_shares, order_target_percent
# 现有JQ策略代码无需修改
context = get_jq_account("my_strategy", 100000, "STOCK")
# context实际是EmuTrader实例,但提供JQ兼容接口
print(f"总资产: {context.portfolio.total_value}")
print(f"可用资金: {context.portfolio.available_cash}")
print(f"持仓市值: {context.portfolio.market_value}")
# 交易API保持不变
order_shares('000001.SZ', 1000)
order_target_percent('600519.SH', 0.3)
# 查看持仓
for security, position in context.portfolio.positions.items():
if position.total_amount > 0:
print(f"{security}: {position.total_amount}股, 市值: {position.value:.2f}")
```
### 子账户管理
```python
from emutrader import set_subportfolios, SubPortfolioConfig, transfer_cash
# 设置子账户
set_subportfolios([
SubPortfolioConfig(cash=400000, type='stock'),
SubPortfolioConfig(cash=300000, type='futures'),
])
# 访问子账户
for i, sub in enumerate(context.subportfolios):
print(f"子账户{i}: 资金={sub.available_cash}, 市值={sub.market_value}")
# 子账户资金转移
transfer_cash(from_pindex=1, to_pindex=0, cash=50000)
```
## 🔌 QSM集成接口详解
### 账户数据访问
```python
# Portfolio对象访问(QSM的context.portfolio)
portfolio = emutrader.get_portfolio()
print(f"总资产: {portfolio.total_value}")
print(f"可用资金: {portfolio.available_cash}")
print(f"持仓市值: {portfolio.market_value}")
print(f"收益率: {portfolio.returns}")
# SubPortfolio列表访问(QSM的context.subportfolios)
subportfolios = emutrader.get_subportfolios()
for sub in subportfolios:
print(f"子账户类型: {sub.type}, 资金: {sub.available_cash}")
```
### 价格数据推送
```python
# 单个价格更新
emutrader.update_market_price('000001.SZ', 12.5)
# 批量价格更新(高性能)
price_data = {
'000001.SZ': 12.5,
'000002.SZ': 8.3,
'600519.SH': 1580.0
}
emutrader.batch_update_prices(price_data)
# 获取需要订阅的证券列表
securities = emutrader.get_all_securities()
print(f"需要订阅行情: {securities}")
```
### 交易执行
```python
# 主账户交易
success = emutrader.execute_trade('000001.SZ', 1000, 12.5) # 买入1000股
# 指定子账户交易
success = emutrader.execute_trade('000001.SZ', 1000, 12.5, subportfolio_index=0)
# 子账户资金转移
success = emutrader.transfer_cash(from_index=1, to_index=0, amount=50000)
```
### 数据持久化
```python
# 从数据库加载账户状态(QSM初始化时)
success = emutrader.load_from_db("accounts.db")
# 保存账户状态到数据库(QSM定期调用)
success = emutrader.save_to_db("accounts.db")
# 获取账户完整信息(用于监控和调试)
info = emutrader.get_account_info()
print(info)
```
## 📋 API 参考
### EmuTrader主类接口
#### 账户数据访问
- `get_portfolio()` → Portfolio对象
- `get_subportfolios()` → List[SubPortfolio]
- `get_subportfolio(index)` → SubPortfolio
- `get_account_info()` → Dict[账户信息]
#### QSM集成接口
- `update_market_price(security, price, timestamp)` - 更新单个价格
- `batch_update_prices(price_data, timestamp)` - 批量更新价格
- `get_all_securities()` → List[str] - 获取持仓证券列表
- `execute_trade(security, amount, price, subportfolio_index)` → bool - 执行交易
- `transfer_cash(from_index, to_index, amount)` → bool - 资金转移
#### 数据持久化
- `load_from_db(db_path)` → bool - 从数据库加载
- `save_to_db(db_path)` → bool - 保存到数据库
### JoinQuant兼容API
#### 核心函数
- `get_jq_account(strategy_name, initial_cash, account_type)` → EmuTrader
- `set_subportfolios(configs)` - 设置子账户
- `transfer_cash(from_pindex, to_pindex, cash)` → bool
#### 交易函数
- `order_shares(security, amount, price=None)` → Order
- `order_value(security, value, price=None)` → Order
- `order_target_percent(security, percent)` → Order
## 📊 性能指标
| 指标 | 目标值 | 实现状态 |
|------|--------|----------|
| 价格更新响应时间 | < 5ms | ✅ 内存操作 |
| 盈亏计算时间 | < 10ms | ✅ 实时计算 |
| 交易执行时间 | < 50ms | ✅ 账户状态更新 |
| 批量价格更新 | > 1000/s | ✅ 批量操作优化 |
| 内存占用 | < 50MB | ✅ 轻量级设计 |
| 数据库保存 | < 100ms | ✅ SQLite优化 |
## 🧪 测试
```bash
# 安装测试依赖
pip install -e ".[dev]"
# 运行所有测试
pytest
# 运行覆盖率测试
pytest --cov=emutrader --cov-report=html
# 运行特定测试
pytest tests/test_jq_compatibility.py
```
## 🗺️ 重构历程
### ✅ v1.0.0 - 架构重构完成
- **重新定位**:从策略框架重构为专业账户管理库
- **QSM集成**:提供完整的策略系统集成接口
- **职责分离**:EmuTrader专注账户,QSM专注策略
- **性能优化**:内存实时计算 + 定期批量保存
### 🔄 v1.1.0 - 规划中
- 数据库性能优化和高级查询支持
- 更多交易品种和复杂订单类型
- 账户状态监控和告警机制
- 多策略账户隔离和资源管理
### 📋 v1.2.0 - 未来版本
- 分布式账户管理支持
- 实时风险监控系统
- 账户数据分析和报表
- 云端部署和集群管理
## 🤝 使用场景
### 1. QSM策略系统
- **专业集成**:为QSM提供高性能账户管理
- **实时计算**:tick级别的盈亏实时更新
- **数据闭环**:完整的账户状态生命周期管理
### 2. JoinQuant策略迁移
- **零修改迁移**:现有JQ策略直接运行
- **完整兼容**:所有JQ API函数和对象访问方式
- **扩展增强**:提供JQ不具备的高级功能
### 3. 多策略管理
- **账户隔离**:不同策略使用独立的EmuTrader实例
- **资源共享**:多个策略可以共享行情数据推送
- **统一管理**:集中的账户状态监控和管理
## 📄 许可证
本项目采用 [MIT 许可证](LICENSE)。
## 📞 支持与联系
- **架构文档**: [docs/ARCHITECTURE_REFACTOR.md](docs/ARCHITECTURE_REFACTOR.md)
- **问题反馈**: [GitHub Issues](https://github.com/your-username/emutrader/issues)
- **功能建议**: [GitHub Discussions](https://github.com/your-username/emutrader/discussions)
## 🙏 致谢
- [JoinQuant](https://www.joinquant.com/) - 提供优秀的API设计思路
- QSM策略系统 - 推动架构重构的重要合作伙伴
- 所有贡献者和使用者的支持
---
**EmuTrader** - 专业的量化交易账户管理库 🚀
>>>>>>> 273075473cf8811eb5b4e75fcecbe452b658d2f8
Raw data
{
"_id": null,
"home_page": "https://gitee.com/xledoo/emutrader",
"name": "emutrader",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "xledoo <xledoo@gmail.com>",
"keywords": "quantitative trading, backtesting, simulation, finance, algorithmic trading",
"author": "xledoo",
"author_email": "xledoo <xledoo@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/93/5f/e905b2e6ce621b2a5942856da2018c34b7aa3cf78a9354610fa97768deda/emutrader-0.1.4.tar.gz",
"platform": null,
"description": "<<<<<<< HEAD\r\n# EmuTrader\r\n\r\n[](https://python.org)\r\n[](https://opensource.org/licenses/MIT)\r\n[](https://badge.fury.io/py/emutrader)\r\n\r\n**EmuTrader** \u662f\u4e00\u4e2a\u4e13\u4e3a\u91cf\u5316\u4ea4\u6613\u8bbe\u8ba1\u7684Python\u72ec\u7acb\u8d26\u6237\u7ba1\u7406\u5e93\uff0c\u63d0\u4f9b\u5f3a\u5927\u7684\u6a21\u62df\u4ea4\u6613\u548c\u56de\u6d4b\u529f\u80fd\u3002\u63d0\u4f9b100%\u517c\u5bb9JoinQuant API\u7684\u7edf\u4e00\u63a5\u53e3\uff0c\u901a\u8fc7\u6a21\u62df\u771f\u5b9e\u7684\u4ea4\u6613\u73af\u5883\uff0c\u5e2e\u52a9\u4ea4\u6613\u8005\u548c\u5f00\u53d1\u8005\u6d4b\u8bd5\u4ea4\u6613\u7b56\u7565\uff0c\u800c\u65e0\u9700\u627f\u62c5\u5b9e\u9645\u8d44\u91d1\u98ce\u9669\u3002\u652f\u6301\u591a\u5e73\u53f0\u90e8\u7f72\u548c\u9ad8\u6027\u80fd\u6570\u636e\u6301\u4e45\u5316\u3002\r\n\r\n\r\n## \u2728 \u6838\u5fc3\u7279\u6027\r\n\r\n- \ud83d\udd04 **JoinQuant\u5b8c\u5168\u517c\u5bb9** - \u73b0\u6709\u7b56\u7565\u96f6\u4fee\u6539\u8fc1\u79fb\uff0cAPI 100%\u517c\u5bb9\r\n- \ud83c\udfd7\ufe0f **\u9002\u914d\u5668\u67b6\u6784** - \u7edf\u4e00\u63a5\u53e3\u652f\u6301QMT\u3001\u805a\u5bbd\u7b49\u591a\u5e73\u53f0\u65e0\u7f1d\u96c6\u6210 \r\n- \ud83d\udcbe **\u6570\u636e\u6301\u4e45\u5316** - SQLite\u53ef\u9760\u5b58\u50a8 + \u667a\u80fd\u7f13\u5b58\u4f18\u5316\r\n- \u26a1 **\u9ad8\u6027\u80fd\u4f18\u5316** - LRU\u7f13\u5b58 + \u6279\u91cf\u64cd\u4f5c\uff0c\u67e5\u8be2\u54cd\u5e94 < 10ms\r\n- \ud83d\udd27 **\u591a\u8d26\u6237\u7c7b\u578b** - STOCK/FUTURE/CREDIT/OPTION\u7b49\u5b8c\u6574\u652f\u6301\r\n- \ud83d\udee1\ufe0f **\u7ebf\u7a0b\u5b89\u5168** - \u652f\u6301\u591a\u7b56\u7565\u5e76\u53d1\u8fd0\u884c\r\n- \ud83d\udcca **\u98ce\u9669\u7ba1\u7406** - \u5185\u7f6e\u98ce\u9669\u63a7\u5236\u548c\u6027\u80fd\u8ffd\u8e2a\r\n\r\n## \u4e3b\u8981\u7279\u6027\r\n\r\n- \ud83c\udfaf **\u6a21\u62df\u4ea4\u6613\u73af\u5883** - \u5b8c\u5168\u6a21\u62df\u771f\u5b9e\u4ea4\u6613\u573a\u666f\r\n- \ud83d\udcca **\u7b56\u7565\u56de\u6d4b** - \u652f\u6301\u5386\u53f2\u6570\u636e\u56de\u6d4b\u5206\u6790\r\n- \ud83d\udcb0 **\u8d44\u91d1\u7ba1\u7406** - \u7075\u6d3b\u7684\u8d26\u6237\u8d44\u91d1\u7ba1\u7406\r\n- \ud83d\udcc8 **\u5b9e\u65f6\u76d1\u63a7** - \u5b9e\u65f6\u8ddf\u8e2a\u4ea4\u6613\u8868\u73b0\r\n- \ud83d\udd27 **\u6613\u4e8e\u6269\u5c55** - \u6a21\u5757\u5316\u8bbe\u8ba1\uff0c\u4fbf\u4e8e\u81ea\u5b9a\u4e49\u6269\u5c55\r\n- \ud83d\udcdd **\u8be6\u7ec6\u65e5\u5fd7** - \u5b8c\u6574\u7684\u4ea4\u6613\u8bb0\u5f55\u548c\u65e5\u5fd7\r\n\r\n## \u5feb\u901f\u5f00\u59cb\r\n\r\n### \u5b89\u88c5\r\n\r\n# EmuTrader - \u4e13\u4e1a\u7684\u91cf\u5316\u4ea4\u6613\u8d26\u6237\u7ba1\u7406\u5e93\r\n\r\n[](https://www.python.org/downloads/)\r\n[](https://opensource.org/licenses/MIT)\r\n[](tests/)\r\n\r\n**EmuTrader** \u662f\u4e00\u4e2a\u4e13\u4e3a\u91cf\u5316\u4ea4\u6613\u8bbe\u8ba1\u7684Python\u8d26\u6237\u7ba1\u7406\u5e93\uff0c\u4e13\u6ce8\u4e8e\u63d0\u4f9b\u9ad8\u6027\u80fd\u7684\u8d26\u6237\u72b6\u6001\u7ba1\u7406\u548c\u5b9e\u65f6\u76c8\u4e8f\u8ba1\u7b97\u529f\u80fd\u3002\u9879\u76ee\u6838\u5fc3\u7279\u8272\u662f**100%\u517c\u5bb9JoinQuant API**\uff0c\u4e3aQSM\u7b49\u7b56\u7565\u7cfb\u7edf\u63d0\u4f9b\u5f3a\u5927\u7684\u8d26\u6237\u7ba1\u7406\u80fd\u529b\u3002\r\n\r\n## \ud83c\udfaf \u91cd\u65b0\u5b9a\u4f4d\uff1a\u4e13\u4e1a\u8d26\u6237\u7ba1\u7406\u5e93\r\n\r\n### \u2705 \u4e3a\u7b56\u7565\u7cfb\u7edf\u670d\u52a1\r\n- **\u4e13\u6ce8\u8d26\u6237\u7ba1\u7406**\uff1aPortfolio\u3001SubPortfolio\u3001Position\u72b6\u6001\u7ba1\u7406\r\n- **\u5b9e\u65f6\u76c8\u4e8f\u8ba1\u7b97**\uff1a\u57fa\u4e8etick\u6570\u636e\u7684\u5185\u5b58\u5b9e\u65f6\u8ba1\u7b97\r\n- **QSM\u96c6\u6210\u63a5\u53e3**\uff1a\u4ef7\u683c\u66f4\u65b0\u3001\u4ea4\u6613\u6267\u884c\u3001\u6570\u636e\u6301\u4e45\u5316\r\n- **JQ API\u517c\u5bb9**\uff1a`context.portfolio` = EmuTrader\u7684Portfolio\u5bf9\u8c61\r\n\r\n### \ud83d\ude80 \u9ad8\u6027\u80fd\u67b6\u6784\r\n- **\u5185\u5b58\u8ba1\u7b97**\uff1a\u4ef7\u683c\u66f4\u65b0\u548c\u76c8\u4e8f\u8ba1\u7b97\u5728\u5185\u5b58\u4e2d\u5b8c\u6210\uff0c\u76ee\u6807\u54cd\u5e94\u65f6\u95f4 < 10ms\r\n- **\u6279\u91cf\u64cd\u4f5c**\uff1a\u652f\u6301\u6279\u91cf\u4ef7\u683c\u66f4\u65b0\uff0c\u51cf\u5c11\u51fd\u6570\u8c03\u7528\u5f00\u9500\r\n- **\u6570\u636e\u95ed\u73af**\uff1aDB\u52a0\u8f7d \u2192 \u5185\u5b58\u5b9e\u65f6\u66f4\u65b0 \u2192 \u5b9a\u671f\u4fdd\u5b58\r\n- **\u8f7b\u91cf\u7ea7\u8bbe\u8ba1**\uff1a\u5185\u5b58\u5360\u7528 < 50MB\r\n\r\n### \ud83d\udcca \u5b8c\u6574\u7684\u8d26\u6237\u4f53\u7cfb\r\n- **\u80a1\u7968\u8d26\u6237** (STOCK)\uff1aA\u80a1\u6807\u51c6\u4ea4\u6613\r\n- **\u671f\u8d27\u8d26\u6237** (FUTURE)\uff1a\u671f\u8d27\u5408\u7ea6\u4ea4\u6613 \r\n- **\u4fe1\u7528\u8d26\u6237** (CREDIT)\uff1a\u878d\u8d44\u878d\u5238\r\n- **\u91d1\u878d\u671f\u8d27** (INDEX_FUTURE)\uff1a\u80a1\u6307\u671f\u8d27\r\n\r\n## \ud83c\udfd7\ufe0f \u65b0\u67b6\u6784\u8bbe\u8ba1\r\n\r\n### \u6838\u5fc3\u5b9a\u4f4d\r\n```\r\nQSM\u7b56\u7565\u7cfb\u7edf EmuTrader\u8d26\u6237\u7ba1\u7406\u5e93\r\n\u251c\u2500\u2500 StrategyContext \u251c\u2500\u2500 AccountContext\r\n\u2502 \u251c\u2500\u2500 current_dt \u2502 \u251c\u2500\u2500 portfolio (\u6295\u8d44\u7ec4\u5408)\r\n\u2502 \u251c\u2500\u2500 run_params \u2502 \u2514\u2500\u2500 subportfolios (\u5b50\u8d26\u6237) \r\n\u2502 \u2514\u2500\u2500 emutrader \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500 EmuTrader\u4e3b\u7c7b\r\n\u251c\u2500\u2500 \u884c\u60c5\u6570\u636e\u7ba1\u7406 \u2502 \u251c\u2500\u2500 \u4ef7\u683c\u66f4\u65b0\u63a5\u53e3\r\n\u251c\u2500\u2500 \u7b56\u7565\u903b\u8f91 \u2502 \u251c\u2500\u2500 \u4ea4\u6613\u6267\u884c\u63a5\u53e3\r\n\u2514\u2500\u2500 \u4ea4\u6613\u51b3\u7b56 \u2502 \u2514\u2500\u2500 \u6570\u636e\u6301\u4e45\u5316\u63a5\u53e3\r\n \u2514\u2500\u2500 Portfolio/Position\u6838\u5fc3\u5bf9\u8c61\r\n```\r\n\r\n### \u6570\u636e\u6d41\u8bbe\u8ba1\r\n```\r\n1. \u521d\u59cb\u5316: DB \u2192 EmuTrader.load_from_db() \u2192 \u5185\u5b58\u8d26\u6237\u72b6\u6001\r\n2. \u8fd0\u884c\u65f6: QSM\u63a8\u9001tick \u2192 update_market_price() \u2192 \u5b9e\u65f6\u76c8\u4e8f\u8ba1\u7b97 \r\n3. \u4ea4\u6613\u65f6: QSM\u7b56\u7565\u51b3\u7b56 \u2192 execute_trade() \u2192 \u8d26\u6237\u72b6\u6001\u66f4\u65b0\r\n4. \u6301\u4e45\u5316: \u5185\u5b58\u72b6\u6001 \u2192 save_to_db() \u2192 DB\u6587\u4ef6\u66f4\u65b0\r\n```\r\n\r\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\r\n\r\n### \u5b89\u88c5\r\n```bash\r\npip install emutrader\r\n```\r\n### QSM\u7b56\u7565\u7cfb\u7edf\u96c6\u6210\r\n```python\r\nfrom emutrader import get_jq_account\r\n\r\n# 1. QSM\u521b\u5efaEmuTrader\u5b9e\u4f8b\r\nemutrader = get_jq_account(\"my_strategy\", 100000)\r\nemutrader.load_from_db(\"account.db\") # \u4eceDB\u52a0\u8f7d\u521d\u59cb\u72b6\u6001\r\n\r\n# 2. QSM\u521b\u5efa\u81ea\u5df1\u7684\u7b56\u7565\u4e0a\u4e0b\u6587\r\nclass QSMStrategyContext:\r\n def __init__(self, emutrader):\r\n self.current_dt = datetime.now() # QSM\u7ba1\u7406\u65f6\u95f4\r\n self.run_params = {} # QSM\u7ba1\u7406\u7b56\u7565\u53c2\u6570\r\n self._emutrader = emutrader # \u5f15\u7528\u8d26\u6237\u7ba1\u7406\u5668\r\n \r\n @property\r\n def portfolio(self):\r\n \"\"\"QSM\u7684context.portfolio = EmuTrader\u7684Portfolio\u5bf9\u8c61\"\"\"\r\n return self._emutrader.get_portfolio()\r\n \r\n @property\r\n def subportfolios(self):\r\n \"\"\"QSM\u7684context.subportfolios = EmuTrader\u7684SubPortfolio\u5217\u8868\"\"\"\r\n return self._emutrader.get_subportfolios()\r\n\r\n# 3. QSM\u8fd0\u884c\u65f6\u96c6\u6210\r\ncontext = QSMStrategyContext(emutrader)\r\n\r\n# \u8ba2\u9605\u884c\u60c5\r\nsecurities = emutrader.get_all_securities() # ['000001.SZ', '000002.SZ']\r\nmarket_data.subscribe(securities)\r\n\r\n# \u5904\u7406tick\u6570\u636e\r\ndef on_tick(security, price):\r\n emutrader.update_market_price(security, price) # \u5b9e\u65f6\u66f4\u65b0\u4ef7\u683c\r\n\r\n# \u7b56\u7565\u4ea4\u6613\r\ndef qsm_order_shares(security, amount):\r\n price = get_current_price(security) # QSM\u83b7\u53d6\u4ef7\u683c\r\n return emutrader.execute_trade(security, amount, price)\r\n\r\n# \u5b9a\u671f\u4fdd\u5b58\r\nemutrader.save_to_db() # QSM\u63a7\u5236\u4fdd\u5b58\u65f6\u673a\r\n```\r\n\r\n### 100% JoinQuant\u517c\u5bb9\u4f7f\u7528\r\n```python\r\nfrom emutrader import get_jq_account, order_shares, order_target_percent\r\n\r\n# \u73b0\u6709JQ\u7b56\u7565\u4ee3\u7801\u65e0\u9700\u4fee\u6539\r\ncontext = get_jq_account(\"my_strategy\", 100000, \"STOCK\")\r\n\r\n# context\u5b9e\u9645\u662fEmuTrader\u5b9e\u4f8b\uff0c\u4f46\u63d0\u4f9bJQ\u517c\u5bb9\u63a5\u53e3\r\nprint(f\"\u603b\u8d44\u4ea7: {context.portfolio.total_value}\")\r\nprint(f\"\u53ef\u7528\u8d44\u91d1: {context.portfolio.available_cash}\")\r\nprint(f\"\u6301\u4ed3\u5e02\u503c: {context.portfolio.market_value}\")\r\n\r\n# \u4ea4\u6613API\u4fdd\u6301\u4e0d\u53d8\r\norder_shares('000001.SZ', 1000)\r\norder_target_percent('600519.SH', 0.3)\r\n\r\n# \u67e5\u770b\u6301\u4ed3\r\nfor security, position in context.portfolio.positions.items():\r\n if position.total_amount > 0:\r\n print(f\"{security}: {position.total_amount}\u80a1, \u5e02\u503c: {position.value:.2f}\")\r\n```\r\n\r\n### \u5b50\u8d26\u6237\u7ba1\u7406\r\n```python\r\nfrom emutrader import set_subportfolios, SubPortfolioConfig, transfer_cash\r\n\r\n# \u8bbe\u7f6e\u5b50\u8d26\u6237\r\nset_subportfolios([\r\n SubPortfolioConfig(cash=400000, type='stock'),\r\n SubPortfolioConfig(cash=300000, type='futures'),\r\n])\r\n\r\n# \u8bbf\u95ee\u5b50\u8d26\u6237\r\nfor i, sub in enumerate(context.subportfolios):\r\n print(f\"\u5b50\u8d26\u6237{i}: \u8d44\u91d1={sub.available_cash}, \u5e02\u503c={sub.market_value}\")\r\n\r\n# \u5b50\u8d26\u6237\u8d44\u91d1\u8f6c\u79fb\r\ntransfer_cash(from_pindex=1, to_pindex=0, cash=50000)\r\n```\r\n\r\n## \ud83d\udd0c QSM\u96c6\u6210\u63a5\u53e3\u8be6\u89e3\r\n\r\n### \u8d26\u6237\u6570\u636e\u8bbf\u95ee\r\n```python\r\n# Portfolio\u5bf9\u8c61\u8bbf\u95ee\uff08QSM\u7684context.portfolio\uff09\r\nportfolio = emutrader.get_portfolio()\r\nprint(f\"\u603b\u8d44\u4ea7: {portfolio.total_value}\")\r\nprint(f\"\u53ef\u7528\u8d44\u91d1: {portfolio.available_cash}\")\r\nprint(f\"\u6301\u4ed3\u5e02\u503c: {portfolio.market_value}\")\r\nprint(f\"\u6536\u76ca\u7387: {portfolio.returns}\")\r\n\r\n# SubPortfolio\u5217\u8868\u8bbf\u95ee\uff08QSM\u7684context.subportfolios\uff09\r\nsubportfolios = emutrader.get_subportfolios()\r\nfor sub in subportfolios:\r\n print(f\"\u5b50\u8d26\u6237\u7c7b\u578b: {sub.type}, \u8d44\u91d1: {sub.available_cash}\")\r\n```\r\n\r\n### \u4ef7\u683c\u6570\u636e\u63a8\u9001\r\n```python\r\n# \u5355\u4e2a\u4ef7\u683c\u66f4\u65b0\r\nemutrader.update_market_price('000001.SZ', 12.5)\r\n\r\n# \u6279\u91cf\u4ef7\u683c\u66f4\u65b0\uff08\u9ad8\u6027\u80fd\uff09\r\nprice_data = {\r\n '000001.SZ': 12.5,\r\n '000002.SZ': 8.3,\r\n '600519.SH': 1580.0\r\n}\r\nemutrader.batch_update_prices(price_data)\r\n\r\n# \u83b7\u53d6\u9700\u8981\u8ba2\u9605\u7684\u8bc1\u5238\u5217\u8868\r\nsecurities = emutrader.get_all_securities()\r\nprint(f\"\u9700\u8981\u8ba2\u9605\u884c\u60c5: {securities}\")\r\n```\r\n\r\n### \u4ea4\u6613\u6267\u884c\r\n```python\r\n# \u4e3b\u8d26\u6237\u4ea4\u6613\r\nsuccess = emutrader.execute_trade('000001.SZ', 1000, 12.5) # \u4e70\u51651000\u80a1\r\n\r\n# \u6307\u5b9a\u5b50\u8d26\u6237\u4ea4\u6613\r\nsuccess = emutrader.execute_trade('000001.SZ', 1000, 12.5, subportfolio_index=0)\r\n\r\n# \u5b50\u8d26\u6237\u8d44\u91d1\u8f6c\u79fb\r\nsuccess = emutrader.transfer_cash(from_index=1, to_index=0, amount=50000)\r\n```\r\n\r\n### \u6570\u636e\u6301\u4e45\u5316\r\n```python\r\n# \u4ece\u6570\u636e\u5e93\u52a0\u8f7d\u8d26\u6237\u72b6\u6001\uff08QSM\u521d\u59cb\u5316\u65f6\uff09\r\nsuccess = emutrader.load_from_db(\"accounts.db\")\r\n\r\n# \u4fdd\u5b58\u8d26\u6237\u72b6\u6001\u5230\u6570\u636e\u5e93\uff08QSM\u5b9a\u671f\u8c03\u7528\uff09\r\nsuccess = emutrader.save_to_db(\"accounts.db\")\r\n\r\n# \u83b7\u53d6\u8d26\u6237\u5b8c\u6574\u4fe1\u606f\uff08\u7528\u4e8e\u76d1\u63a7\u548c\u8c03\u8bd5\uff09\r\ninfo = emutrader.get_account_info()\r\nprint(info)\r\n```\r\n\r\n## \ud83d\udccb API \u53c2\u8003\r\n\r\n### EmuTrader\u4e3b\u7c7b\u63a5\u53e3\r\n\r\n#### \u8d26\u6237\u6570\u636e\u8bbf\u95ee\r\n- `get_portfolio()` \u2192 Portfolio\u5bf9\u8c61\r\n- `get_subportfolios()` \u2192 List[SubPortfolio]\r\n- `get_subportfolio(index)` \u2192 SubPortfolio\r\n- `get_account_info()` \u2192 Dict[\u8d26\u6237\u4fe1\u606f]\r\n\r\n#### QSM\u96c6\u6210\u63a5\u53e3\r\n- `update_market_price(security, price, timestamp)` - \u66f4\u65b0\u5355\u4e2a\u4ef7\u683c\r\n- `batch_update_prices(price_data, timestamp)` - \u6279\u91cf\u66f4\u65b0\u4ef7\u683c\r\n- `get_all_securities()` \u2192 List[str] - \u83b7\u53d6\u6301\u4ed3\u8bc1\u5238\u5217\u8868\r\n- `execute_trade(security, amount, price, subportfolio_index)` \u2192 bool - \u6267\u884c\u4ea4\u6613\r\n- `transfer_cash(from_index, to_index, amount)` \u2192 bool - \u8d44\u91d1\u8f6c\u79fb\r\n\r\n#### \u6570\u636e\u6301\u4e45\u5316\r\n- `load_from_db(db_path)` \u2192 bool - \u4ece\u6570\u636e\u5e93\u52a0\u8f7d\r\n- `save_to_db(db_path)` \u2192 bool - \u4fdd\u5b58\u5230\u6570\u636e\u5e93\r\n\r\n### JoinQuant\u517c\u5bb9API\r\n\r\n#### \u6838\u5fc3\u51fd\u6570\r\n- `get_jq_account(strategy_name, initial_cash, account_type)` \u2192 EmuTrader\r\n- `set_subportfolios(configs)` - \u8bbe\u7f6e\u5b50\u8d26\u6237\r\n- `transfer_cash(from_pindex, to_pindex, cash)` \u2192 bool\r\n\r\n#### \u4ea4\u6613\u51fd\u6570\r\n- `order_shares(security, amount, price=None)` \u2192 Order\r\n- `order_value(security, value, price=None)` \u2192 Order\r\n- `order_target_percent(security, percent)` \u2192 Order\r\n\r\n## \ud83d\udcca \u6027\u80fd\u6307\u6807\r\n\r\n| \u6307\u6807 | \u76ee\u6807\u503c | \u5b9e\u73b0\u72b6\u6001 |\r\n|------|--------|----------|\r\n| \u4ef7\u683c\u66f4\u65b0\u54cd\u5e94\u65f6\u95f4 | < 5ms | \u2705 \u5185\u5b58\u64cd\u4f5c |\r\n| \u76c8\u4e8f\u8ba1\u7b97\u65f6\u95f4 | < 10ms | \u2705 \u5b9e\u65f6\u8ba1\u7b97 |\r\n| \u4ea4\u6613\u6267\u884c\u65f6\u95f4 | < 50ms | \u2705 \u8d26\u6237\u72b6\u6001\u66f4\u65b0 |\r\n| \u6279\u91cf\u4ef7\u683c\u66f4\u65b0 | > 1000/s | \u2705 \u6279\u91cf\u64cd\u4f5c\u4f18\u5316 |\r\n| \u5185\u5b58\u5360\u7528 | < 50MB | \u2705 \u8f7b\u91cf\u7ea7\u8bbe\u8ba1 |\r\n| \u6570\u636e\u5e93\u4fdd\u5b58 | < 100ms | \u2705 SQLite\u4f18\u5316 |\r\n\r\n## \ud83e\uddea \u6d4b\u8bd5\r\n\r\n```bash\r\n# \u5b89\u88c5\u6d4b\u8bd5\u4f9d\u8d56\r\npip install -e \".[dev]\"\r\n\r\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\r\npytest\r\n\r\n# \u8fd0\u884c\u8986\u76d6\u7387\u6d4b\u8bd5\r\npytest --cov=emutrader --cov-report=html\r\n\r\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\r\npytest tests/test_jq_compatibility.py\r\n```\r\n\r\n## \ud83d\uddfa\ufe0f \u91cd\u6784\u5386\u7a0b\r\n\r\n### \u2705 v1.0.0 - \u67b6\u6784\u91cd\u6784\u5b8c\u6210\r\n- **\u91cd\u65b0\u5b9a\u4f4d**\uff1a\u4ece\u7b56\u7565\u6846\u67b6\u91cd\u6784\u4e3a\u4e13\u4e1a\u8d26\u6237\u7ba1\u7406\u5e93\r\n- **QSM\u96c6\u6210**\uff1a\u63d0\u4f9b\u5b8c\u6574\u7684\u7b56\u7565\u7cfb\u7edf\u96c6\u6210\u63a5\u53e3\r\n- **\u804c\u8d23\u5206\u79bb**\uff1aEmuTrader\u4e13\u6ce8\u8d26\u6237\uff0cQSM\u4e13\u6ce8\u7b56\u7565\r\n- **\u6027\u80fd\u4f18\u5316**\uff1a\u5185\u5b58\u5b9e\u65f6\u8ba1\u7b97 + \u5b9a\u671f\u6279\u91cf\u4fdd\u5b58\r\n\r\n### \ud83d\udd04 v1.1.0 - \u89c4\u5212\u4e2d\r\n- \u6570\u636e\u5e93\u6027\u80fd\u4f18\u5316\u548c\u9ad8\u7ea7\u67e5\u8be2\u652f\u6301\r\n- \u66f4\u591a\u4ea4\u6613\u54c1\u79cd\u548c\u590d\u6742\u8ba2\u5355\u7c7b\u578b\r\n- \u8d26\u6237\u72b6\u6001\u76d1\u63a7\u548c\u544a\u8b66\u673a\u5236\r\n- \u591a\u7b56\u7565\u8d26\u6237\u9694\u79bb\u548c\u8d44\u6e90\u7ba1\u7406\r\n\r\n### \ud83d\udccb v1.2.0 - \u672a\u6765\u7248\u672c\r\n- \u5206\u5e03\u5f0f\u8d26\u6237\u7ba1\u7406\u652f\u6301\r\n- \u5b9e\u65f6\u98ce\u9669\u76d1\u63a7\u7cfb\u7edf\r\n- \u8d26\u6237\u6570\u636e\u5206\u6790\u548c\u62a5\u8868\r\n- \u4e91\u7aef\u90e8\u7f72\u548c\u96c6\u7fa4\u7ba1\u7406\r\n\r\n## \ud83e\udd1d \u4f7f\u7528\u573a\u666f\r\n\r\n### 1. QSM\u7b56\u7565\u7cfb\u7edf\r\n- **\u4e13\u4e1a\u96c6\u6210**\uff1a\u4e3aQSM\u63d0\u4f9b\u9ad8\u6027\u80fd\u8d26\u6237\u7ba1\u7406\r\n- **\u5b9e\u65f6\u8ba1\u7b97**\uff1atick\u7ea7\u522b\u7684\u76c8\u4e8f\u5b9e\u65f6\u66f4\u65b0\r\n- **\u6570\u636e\u95ed\u73af**\uff1a\u5b8c\u6574\u7684\u8d26\u6237\u72b6\u6001\u751f\u547d\u5468\u671f\u7ba1\u7406\r\n\r\n### 2. JoinQuant\u7b56\u7565\u8fc1\u79fb\r\n- **\u96f6\u4fee\u6539\u8fc1\u79fb**\uff1a\u73b0\u6709JQ\u7b56\u7565\u76f4\u63a5\u8fd0\u884c\r\n- **\u5b8c\u6574\u517c\u5bb9**\uff1a\u6240\u6709JQ API\u51fd\u6570\u548c\u5bf9\u8c61\u8bbf\u95ee\u65b9\u5f0f\r\n- **\u6269\u5c55\u589e\u5f3a**\uff1a\u63d0\u4f9bJQ\u4e0d\u5177\u5907\u7684\u9ad8\u7ea7\u529f\u80fd\r\n\r\n### 3. \u591a\u7b56\u7565\u7ba1\u7406\r\n- **\u8d26\u6237\u9694\u79bb**\uff1a\u4e0d\u540c\u7b56\u7565\u4f7f\u7528\u72ec\u7acb\u7684EmuTrader\u5b9e\u4f8b\r\n- **\u8d44\u6e90\u5171\u4eab**\uff1a\u591a\u4e2a\u7b56\u7565\u53ef\u4ee5\u5171\u4eab\u884c\u60c5\u6570\u636e\u63a8\u9001\r\n- **\u7edf\u4e00\u7ba1\u7406**\uff1a\u96c6\u4e2d\u7684\u8d26\u6237\u72b6\u6001\u76d1\u63a7\u548c\u7ba1\u7406\r\n\r\n## \ud83d\udcc4 \u8bb8\u53ef\u8bc1\r\n\r\n\u672c\u9879\u76ee\u91c7\u7528 [MIT \u8bb8\u53ef\u8bc1](LICENSE)\u3002\r\n\r\n## \ud83d\udcde \u652f\u6301\u4e0e\u8054\u7cfb\r\n\r\n- **\u67b6\u6784\u6587\u6863**: [docs/ARCHITECTURE_REFACTOR.md](docs/ARCHITECTURE_REFACTOR.md)\r\n- **\u95ee\u9898\u53cd\u9988**: [GitHub Issues](https://github.com/your-username/emutrader/issues)\r\n- **\u529f\u80fd\u5efa\u8bae**: [GitHub Discussions](https://github.com/your-username/emutrader/discussions)\r\n\r\n## \ud83d\ude4f \u81f4\u8c22\r\n\r\n- [JoinQuant](https://www.joinquant.com/) - \u63d0\u4f9b\u4f18\u79c0\u7684API\u8bbe\u8ba1\u601d\u8def\r\n- QSM\u7b56\u7565\u7cfb\u7edf - \u63a8\u52a8\u67b6\u6784\u91cd\u6784\u7684\u91cd\u8981\u5408\u4f5c\u4f19\u4f34\r\n- \u6240\u6709\u8d21\u732e\u8005\u548c\u4f7f\u7528\u8005\u7684\u652f\u6301\r\n\r\n---\r\n\r\n**EmuTrader** - \u4e13\u4e1a\u7684\u91cf\u5316\u4ea4\u6613\u8d26\u6237\u7ba1\u7406\u5e93 \ud83d\ude80\r\n>>>>>>> 273075473cf8811eb5b4e75fcecbe452b658d2f8\r\n",
"bugtrack_url": null,
"license": "MIT License\r\n \r\n Copyright (c) 2024 EmuTrader\r\n \r\n Permission is hereby granted, free of charge, to any person obtaining a copy\r\n of this software and associated documentation files (the \"Software\"), to deal\r\n in the Software without restriction, including without limitation the rights\r\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n copies of the Software, and to permit persons to whom the Software is\r\n furnished to do so, subject to the following conditions:\r\n \r\n The above copyright notice and this permission notice shall be included in all\r\n copies or substantial portions of the Software.\r\n \r\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n SOFTWARE.\r\n ",
"summary": "A Python library for quantitative trading simulation and backtesting",
"version": "0.1.4",
"project_urls": {
"Bug Tracker": "https://gitee.com/xledoo/emutrader/issues",
"Documentation": "https://emutrader.readthedocs.io/",
"Homepage": "https://gitee.com/xledoo/emutrader",
"Repository": "https://gitee.com/xledoo/emutrader.git"
},
"split_keywords": [
"quantitative trading",
" backtesting",
" simulation",
" finance",
" algorithmic trading"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "bbf0b64bc02010062c25bc888cd96ede4bce0b2d29b8325889231b5fd4ed61a5",
"md5": "a15cb69ba42a25cc691fbd65dd8b4236",
"sha256": "5f62895c1eba762e5b0dcfc59d5b0363a7ee79b781c515b492f5c0966ee37eda"
},
"downloads": -1,
"filename": "emutrader-0.1.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a15cb69ba42a25cc691fbd65dd8b4236",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 95717,
"upload_time": "2025-09-12T21:21:37",
"upload_time_iso_8601": "2025-09-12T21:21:37.682759Z",
"url": "https://files.pythonhosted.org/packages/bb/f0/b64bc02010062c25bc888cd96ede4bce0b2d29b8325889231b5fd4ed61a5/emutrader-0.1.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "935fe905b2e6ce621b2a5942856da2018c34b7aa3cf78a9354610fa97768deda",
"md5": "ef0434fe62c4ee540ee9654474d8e69a",
"sha256": "6f643c7dff28f32fb72b18e3dc2b9bf8b157de445e97acd0cef03ac4aa294015"
},
"downloads": -1,
"filename": "emutrader-0.1.4.tar.gz",
"has_sig": false,
"md5_digest": "ef0434fe62c4ee540ee9654474d8e69a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 403936,
"upload_time": "2025-09-12T21:21:39",
"upload_time_iso_8601": "2025-09-12T21:21:39.720187Z",
"url": "https://files.pythonhosted.org/packages/93/5f/e905b2e6ce621b2a5942856da2018c34b7aa3cf78a9354610fa97768deda/emutrader-0.1.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-12 21:21:39",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "emutrader"
}