VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 11
声望: 1

根据论坛 导入通达信的历史数据方法 方法,完善了一下批量导入功能

效果如图

description

导出时,框处注意选项:头部、分隔符号、日期格式
description

关键代码

    def load_tdx_csv_to_sqlite(self, file_path: str, exchange: Exchange, symbol: str, interval: Interval):
        with open(file_path, "rt") as f:
            buf = [line.replace("\0", "") for line in f]

        reader = csv.DictReader(buf, delimiter=",")

        bars = []
        start = None
        count = 0
        tz = timezone("Asia/Shanghai")

        for item in reader:
            tt1 = item["时间"]

            ln = len(tt1)
            tt2 = tt1[0:2]
            tt3 = tt1[2:4]
            tt1 = f"{tt2}:{tt3}"

            dt = datetime.strptime(item["日期"] + ' ' + tt1, '%Y/%m/%d %H:%M')
            # minute - 1;
            dt = dt + timedelta(minutes=-1)
            # time > 15:00, day - 1
            if dt.hour > 15:
                dt = dt + timedelta(days=-1)
            dt = tz.localize(dt)

            bar = BarData(
                symbol=symbol,
                exchange=exchange,
                interval=interval,
                datetime=dt,
                open_price=float(item["开盘"]),
                high_price=float(item["最高"]),
                low_price=float(item["最低"]),
                close_price=float(item["收盘"]),
                volume=float(item["成交量"]),
                turnover=float(item["持仓量"]),
                open_interest=float(item["结算价"]),
                gateway_name="sqlite",
            )
            bars.append(bar)

            # do some statistics
            count += 1
            if not start:
                start = bar.datetime
        end = bar.datetime

        # insert into database
        database: BaseDatabase = get_database()
        database.save_bar_data(bars)
        return f'{symbol} 导入完成: {count} from {start} - {end}'

    def import_tdx_export_folder(self, tdx_export_folder: str):
        """从通达信1分钟导出数据文件夹导入"""

        count = 0
        list_folder = os.listdir(tdx_export_folder)
        for filename in list_folder:

            # 过滤
            extension = filename.split('.')

            file_path: str = os.path.join(tdx_export_folder, filename)
            csv_file: str = os.path.join(tdx_export_folder, f"{extension[0]}.csv")

            if extension[1] == "csv":
                continue

            if extension[1] == "txt" and os.path.exists(csv_file):
                continue

            # 打开文件
            with open(file_path, "r", encoding="gb2312") as file:
                lines = file.readlines()

                # 取代码,获取合约信息
                symbol_list = lines[0].split()

                # 通达信的标识代码
                symbol = symbol_list[0]

                # 品种信息
                future = futures.get_future(utility.get_commodity_code(symbol).upper())

                # 未管理的品种
                if future is None:
                    continue

                exchange = Exchange(future['exchange'])

                # 符合交易所规则的合约代码
                vt_symbol = utility.vt_symbol(symbol, exchange)

                # 第二行中,把空格替换成,
                lines[1] = ",".join(lines[1].split()) + "\r\n"

                # 去掉通达信头尾标识
                lines = lines[1:-1]

            # 修改过的内容,存到CSV
            with open(csv_file, "w", encoding="gb2312") as file:
                # 写回文件
                file.writelines(lines)

            # CSV导入
            self.signal_import_compiled.emit(f"正在导入: {vt_symbol}.{exchange.value}", False)
            self.load_tdx_csv_to_sqlite(csv_file, exchange, vt_symbol, Interval.MINUTE)

            count = count + 1

        # 通知画面导入结束
        self.signal_import_compiled.emit(f"数据导入完成,共导入 {count} 条记录", True)
Member
avatar
加入于:
帖子: 11
声望: 1

完整代码

import csv
import os
import sys
import threading
from datetime import datetime, timedelta, timezone
from functools import partial
from typing import List, Tuple, Dict

from PySide6 import QtCore
from PySide6.QtCore import QDate, Qt, QSize, QPoint
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QHeaderView, QTreeWidgetItem, QFileDialog, QTableWidgetItem, \
    QApplication
from pytz import timezone
from qfluentwidgets import PushButton, TreeWidget, TableWidget, PrimaryPushButton, SubtitleLabel, DateEdit, MessageBox, \
    FluentIcon, setTheme, Theme, StateToolTip

from vnpy.event import Event, EventEngine
from vnpy_data_manager.engine import ManagerEngine, APP_NAME
from guanlan_core.trader import futures, utility
from vnpy.trader.constant import Interval, Exchange
from vnpy.trader.database import BarOverview, get_database, BaseDatabase
from vnpy.trader.engine import MainEngine
from vnpy.trader.object import BarData

from guanlan_widgets import MessageBoxBase, MicaWindow
from guanlan_futures.common import resource

INTERVAL_NAME_MAP = {
    Interval.MINUTE: "分钟线",
    Interval.HOUR: "小时线",
    Interval.DAILY: "日线",
}


# from guanlan_futures.common import resource


class DataManagerWindow(MicaWindow):
    signal_log: QtCore.Signal = QtCore.Signal(Event)
    signal_update: QtCore.Signal = QtCore.Signal(Event)
    signal_contract: QtCore.Signal = QtCore.Signal(Event)
    signal_exception: QtCore.Signal = QtCore.Signal(Event)

    signal_import_compiled: QtCore.Signal = QtCore.Signal(str, bool)

    def __init__(self):
        super().__init__()

        self.setWindowIcon(QIcon(':/futures/icon/data.png'))
        self.setWindowTitle("数据管理")
        self.setMicaEffectEnabled(True)
        self.resize(1780, 960)

        self.event_engine = EventEngine()
        self.main_engine = MainEngine(self.event_engine)
        self.main_engine.add_engine(ManagerEngine)

        self.engine: ManagerEngine = self.main_engine.get_engine(APP_NAME)

        self.init_ui()

        # 移到屏幕中央显示
        w, h = desktop_size()
        self.move(w / 2 - self.width() / 2, h / 2 - self.height() / 2)
        self.showNormal()

    def closeEvent(self, event):
        self.engine.close()
        self.main_engine.close()
        event.accept()

    def init_ui(self) -> None:
        """"""

        self.init_tree()
        self.init_table()

        refresh_button: PrimaryPushButton = PrimaryPushButton(text="刷新", icon=FluentIcon.SYNC, parent=self)
        refresh_button.clicked.connect(self.refresh_tree)
        refresh_button.setFixedSize(110, 32)

        self.import_button: PrimaryPushButton = PrimaryPushButton(text="导入数据", icon=FluentIcon.S$E_COPY,
                                                                  parent=self)
        self.import_button.clicked.connect(self.import_data)
        self.signal_import_compiled.connect(self.import_complied)

        # update_button: PrimaryPushButton = PrimaryPushButton(text="更新数据", icon=FluentIcon.HISTORY, parent=self)
        # update_button.clicked.connect(self.update_data)
        #
        # download_button: PrimaryPushButton = PrimaryPushButton(text="下载数据", icon=FluentIcon.DOWNLOAD, parent=self)
        # download_button.clicked.connect(self.download_data)

        hbox1: QHBoxLayout = QHBoxLayout()
        hbox1.addStretch()
        hbox1.addWidget(self.import_button)
        # hbox1.addWidget(update_button)
        # hbox1.addWidget(download_button)
        hbox1.addSpacing(20)
        hbox1.addWidget(refresh_button)

        hbox2: QVBoxLayout = QVBoxLayout()
        hbox2.addWidget(self.tree)
        hbox2.addWidget(self.table)
        hbox2.setSpacing(30)

        hbox2.setContentsMargins(0, 20, 0, 0)

        vbox: QVBoxLayout = QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)

        vbox.setContentsMargins(20, 40, 12, 12)

        self.setLayout(vbox)

    def init_tree(self) -> None:
        """"""
        labels: list = [
            "数据",
            "本地代码",
            "代码",
            "名称",
            "交易所",
            "数据量",
            "开始时间",
            "结束时间",
            "",
            "",
            ""
        ]

        self.tree: TreeWidget = TreeWidget()
        self.tree.setColumnCount(len(labels))
        self.tree.setHeaderLabels(labels)

        # 隐藏边框
        self.tree.setBorderVisible(False)
        self.tree.header().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)

        for column in range(8, 10):
            self.tree.header().setSectionResizeMode(column, QHeaderView.ResizeMode.Fixed)
            self.tree.setColumnWidth(column, 160)

    def init_table(self) -> None:
        """"""
        labels: list = [
            "时间",
            "开盘价",
            "最高价",
            "最低价",
            "收盘价",
            "成交量",
            "成交额",
            "持仓量"
        ]

        self.table: TableWidget = TableWidget()
        self.table.setColumnCount(len(labels))
        self.table.setHorizontalHeaderLabels(labels)

        # 隐藏边框
        self.table.setBorderVisible(False)

        self.table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch
        )

    def refresh_tree(self) -> None:
        """"""
        self.tree.clear()

        # 初始化节点缓存字典
        interval_childs: Dict[Interval, QTreeWidgetItem] = {}
        exchange_childs: Dict[tuple[Interval, Exchange], QTreeWidgetItem] = {}

        # 查询数据汇总,并基于合约代码进行排序
        overviews: List[BarOverview] = self.engine.get_bar_overview()
        overviews.sort(key=lambda x: x.symbol)

        # 添加数据周期节点
        for interval in [Interval.MINUTE, Interval.HOUR, Interval.DAILY]:
            interval_child = QTreeWidgetItem()
            interval_childs[interval] = interval_child

            interval_name: str = INTERVAL_NAME_MAP[interval]
            interval_child.setText(0, interval_name)

        # 遍历添加数据节点
        for overview in overviews:
            # 获取交易所节点
            key: tuple = (overview.interval, overview.exchange)
            exchange_child: QTreeWidgetItem = exchange_childs.get(key, None)

            if not exchange_child:
                interval_child: QTreeWidgetItem = interval_childs[overview.interval]

                exchange_child: QTreeWidgetItem = QTreeWidgetItem(interval_child)
                exchange_child.setText(0, overview.exchange.value)

                exchange_childs[key] = exchange_child

            #  创建数据节点
            item = QTreeWidgetItem(exchange_child)

            future_code = utility.get_commodity_code(overview.symbol).upper()
            future = futures.get_future(future_code)

            item.setText(1, f"{overview.symbol}.{overview.exchange.value}")
            item.setText(2, overview.symbol)
            item.setText(3, future['name'])
            item.setText(4, overview.exchange.value)
            item.setText(5, str(overview.count))
            item.setText(6, overview.start.strftime("%Y-%m-%d %H:%M:%S"))
            item.setText(7, overview.end.strftime("%Y-%m-%d %H:%M:%S"))

            output_button: PushButton = PushButton("导出")
            output_func = partial(
                self.output_data,
                overview.symbol,
                overview.exchange,
                overview.interval,
                overview.start,
                overview.end
            )
            output_button.clicked.connect(output_func)
            output_button.setFixedSize(80, 30)

            show_button: PushButton = PushButton(text="查看", parent=self)
            show_func = partial(
                self.show_data,
                overview.symbol,
                overview.exchange,
                overview.interval,
                overview.start,
                overview.end
            )
            show_button.clicked.connect(show_func)
            show_button.setFixedSize(80, 30)

            delete_button: PushButton = PushButton(text="删除", parent=self)
            delete_func = partial(
                self.delete_data,
                overview.symbol,
                overview.exchange,
                overview.interval
            )
            delete_button.clicked.connect(delete_func)
            delete_button.setFixedSize(80, 30)

            item.setSizeHint(8, QSize(20, 40))
            item.setSizeHint(9, QSize(20, 40))
            item.setSizeHint(10, QSize(20, 40))

            self.tree.setItemWidget(item, 8, show_button)
            self.tree.setItemWidget(item, 9, output_button)
            self.tree.setItemWidget(item, 10, delete_button)

        # 展开顶层节点
        self.tree.addTopLevelItems(list(interval_childs.values()))

        for interval_child in interval_childs.values():
            interval_child.setExpanded(True)

    def load_tdx_csv_to_sqlite(self, file_path: str, exchange: Exchange, symbol: str, interval: Interval):
        with open(file_path, "rt") as f:
            buf = [line.replace("\0", "") for line in f]

        reader = csv.DictReader(buf, delimiter=",")

        bars = []
        start = None
        count = 0
        tz = timezone("Asia/Shanghai")

        for item in reader:
            tt1 = item["时间"]

            ln = len(tt1)
            tt2 = tt1[0:2]
            tt3 = tt1[2:4]
            tt1 = f"{tt2}:{tt3}"

            dt = datetime.strptime(item["日期"] + ' ' + tt1, '%Y/%m/%d %H:%M')
            # minute - 1;
            dt = dt + timedelta(minutes=-1)
            # time > 15:00, day - 1
            if dt.hour > 15:
                dt = dt + timedelta(days=-1)
            dt = tz.localize(dt)

            bar = BarData(
                symbol=symbol,
                exchange=exchange,
                interval=interval,
                datetime=dt,
                open_price=float(item["开盘"]),
                high_price=float(item["最高"]),
                low_price=float(item["最低"]),
                close_price=float(item["收盘"]),
                volume=float(item["成交量"]),
                turnover=float(item["持仓量"]),
                open_interest=float(item["结算价"]),
                gateway_name="sqlite",
            )
            bars.append(bar)

            # do some statistics
            count += 1
            if not start:
                start = bar.datetime
        end = bar.datetime

        # insert into database
        database: BaseDatabase = get_database()
        database.save_bar_data(bars)
        return f'{symbol} 导入完成: {count} from {start} - {end}'

    def import_tdx_export_folder(self, tdx_export_folder: str):
        """从通达信1分钟导出数据文件夹导入"""

        count = 0
        list_folder = os.listdir(tdx_export_folder)
        for filename in list_folder:

            # 过滤
            extension = filename.split('.')

            file_path: str = os.path.join(tdx_export_folder, filename)
            csv_file: str = os.path.join(tdx_export_folder, f"{extension[0]}.csv")

            if extension[1] == "csv":
                continue

            if extension[1] == "txt" and os.path.exists(csv_file):
                continue

            # 打开文件
            with open(file_path, "r", encoding="gb2312") as file:
                lines = file.readlines()

                # 取代码,获取合约信息
                symbol_list = lines[0].split()

                # 通达信的标识代码
                symbol = symbol_list[0]

                # 品种信息
                future = futures.get_future(utility.get_commodity_code(symbol).upper())

                # 未管理的品种
                if future is None:
                    continue

                exchange = Exchange(future['exchange'])

                # 符合交易所规则的合约代码
                vt_symbol = utility.vt_symbol(symbol, exchange)

                # 第二行中,把空格替换成,
                lines[1] = ",".join(lines[1].split()) + "\r\n"

                # 去掉通达信头尾标识
                lines = lines[1:-1]

            # 修改过的内容,存到CSV
            with open(csv_file, "w", encoding="gb2312") as file:
                # 写回文件
                file.writelines(lines)

            # CSV导入
            self.signal_import_compiled.emit(f"正在导入: {vt_symbol}.{exchange.value}", False)
            self.load_tdx_csv_to_sqlite(csv_file, exchange, vt_symbol, Interval.MINUTE)

            count = count + 1

        # 通知画面导入结束
        self.signal_import_compiled.emit(f"数据导入完成,共导入 {count} 条记录", True)

    def import_complied(self, message: str, complied: bool):
        self.stateTooltip.setContent(message)

        if complied:
            self.stateTooltip.setState(True)
            self.import_button.setEnabled(True)

    def import_data(self) -> None:
        """"""
        default_folder = "D:/08.Trade/Tdxqh/T0002/export"
        folder: QFileDialog = QFileDialog.getExistingDirectory(self, dir=default_folder)
        if not folder:
            return

        # 按钮不可用
        self.import_button.setDisabled(True)

        # 显示提示
        self.stateTooltip = StateToolTip("数据导入", "正在导入通达信数据,请稍候", self.window())

        self.stateTooltip.move(QPoint((self.window().width() - self.stateTooltip.width()) / 2, 50))
        self.stateTooltip.show()

        # 单独线程处理
        self.import_thread = threading.Thread(target=self.import_tdx_export_folder, args=(folder,))
        self.import_thread.start()

    def output_data(self, symbol: str, exchange: Exchange, interval: Interval, start: datetime, end: datetime) -> None:
        """"""
        # Get output date range
        dialog: DateRangeDialog = DateRangeDialog(start, end, self)
        if not dialog.exec():
            return
        start, end = dialog.get_date_range()

        # Get output file path
        path, _ = QFileDialog.getSaveFileName(
            self,
            "导出数据",
            "",
            "CSV(*.csv)"
        )
        if not path:
            return

        result: bool = self.engine.output_data_to_csv(
            path,
            symbol,
            exchange,
            interval,
            start,
            end
        )

        if not result:
            infobar.error("导出失败!", "该文件已在其他程序中打开,请关闭相关程序后再尝试导出数据。")

    def show_data(self, symbol: str, exchange: Exchange, interval: Interval, start: datetime, end: datetime) -> None:
        """"""
        # Get output date range
        dialog: DateRangeDialog = DateRangeDialog(start, end, self)

        if not dialog.exec():
            return
        start, end = dialog.get_date_range()

        bars: List[BarData] = self.engine.load_bar_data(
            symbol,
            exchange,
            interval,
            start,
            end
        )

        self.table.setRowCount(0)
        self.table.setRowCount(len(bars))

        for row, bar in enumerate(bars):
            self.table.setItem(row, 0, DataCell(bar.datetime.strftime("%Y-%m-%d %H:%M:%S")))
            self.table.setItem(row, 1, DataCell(str(round(bar.open_price, 3))))
            self.table.setItem(row, 2, DataCell(str(round(bar.high_price, 3))))
            self.table.setItem(row, 3, DataCell(str(round(bar.low_price, 3))))
            self.table.setItem(row, 4, DataCell(str(round(bar.close_price, 3))))
            self.table.setItem(row, 5, DataCell(str(bar.volume)))
            self.table.setItem(row, 6, DataCell(str(bar.turnover)))
            self.table.setItem(row, 7, DataCell(str(bar.open_interest)))

    def delete_data(self, symbol: str, exchange: Exchange, interval: Interval) -> None:

        title = "删除确认"
        content = f"请确认是否要删除{symbol} {exchange.value} {interval.value}的全部数据"
        w = MessageBox(title, content, self.window())
        w.setContentCopyable(True)
        if not w.exec():
            return

        count: int = self.engine.delete_bar_data(
            symbol,
            exchange,
            interval
        )

        infobar.success("删除成功", f"已删除{symbol} {exchange.value} {interval.value}共计{count}条数据")

    def show(self) -> None:
        """"""
        self.showMaximized()

    def output(self, msg: str) -> None:
        """输出下载过程中的日志"""
        infobar.warning("数据下载", msg)


class DataCell(QTableWidgetItem):
    """"""

    def __init__(self, text: str = "") -> None:
        super().__init__(text)

        self.setTextAlignment(Qt.AlignCenter)


class DateRangeDialog(MessageBoxBase):
    """"""

    def __init__(self, start: datetime, end: datetime, parent=None) -> None:
        """"""
        super().__init__(parent)

        self.titleLabel = SubtitleLabel('选择数据区间', self)
        self.viewLayout.addWidget(self.titleLabel)

        # 账户标识
        self.start_edit: DateEdit = DateEdit(parent=self)
        self.start_edit.setDate(QDate(start.year, start.month, start.day))
        self.viewLayout.addWidget(self.start_edit)

        self.end_edit: DateEdit = DateEdit(parent=self)
        self.end_edit.setDate(QDate(end.year, end.month, end.day))
        self.viewLayout.addWidget(self.end_edit)

    def get_date_range(self) -> Tuple[datetime, datetime]:
        """"""
        start = self.start_edit.dateTime().toPython()
        end = self.end_edit.dateTime().toPython() + timedelta(days=1)
        return start, end


if __name__ == '__main__':

    setTheme(Theme.DARK)

    # 初始化应用和窗口
    app = QApplication(sys.argv)
    win = DataManagerWindow()

    app.exec()
Member
avatar
加入于:
帖子: 11
声望: 1

相关类: futures.py

from guanlan_core.trader import utility
from guanlan_core.trader.constant import Offset
from guanlan_core.trader.object import TradeData

# 通过Json读取连接参数
FUTURES_FILENAME = "futures.json"
futures_data: dict = utility.load_json(FUTURES_FILENAME)


def get_future(symbol):
    """获取合约基本信息"""

    # 存在则返回
    if symbol not in futures_data:
        return None

    return futures_data[symbol]


def edit_symbol(symbol: str, attr: str, value: str) -> tuple[bool, str]:
    if symbol not in futures_data:
        return False, "期货品种不存在"

    """属性编辑"""
    futures_data[symbol][attr] = value
    utility.save_json(FUTURES_FILENAME, futures_data)

    return True, "期货信息编辑成功"


def save_futures():
    utility.save_json(FUTURES_FILENAME, futures_data)


def commission(trade: TradeData, future: dict = None) -> float:
    if future is None:
        code = utility.get_commodity_code(trade.symbol).upper()
        future = get_future(code)

    # 合约乘数
    size = future['size']

    # 开仓
    if trade.offset == Offset.OPEN:

        # x%%比例手续费
        if future['open_ratio'] != 0:
            return future['open_ratio'] * trade.price * trade.volume * 0.0001 * size

        # 固定手续费
        else:
            return future['open']

    # 平今
    elif trade.offset == Offset.CLOSETODAY:

        # x%%比例手续费
        if future['close_today_ratio'] != 0:
            return future['close_today_ratio'] * trade.price * trade.volume * 0.0001 * size

        # 固定手续费
        else:
            return future['close_today']

    # 平昨,或其它
    else:

        # x%%比例手续费
        if future['close_ratio'] != 0:
            return future['close_ratio'] * trade.price * trade.volume * 0.0001 * size

        # 固定手续费
        else:
            return future['close']

futures.json

{
    "IF":{"name":"沪深300","exchange":"CFFEX","tick":0.2,"size":300,"vt_symbol":"","open_ratio":0.23,"open":0,"close_ratio":0.23,"close":0,"close_today_ratio":2.3,"close_today":0,"sina_key":"qz_qh"},
    "IC":{"name":"中证500","exchange":"CFFEX","tick":0.2,"size":200,"vt_symbol":"","open_ratio":0.23,"open":0,"close_ratio":0.23,"close":0,"close_today_ratio":2.3,"close_today":0,"sina_key":"zzgz_qh"},
    "IH":{"name":"上证50","exchange":"CFFEX","tick":0.2,"size":300,"vt_symbol":"","open_ratio":0.23,"open":0,"close_ratio":0.23,"close":0,"close_today_ratio":2.3,"close_today":0,"sina_key":"szgz_qh"},
    "IM":{"name":"中证1000","exchange":"CFFEX","tick":0.2,"size":200,"vt_symbol":"","open_ratio":0.23,"open":0,"close_ratio":0.23,"close":0,"close_today_ratio":2.3,"close_today":0,"sina_key":"im_qh"},
    "T":{"name":"十年国债","exchange":"CFFEX","tick":0.005,"size":10000,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"sngz_qh"},
    "TF":{"name":"五年国债","exchange":"CFFEX","tick":0.005,"size":10000,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"gz_qh"},
    "TS":{"name":"二年国债","exchange":"CFFEX","tick":0.005,"size":20000,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"engz_qh"},
    "TL":{"name":"三十年国债","exchange":"CFFEX","tick":0.01,"size":10000,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"tl_qh"},
    "AU":{"name":"沪金","exchange":"SHFE","tick":0.02,"size":1000,"vt_symbol":"","open_ratio":0,"open":10,"close_ratio":0,"close":10,"close_today_ratio":0,"close_today":10,"sina_key":"hj_qh"},
    "AG":{"name":"沪银","exchange":"SHFE","tick":1,"size":15,"vt_symbol":"","open_ratio":0.5,"open":0,"close_ratio":0.5,"close":0,"close_today_ratio":0.5,"close_today":0,"sina_key":"by_qh"},
    "CU":{"name":"沪铜","exchange":"SHFE","tick":10,"size":5,"vt_symbol":"","open_ratio":0.5,"open":0,"close_ratio":0.5,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"tong_qh"},
    "AL":{"name":"沪铝","exchange":"SHFE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":3,"sina_key":"lv_qh"},
    "ZN":{"name":"沪锌","exchange":"SHFE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"xing_qh"},
    "PB":{"name":"沪铅","exchange":"SHFE","tick":5,"size":5,"vt_symbol":"","open_ratio":0.4,"open":0,"close_ratio":0.4,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"qian_qh"},
    "NI":{"name":"沪镍","exchange":"SHFE","tick":10,"size":1,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":3,"sina_key":"ni_qh"},
    "SN":{"name":"沪锡","exchange":"SHFE","tick":10,"size":1,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":3,"sina_key":"xi_qh"},
    "RB":{"name":"螺纹钢","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":3,"open":0,"close_ratio":3,"close":0,"close_today_ratio":3,"close_today":0,"sina_key":"lwg_qh"},
    "WR":{"name":"线材","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":0.4,"open":0,"close_ratio":0.4,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"xc_qh"},
    "I":{"name":"铁矿石","exchange":"DCE","tick":0.5,"size":100,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":2,"close_today":0,"sina_key":"tks_qh"},
    "HC":{"name":"热卷","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"rzjb_qh"},
    "SS":{"name":"不锈钢","exchange":"SHFE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":2,"close_ratio":0,"close":2,"close_today_ratio":0,"close_today":0,"sina_key":"bxg_qh"},
    "SF":{"name":"硅铁","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"gt_qh"},
    "SM":{"name":"锰硅","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":0,"open":0,"close_ratio":0,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"mg_qh"},
    "JM":{"name":"焦煤","exchange":"DCE","tick":0.5,"size":60,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":3,"close_today":0,"sina_key":"jm_qh"},
    "J":{"name":"焦炭","exchange":"DCE","tick":0.5,"size":100,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1.4,"close_today":0,"sina_key":"jt_qh"},
    "FG":{"name":"玻璃","exchange":"CZCE","tick":1,"size":20,"vt_symbol":"","open_ratio":0,"open":6,"close_ratio":0,"close":6,"close_today_ratio":0,"close_today":6,"sina_key":"bl_qh"},
    "SP":{"name":"纸浆","exchange":"SHFE","tick":2,"size":10,"vt_symbol":"","open_ratio":0.5,"open":0,"close_ratio":0.5,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"zj_qh"},
    "FU":{"name":"燃油","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":0.5,"open":0,"close_ratio":0.5,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"ry_qh"},
    "LU":{"name":"低硫燃油","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":0.1,"open":0,"close_ratio":0.1,"close":0,"close_today_ratio":0.1,"close_today":0,"sina_key":"lu_qh"},
    "SC":{"name":"原油","exchange":"SHFE","tick":0.1,"size":1000,"vt_symbol":"","open_ratio":0,"open":20,"close_ratio":0,"close":20,"close_today_ratio":0,"close_today":0,"sina_key":"yy_qh"},
    "BC":{"name":"国际铜","exchange":"SHFE","tick":10,"size":5,"vt_symbol":"","open_ratio":0.1,"open":0,"close_ratio":0.1,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"bc_qh"},
    "EC":{"name":"欧运航线","exchange":"SHFE","tick":0.1,"size":50,"vt_symbol":"","open_ratio":6,"open":0,"close_ratio":6,"close":0,"close_today_ratio":12,"close_today":0,"sina_key":"ec_qh"},
    "BU":{"name":"沥青","exchange":"SHFE","tick":1,"size":10,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"lq_qh"},
    "PG":{"name":"LPG","exchange":"DCE","tick":1,"size":20,"vt_symbol":"","open_ratio":0,"open":6,"close_ratio":0,"close":6,"close_today_ratio":0,"close_today":6,"sina_key":"pg_qh"},
    "RU":{"name":"橡胶","exchange":"SHFE","tick":5,"size":10,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"xj_qh"},
    "NR":{"name":"20号胶","exchange":"SHFE","tick":5,"size":10,"vt_symbol":"","open_ratio":0.2,"open":0,"close_ratio":0.2,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"ehj_qh"},
    "L":{"name":"塑料","exchange":"DCE","tick":1,"size":5,"vt_symbol":"","open_ratio":0,"open":1,"close_ratio":0,"close":1,"close_today_ratio":0,"close_today":1,"sina_key":"lldpe_qh"},
    "TA":{"name":"PTA","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"pta_qh"},
    "V":{"name":"PVC","exchange":"DCE","tick":1,"size":5,"vt_symbol":"","open_ratio":0,"open":1,"close_ratio":0,"close":1,"close_today_ratio":0,"close_today":1,"sina_key":"pvc_qh"},
    "EG":{"name":"乙二醇","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":0,"close_ratio":0,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"yec_qh"},
    "MA":{"name":"甲醇","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"zc_qh"},
    "PP":{"name":"聚丙烯","exchange":"DCE","tick":1,"size":5,"vt_symbol":"","open_ratio":0,"open":1,"close_ratio":0,"close":1,"close_today_ratio":0,"close_today":1,"sina_key":"jbx_qh"},
    "EB":{"name":"苯乙烯","exchange":"DCE","tick":1,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":3,"sina_key":"byx_qh"},
    "UR":{"name":"尿素","exchange":"CZCE","tick":1,"size":20,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"ns_qh"},
    "SA":{"name":"纯碱","exchange":"CZCE","tick":1,"size":20,"vt_symbol":"","open_ratio":4,"open":0,"close_ratio":4,"close":0,"close_today_ratio":4,"close_today":0,"sina_key":"cj_qh"},
    "C":{"name":"玉米","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":1.2,"close_ratio":0,"close":1.2,"close_today_ratio":0,"close_today":1.2,"sina_key":"hym_qh"},
    "A":{"name":"豆一","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":2,"close_ratio":0,"close":2,"close_today_ratio":0,"close_today":2,"sina_key":"dd_qh"},
    "CS":{"name":"玉米淀粉","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":1.5,"close_ratio":0,"close":1.5,"close_today_ratio":0,"close_today":1.5,"sina_key":"ymdf_qh"},
    "B":{"name":"豆二","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":1,"close_ratio":0,"close":1,"close_today_ratio":0,"close_today":1,"sina_key":"de_qh"},
    "M":{"name":"豆粕","exchange":"DCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":1.5,"close_ratio":0,"close":1.5,"close_today_ratio":0,"close_today":1.5,"sina_key":"dp_qh"},
    "Y":{"name":"豆油","exchange":"DCE","tick":2,"size":10,"vt_symbol":"","open_ratio":0,"open":2.5,"close_ratio":0,"close":2.5,"close_today_ratio":0,"close_today":2.5,"sina_key":"dy_qh"},
    "RS":{"name":"菜籽","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":2,"close_ratio":0,"close":2,"close_today_ratio":0,"close_today":2,"sina_key":"ycz_qh"},
    "RM":{"name":"菜粕","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":1.5,"close_ratio":0,"close":1.5,"close_today_ratio":0,"close_today":1.5,"sina_key":"czp_qh"},
    "OI":{"name":"菜油","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":2,"close_ratio":0,"close":2,"close_today_ratio":0,"close_today":2,"sina_key":"czy_qh"},
    "P":{"name":"棕榈油","exchange":"DCE","tick":2,"size":10,"vt_symbol":"","open_ratio":0,"open":2.5,"close_ratio":0,"close":2.5,"close_today_ratio":0,"close_today":2.5,"sina_key":"zly_qh"},
    "CF":{"name":"棉花","exchange":"CZCE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":4.3,"close_ratio":0,"close":4.3,"close_today_ratio":0,"close_today":0,"sina_key":"mh_qh"},
    "SR":{"name":"白糖","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":0,"sina_key":"bst_qh"},
    "JD":{"name":"鸡蛋","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":1.5,"open":0,"close_ratio":1.5,"close":0,"close_today_ratio":0,"close_today":1.5,"sina_key":"jd_qh"},
    "AP":{"name":"苹果","exchange":"CZCE","tick":1,"size":10,"vt_symbol":"","open_ratio":0,"open":5,"close_ratio":0,"close":5,"close_today_ratio":0,"close_today":20,"sina_key":"xpg_qh"},
    "CJ":{"name":"红枣","exchange":"CZCE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":10,"close_ratio":0,"close":10,"close_today_ratio":0,"close_today":10,"sina_key":"hz_qh"},
    "PF":{"name":"短纤","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":0,"open":3,"close_ratio":0,"close":3,"close_today_ratio":0,"close_today":3,"sina_key":"pf_qh"},
    "PK":{"name":"花生","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":0,"open":4,"close_ratio":0,"close":4,"close_today_ratio":0,"close_today":4,"sina_key":"pk_qh"},
    "AO":{"name":"氧化铝","exchange":"SHFE","tick":1,"size":20,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"ao_qh"},
    "LC":{"name":"碳酸锂","exchange":"GFEXE","tick":50,"size":1,"vt_symbol":"","open_ratio":1.6,"open":0,"close_ratio":1.6,"close":0,"close_today_ratio":1.6,"close_today":0,"sina_key":"lc_qh"},
    "SI":{"name":"工业硅","exchange":"GFEXE","tick":5,"size":5,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":0,"close_today":0,"sina_key":"si_qh"},
    "BR":{"name":"丁二烯橡胶","exchange":"SHFE","tick":5,"size":5,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"br_qh"},
    "LH":{"name":"生猪","exchange":"DCE","tick":5,"size":16,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":2,"close_today":0,"sina_key":"lh_qh"},
    "PX":{"name":"对二甲苯","exchange":"CZCE","tick":2,"size":5,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"px_qh"},
    "SH":{"name":"烧碱","exchange":"CZCE","tick":1,"size":30,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":0,"close_today":1,"sina_key":"sh_qh"},
    "FB":{"name":"纤维板","exchange":"DCE","tick":0.5,"size":10,"vt_symbol":"","open_ratio":1,"open":0,"close_ratio":1,"close":0,"close_today_ratio":1,"close_today":0,"sina_key":"xwb_qh"},
    "CY":{"name":"棉纱","exchange":"CZCE","tick":5,"size":5,"vt_symbol":"","open_ratio":0,"open":4,"close_ratio":0,"close":4,"close_today_ratio":0,"close_today":0,"sina_key":"ms_qh"}
}
Member
avatar
加入于:
帖子: 11
声望: 1
def get_commodity_code(symbol):
    """
    返回合约的字母部分
    get 'rb' out of 'rb2401'
    # return  'rb'
    """
    com_code = ''
    for char in symbol:
        try:
            int(char)
            break
        except ValueError:
            com_code += char
    return com_code


def get_full_code(symbol):
    """
    3位加到4位
    add '2' to MA301, make it MA2301, for CZCE only
    """
    long_code = str.upper(symbol)
    com_c = get_commodity_code(symbol)
    ym = long_code[len(com_c):]

    if 3 == len(ym):
        full_code = ''.join([com_c, '2', ym])
    else:
        full_code = long_code

    return full_code


def vt_symbol(symbol: str, exchange: Exchange):

    long_code = str.upper(symbol)
    com_c = get_commodity_code(symbol)
    ym = long_code[len(com_c):]

    # 上期所,全小写,4位数字
    if exchange == Exchange.SHFE:
        return symbol.lower()

    # 郑期所,全大写,3位数字
    elif exchange == Exchange.CZCE:
        if 3 == len(ym):
            return f"{com_c}{ym}".upper()
        else:
            return f"{com_c}{ym[1:]}".upper()

    # 大期所,全小写,4位数字
    elif exchange == Exchange.DCE:
        return symbol.lower()

    return symbol
Member
avatar
加入于:
帖子: 260
声望: 3

老师您好,咨询一下,通达信虽然自定义日期是2010-01-01-至今,但是导出的数据只有近3-5年的数据,2010-01-01-至今无法全部导出吗?

Member
avatar
加入于:
帖子: 11
声望: 1

这个不清楚,要么可能是之前没有数据,要么可能有些品种那个时候还没有
具体的可以去通达信论坛上去问问吧

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】