添加dummy gateway
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
Dummy QMT 模拟器 - 用于在非 Windows 环境下模拟 QMT 交易功能
|
||||
"""
|
||||
import datetime
|
||||
import threading
|
||||
import time
|
||||
import random
|
||||
import config
|
||||
import core.eventbus as eBus
|
||||
from core.logger import LogLevel, PrintLog
|
||||
|
||||
class DummyPosition:
|
||||
"""模拟持仓"""
|
||||
def __init__(self, stock_code, stock_name, volume, yesterday_vol=0):
|
||||
self.stock_code = stock_code
|
||||
self.stock_name = stock_name
|
||||
self.volume = volume
|
||||
self.can_use_volume = volume
|
||||
self.yesterday_volume = yesterday_vol
|
||||
|
||||
class DummyOrder:
|
||||
"""模拟订单"""
|
||||
def __init__(self, stock_code, order_id, status, price, volume):
|
||||
self.stock_code = stock_code
|
||||
self.order_id = order_id
|
||||
self.order_status = status
|
||||
self.order_price = price
|
||||
self.volume = volume
|
||||
|
||||
class DummyTrade:
|
||||
"""模拟成交"""
|
||||
def __init__(self, stock_code, trade_id, price, volume, strategy_name):
|
||||
self.stock_code = stock_code
|
||||
self.trade_id = trade_id
|
||||
self.trade_price = price
|
||||
self.trade_volume = volume
|
||||
self.strategy_name = strategy_name
|
||||
|
||||
class DummyOrderResponse:
|
||||
"""模拟下单响应"""
|
||||
def __init__(self, order_id, stock_code, seq, error_msg, strategy_name):
|
||||
self.order_id = order_id
|
||||
self.stock_code = stock_code
|
||||
self.seq = seq
|
||||
self.error_msg = error_msg
|
||||
self.strategy_name = strategy_name
|
||||
|
||||
class DummyQmtV:
|
||||
"""
|
||||
Dummy QMT 模拟器
|
||||
模拟 QmtV 类的接口,用于在没有 miniQMT 的环境下运行和测试
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
self.inited = False
|
||||
self.details = {}
|
||||
self.lastMarketDataUpdateTimestamp = time.time()
|
||||
self.isMarketActive = True
|
||||
self.connected = False
|
||||
self.account = None
|
||||
self._positions = {}
|
||||
self._pending_orders = []
|
||||
self._market_data_thread = None
|
||||
self._counter = 0
|
||||
|
||||
def getTrader(self):
|
||||
return self
|
||||
|
||||
def init_qmtv(self):
|
||||
"""初始化交易器"""
|
||||
PrintLog(LogLevel.INFO, f'- [模拟] QMT 交易器初始化')
|
||||
self.connected = True
|
||||
self.inited = True
|
||||
|
||||
def connect(self) -> bool:
|
||||
"""连接 QMT (模拟总是成功)"""
|
||||
PrintLog(LogLevel.INFO, f'- [成功] 市场交易连接 (模拟模式)')
|
||||
|
||||
# 创建模拟账号
|
||||
try:
|
||||
from xtquant.xttype import StockAccount
|
||||
self.account = StockAccount(config.account_no, 'STOCK')
|
||||
except ImportError:
|
||||
self.account = type('StockAccount', (), {'account_id': config.account_no})()
|
||||
PrintLog(LogLevel.INFO, f'- [成功] 交易账号: {config.account_no}')
|
||||
|
||||
self._init_dummy_positions()
|
||||
self.startMarketDataSubscription()
|
||||
|
||||
return self.inited
|
||||
|
||||
def _init_dummy_positions(self):
|
||||
"""初始化模拟持仓数据"""
|
||||
dummy_stocks = [
|
||||
('600519', '贵州茅台', 100, 2800.0),
|
||||
('000858', '五粮液', 200, 180.0),
|
||||
('600036', '招商银行', 500, 42.0),
|
||||
('000001', '平安银行', 300, 13.5),
|
||||
]
|
||||
for code, name, volume, price in dummy_stocks:
|
||||
self._positions[code] = {
|
||||
'stock_code': code,
|
||||
'stock_name': name,
|
||||
'volume': volume,
|
||||
'can_use_volume': volume,
|
||||
'open_cost': price,
|
||||
'market_value': volume * price
|
||||
}
|
||||
PrintLog(LogLevel.INFO, f'- [模拟] 已加载 {len(self._positions)} 个持仓')
|
||||
|
||||
def getStockPosition(self, stock_code: str):
|
||||
"""获取持仓 (模拟)"""
|
||||
if stock_code in self._positions:
|
||||
pos = self._positions[stock_code]
|
||||
return type('DummyPos', (), pos)()
|
||||
return None
|
||||
|
||||
def queryPendingOrder(self, stock_code: str, tag: str) -> list:
|
||||
"""查询挂单"""
|
||||
return [o for o in self._pending_orders
|
||||
if o.stock_code == stock_code and
|
||||
(tag is None or getattr(o, 'strategy_name', None) == tag)]
|
||||
|
||||
def orderAsync(self, stock_code, orderVolume, orderType, orderPrice, priceType, orderRemark, strategy_name):
|
||||
"""异步下单 (模拟)"""
|
||||
self._counter += 1
|
||||
order_id = f"DUMMY{self._counter:06d}"
|
||||
seq = self._counter
|
||||
|
||||
order = DummyOrder(
|
||||
stock_code=stock_code,
|
||||
order_id=order_id,
|
||||
status='reported',
|
||||
price=orderPrice,
|
||||
volume=orderVolume
|
||||
)
|
||||
order.strategy_name = strategy_name
|
||||
order.order_remark = orderRemark
|
||||
self._pending_orders.append(order)
|
||||
|
||||
response = DummyOrderResponse(
|
||||
order_id=order_id,
|
||||
stock_code=stock_code,
|
||||
seq=seq,
|
||||
error_msg='成功',
|
||||
strategy_name=strategy_name
|
||||
)
|
||||
response.order_remark = orderRemark
|
||||
|
||||
eBus.event_bus.publish(eBus.MarketOrderCreated, response)
|
||||
PrintLog(LogLevel.INFO, f'- [模拟下单] {stock_code} 数量:{orderVolume} 价格:{orderPrice} 订单号:{order_id}')
|
||||
|
||||
# 模拟成交 (80% 概率)
|
||||
if random.random() > 0.2:
|
||||
threading.Timer(random.uniform(0.5, 3.0), self._simulate_trade,
|
||||
args=(stock_code, order_id, orderPrice, orderVolume, strategy_name)).start()
|
||||
|
||||
return 0
|
||||
|
||||
def _simulate_trade(self, stock_code, order_id, price, volume, strategy_name):
|
||||
"""模拟成交"""
|
||||
trade = DummyTrade(
|
||||
stock_code=stock_code,
|
||||
trade_id=f"TRADE{self._counter:06d}",
|
||||
price=price,
|
||||
volume=volume,
|
||||
strategy_name=strategy_name
|
||||
)
|
||||
trade.trade_time = int(time.strftime('%H%M%S'))
|
||||
trade.order_remark = stock_code
|
||||
|
||||
if stock_code in self._positions:
|
||||
self._positions[stock_code]['volume'] += volume
|
||||
self._positions[stock_code]['can_use_volume'] += volume
|
||||
|
||||
eBus.event_bus.publish(eBus.MarketOrderTraded, trade)
|
||||
PrintLog(LogLevel.INFO, f'- [模拟成交] {stock_code} 数量:{volume} 价格:{price}')
|
||||
|
||||
def cacheStockDetail(self, stock_code: str):
|
||||
"""获取股票详情 (模拟)"""
|
||||
if stock_code not in self.details:
|
||||
self.details[stock_code] = {
|
||||
'InstrumentName': self._get_dummy_name(stock_code),
|
||||
'UpStopPrice': 0,
|
||||
'DownStopPrice': 0
|
||||
}
|
||||
return self.details[stock_code]
|
||||
|
||||
def _get_dummy_name(self, stock_code: str) -> str:
|
||||
"""获取模拟股票名称"""
|
||||
names = {
|
||||
'600519': '贵州茅台', '000858': '五粮液', '600036': '招商银行',
|
||||
'000001': '平安银行', '000002': '万科A', '600000': '浦发银行'
|
||||
}
|
||||
return names.get(stock_code, f'股票{stock_code}')
|
||||
|
||||
def getInstrumentName(self, stock_code: str) -> str:
|
||||
"""获取股票名称"""
|
||||
return self.cacheStockDetail(stock_code)['InstrumentName']
|
||||
|
||||
def dailyUpStop(self, stock_code: str):
|
||||
"""获取涨停价 (模拟)"""
|
||||
cacheStock = self.cacheStockDetail(stock_code)
|
||||
PrintLog(LogLevel.INFO, f'- [模拟] 获取股票详情: {stock_code} {cacheStock["InstrumentName"]} 涨停价: 0')
|
||||
return 0.0
|
||||
|
||||
def dailyDownStop(self, stock_code: str):
|
||||
"""获取跌停价 (模拟)"""
|
||||
return 0.0
|
||||
|
||||
def startMarketDataSubscription(self):
|
||||
"""启动市场数据订阅 (模拟)"""
|
||||
try:
|
||||
self._market_data_thread = threading.Thread(target=self._generate_market_data, daemon=True)
|
||||
self._market_data_thread.start()
|
||||
PrintLog(LogLevel.INFO, f'- [市场数据订阅成功-模拟]')
|
||||
except Exception as e:
|
||||
PrintLog(LogLevel.ERROR, f'- [市场数据订阅失败-{e}]')
|
||||
|
||||
def stopMarketDataSubscription(self):
|
||||
"""停止市场数据订阅"""
|
||||
PrintLog(LogLevel.INFO, '- 停止市场数据订阅 (模拟)')
|
||||
|
||||
def _generate_market_data(self):
|
||||
"""生成模拟市场数据"""
|
||||
stocks = ['600519', '000858', '600036', '000001', '000002', '600000']
|
||||
base_prices = [2800.0, 180.0, 42.0, 13.5, 10.0, 10.0]
|
||||
|
||||
while True:
|
||||
try:
|
||||
for i, stock in enumerate(stocks):
|
||||
data = {
|
||||
'stock_code': stock,
|
||||
'last_price': base_prices[i] + random.uniform(-1, 1),
|
||||
'open_price': base_prices[i],
|
||||
'high_price': base_prices[i] + random.uniform(0, 2),
|
||||
'low_price': base_prices[i] - random.uniform(0, 2),
|
||||
'volume': random.randint(1000, 10000),
|
||||
'timestamp': time.time()
|
||||
}
|
||||
eBus.event_bus.publish(eBus.MarketDataUpdate, data)
|
||||
base_prices[i] = data['last_price']
|
||||
|
||||
self.lastMarketDataUpdateTimestamp = time.time()
|
||||
self.isMarketActive = True
|
||||
eBus.event_bus.publish(eBus.EventMarketActiveSwitch, True)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
except Exception as e:
|
||||
PrintLog(LogLevel.ERROR, f'- [市场数据模拟异常-{e}]')
|
||||
time.sleep(1)
|
||||
|
||||
def on_connected(self):
|
||||
print(datetime.datetime.now(), '模拟连接成功')
|
||||
|
||||
def on_disconnected(self):
|
||||
print(datetime.datetime.now(), '模拟连接断开')
|
||||
|
||||
def on_stock_order(self, order):
|
||||
pass
|
||||
|
||||
def on_stock_trade(self, trade):
|
||||
eBus.event_bus.publish(eBus.MarketOrderTraded, trade)
|
||||
|
||||
def on_order_stock_async_response(self, response):
|
||||
eBus.event_bus.publish(eBus.MarketOrderCreated, response)
|
||||
|
||||
def on_order_error(self, order_error):
|
||||
print(f"\n模拟委托报错回调 {order_error}")
|
||||
|
||||
def on_account_status(self, status):
|
||||
print(datetime.datetime.now(), status)
|
||||
|
||||
|
||||
qmtv = DummyQmtV()
|
||||
Reference in New Issue
Block a user