Upgrade prosesor - mendukung impor file format CSV untuk menyediakan sumber data yang disesuaikan

Penulis:Mimpi kecil, Dibuat: 2020-05-23 15:44:47, Diperbarui: 2023-10-08 19:48:40

img

Re-upgrade file collector untuk mendukung impor file format CSV dan menyediakan sumber data yang disesuaikan

Pengguna terbaru perlu memiliki file CSV mereka sendiri sebagai sumber data, sehingga penemu dapat menggunakan sistem retesting platform perdagangan kuantitatif. Penemu menggunakan sistem retesting platform perdagangan kuantitatif yang memiliki banyak fitur, menggunakan sederhana dan efisien, sehingga selama mereka memiliki data, mereka dapat melakukan retesting, tidak lagi terbatas pada pertukaran yang didukung pusat data platform.

Pikiran Desain

Ide desain ini sangat sederhana, hanya dengan sedikit perubahan dari pengumpul pasar sebelumnya, kita menambahkan parameter ke pengumpul pasar.isOnlySupportCSVUntuk mengontrol apakah hanya menggunakan file CSV sebagai sumber data yang diberikan kepada sistem retargeting, ditambah satu parameterfilePathForCSV, yang digunakan untuk mengatur jalur untuk menempatkan file data CSV di server yang dijalankan oleh robot pengumpul data.isOnlySupportCSVApakah parameter disetel keTrueUntuk menentukan sumber data yang akan digunakan (data yang dikumpulkan sendiri, data dalam file CSV), perubahan ini terutama dilakukan pada perangkat lunak yang digunakan oleh pengguna.ProviderKelasdo_GETFungsi tersebut adalah sebagai berikut:

Apa itu CSV?

Comma-Separated Values (CSV, kadang-kadang juga disebut sebagai nilai pemisahan karakter, karena pemisahan karakter juga bisa bukan koma) adalah dokumen yang menyimpan data tabel dalam bentuk teks murni (angka dan teks). Teks murni berarti bahwa dokumen tersebut adalah urutan karakter, tidak berisi data yang harus dibaca seperti angka biner. File CSV terdiri dari rekaman tujuan arbitrer, yang dipisahkan oleh beberapa tanda pergantian baris; setiap rekaman terdiri dari bidang, yang dipisahkan oleh karakter atau string lainnya, yang paling umum adalah koma atau karakter tabel.

Tidak ada standar umum untuk format file CSV, tetapi ada aturan yang berlaku, biasanya untuk satu baris catatan, dengan judul pertama; data dalam setiap baris dengan interval koma.

Sebagai contoh, file CSV yang kami gunakan untuk pengujian dibuka dengan buku catatan seperti ini:img

Perhatikan bahwa baris pertama dari file CSV adalah judul tabel.

,open,high,low,close,vol

Kami hanya akan mengurutkan data ini dan kemudian membuat format yang membuat sistem pencarian mengkonfigurasi permintaan sumber data, yang telah diproses dalam kode artikel sebelumnya, hanya dengan sedikit modifikasi.

Kode yang diubah

import _thread
import pymongo
import json
import math
import csv
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qs, urlparse

def url2Dict(url):
    query = urlparse(url).query  
    params = parse_qs(query)  
    result = {key: params[key][0] for key in params}  
    return result

class Provider(BaseHTTPRequestHandler):
    def do_GET(self):
        global isOnlySupportCSV, filePathForCSV
        try:
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.end_headers()

            dictParam = url2Dict(self.path)
            Log("自定义数据源服务接收到请求,self.path:", self.path, "query 参数:", dictParam)
            
            # 目前回测系统只能从列表中选择交易所名称,在添加自定义数据源时,设置为币安,即:Binance
            exName = exchange.GetName()                                     
            # 注意,period为底层K线周期
            tabName = "%s_%s" % ("records", int(int(dictParam["period"]) / 1000))  
            priceRatio = math.pow(10, int(dictParam["round"]))
            amountRatio = math.pow(10, int(dictParam["vround"]))
            fromTS = int(dictParam["from"]) * int(1000)
            toTS = int(dictParam["to"]) * int(1000)

            # 要求应答的数据
            data = {
                "schema" : ["time", "open", "high", "low", "close", "vol"],
                "data" : []
            }
            
            if isOnlySupportCSV:
                # 处理CSV读取,filePathForCSV路径
                listDataSequence = []
                with open(filePathForCSV, "r") as f:
                    reader = csv.reader(f)
                    # 获取表头
                    header = next(reader)
                    headerIsNoneCount = 0
                    if len(header) != len(data["schema"]):
                        Log("CSV文件格式有误,列数不同,请检查!", "#FF0000")
                        return 
                    for ele in header:
                        for i in range(len(data["schema"])):
                            if data["schema"][i] == ele or ele == "":
                                if ele == "":
                                    headerIsNoneCount += 1
                                if headerIsNoneCount > 1:
                                    Log("CSV文件格式有误,请检查!", "#FF0000")
                                    return 
                                listDataSequence.append(i)
                                break
                    
                    # 读取内容
                    while True:
                        record = next(reader, -1)
                        if record == -1:
                            break
                        index = 0
                        arr = [0, 0, 0, 0, 0, 0]
                        for ele in record:
                            arr[listDataSequence[index]] = int(ele) if listDataSequence[index] == 0 else (int(float(ele) * amountRatio) if listDataSequence[index] == 5 else int(float(ele) * priceRatio))
                            index += 1
                        data["data"].append(arr)
                
                Log("数据:", data, "响应回测系统请求。")
                self.wfile.write(json.dumps(data).encode())
                return 
            
            # 连接数据库
            Log("连接数据库服务,获取数据,数据库:", exName, "表:", tabName)
            myDBClient = pymongo.MongoClient("mongodb://localhost:27017")
            ex_DB = myDBClient[exName]
            exRecords = ex_DB[tabName]
            
            # 构造查询条件:大于某个值{'age': {'$gt': 20}} 小于某个值{'age': {'$lt': 20}}
            dbQuery = {"$and":[{'Time': {'$gt': fromTS}}, {'Time': {'$lt': toTS}}]}
            Log("查询条件:", dbQuery, "查询条数:", exRecords.find(dbQuery).count(), "数据库总条数:", exRecords.find().count())
            
            for x in exRecords.find(dbQuery).sort("Time"):
                # 需要根据请求参数round和vround,处理数据精度
                bar = [x["Time"], int(x["Open"] * priceRatio), int(x["High"] * priceRatio), int(x["Low"] * priceRatio), int(x["Close"] * priceRatio), int(x["Volume"] * amountRatio)]
                data["data"].append(bar)
            
            Log("数据:", data, "响应回测系统请求。")
            # 写入数据应答
            self.wfile.write(json.dumps(data).encode())
        except BaseException as e:
            Log("Provider do_GET error, e:", e)


def createServer(host):
    try:
        server = HTTPServer(host, Provider)
        Log("Starting server, listen at: %s:%s" % host)
        server.serve_forever()
    except BaseException as e:
        Log("createServer error, e:", e)
        raise Exception("stop")

def main():
    LogReset(1)
    if (isOnlySupportCSV):
        try:
        # _thread.start_new_thread(createServer, (("localhost", 9090), ))         # 本机测试
            _thread.start_new_thread(createServer, (("0.0.0.0", 9090), ))         # VPS服务器上测试
            Log("开启自定义数据源服务线程,数据由CSV文件提供。", "#FF0000")
        except BaseException as e:
            Log("启动自定义数据源服务失败!")
            Log("错误信息:", e)
            raise Exception("stop")
        while True:
            LogStatus(_D(), "只启动自定义数据源服务,不收集数据!")
            Sleep(2000)
    
    exName = exchange.GetName()
    period = exchange.GetPeriod()
    Log("收集", exName, "交易所的K线数据,", "K线周期:", period, "秒")
    
    # 连接数据库服务,服务地址 mongodb://127.0.0.1:27017 具体看服务器上安装的mongodb设置
    Log("连接托管者所在设备mongodb服务,mongodb://localhost:27017")
    myDBClient = pymongo.MongoClient("mongodb://localhost:27017")   
    # 创建数据库
    ex_DB = myDBClient[exName]
    
    # 打印目前数据库表
    collist = ex_DB.list_collection_names()
    Log("mongodb ", exName, " collist:", collist)
    
    # 检测是否删除表
    arrDropNames = json.loads(dropNames)
    if isinstance(arrDropNames, list):
        for i in range(len(arrDropNames)):
            dropName = arrDropNames[i]
            if isinstance(dropName, str):
                if not dropName in collist:
                    continue
                tab = ex_DB[dropName]
                Log("dropName:", dropName, "删除:", dropName)
                ret = tab.drop()
                collist = ex_DB.list_collection_names()
                if dropName in collist:
                    Log(dropName, "删除失败")
                else :
                    Log(dropName, "删除成功")
    
    # 开启一个线程,提供自定义数据源服务
    try:
        # _thread.start_new_thread(createServer, (("localhost", 9090), ))     # 本机测试
        _thread.start_new_thread(createServer, (("0.0.0.0", 9090), ))         # VPS服务器上测试
        Log("开启自定义数据源服务线程", "#FF0000")
    except BaseException as e:
        Log("启动自定义数据源服务失败!")
        Log("错误信息:", e)
        raise Exception("stop")
    
    # 创建records表
    ex_DB_Records = ex_DB["%s_%d" % ("records", period)]
    Log("开始收集", exName, "K线数据", "周期:", period, "打开(创建)数据库表:", "%s_%d" % ("records", period), "#FF0000")
    preBarTime = 0
    index = 1
    while True:
        r = _C(exchange.GetRecords)
        if len(r) < 2:
            Sleep(1000)
            continue
        if preBarTime == 0:
            # 首次写入所有BAR数据
            for i in range(len(r) - 1):
                bar = r[i]
                # 逐根写入,需要判断当前数据库表中是否已经有该条数据,基于时间戳检测,如果有该条数据,则跳过,没有则写入
                retQuery = ex_DB_Records.find({"Time": bar["Time"]})
                if retQuery.count() > 0:
                    continue
                
                # 写入bar到数据库表
                ex_DB_Records.insert_one({"High": bar["High"], "Low": bar["Low"], "Open": bar["Open"], "Close": bar["Close"], "Time": bar["Time"], "Volume": bar["Volume"]})                
                index += 1
            preBarTime = r[-1]["Time"]
        elif preBarTime != r[-1]["Time"]:
            bar = r[-2]
            # 写入数据前检测,数据是否已经存在,基于时间戳检测
            retQuery = ex_DB_Records.find({"Time": bar["Time"]})
            if retQuery.count() > 0:
                continue
            
            ex_DB_Records.insert_one({"High": bar["High"], "Low": bar["Low"], "Open": bar["Open"], "Close": bar["Close"], "Time": bar["Time"], "Volume": bar["Volume"]})
            index += 1
            preBarTime = r[-1]["Time"]
        LogStatus(_D(), "preBarTime:", preBarTime, "_D(preBarTime):", _D(preBarTime/1000), "index:", index)
        # 增加画图展示
        ext.PlotRecords(r, "%s_%d" % ("records", period))
        Sleep(10000)
        

Uji coba

Pertama, kita mengaktifkan robot pengumpul pasar, kita menambahkan sebuah bursa ke robot, dan robot itu berjalan. Konfigurasi parameter:img

img

Dan kemudian kami membuat strategi uji coba:

function main() {
    Log(exchange.GetRecords())
    Log(exchange.GetRecords())
    Log(exchange.GetRecords())
}

Strategi ini sangat sederhana, hanya mengambil dan mencetak tiga kali data K-line.

Halaman retargeting, yang mengatur sumber data sistem retargeting sebagai sumber data kustom, dan alamat mengisi alamat server yang dijalankan oleh robot pengumpul transaksi. Karena data dalam file CSV kami adalah 1 menit K-line. Jadi saat retargeting, kita mengatur siklus K-line menjadi 1 menit.

img

Setelah mengklik untuk memulai retesting, robot pengumpul pasar menerima permintaan data:img

Setelah kebijakan pelaksanaan sistem retest selesai, grafik baris K dihasilkan berdasarkan data baris K dari sumber data.img

Perbandingan data dalam file:img

img

RecordsCollector (ditingkatkan untuk menyediakan sumber data khusus, mendukung file data CSV untuk sumber data)

"Saya tidak akan membiarkan orang-orang yang tidak bertanggung jawab melakukan hal-hal yang tidak diinginkan.


Berkaitan

Lebih banyak

PergilahApakah Python harus diinstal di server administrator?

Spada bermain kuantitatifDreamcatcher, sekarang sumber data kustom ini diulang di browser, dan ada masalah dengan akurasi data, cobalah.

AiKPM-/upload/asset/19cfcf5244f5e2cd73173.png /upload/asset/19c100ceb1eb25a38a970.png Hubungi robot, bagaimana harus mengisi alamat di sana, alamat server yang saya isi port 9090 tidak merespon di kolektor.

WeixxSilahkan tanyakan mengapa saya telah mengatur sumber data CSV khusus di server host, dengan permintaan halaman yang memiliki pengembalian data, dan kemudian tidak ada pengembalian data dalam retesting, ketika data langsung diatur hanya untuk dua data, terminal httpsserver dapat menerima permintaan, /upload/asset/1691b2d9549fcec81c12a.png /upload/asset/168f8050b3db4a84d7e2f.png /upload/asset/16a67eaa598e95b11edb9.png /upload/asset/169c6c4c3d286587b3e.ng /upload/asset/169e8dcdbf9c0c544pbac8.png

WeixxTanyakan mengapa saya telah mengatur sumber data CSV khusus di server yang di-host, dengan permintaan halaman yang memiliki pengembalian data, dan kemudian tidak ada pengembalian data dalam retesting, dan tidak ada permintaan ke /upload/asset/1691b2d9549fcec81c12a.png /upload/asset/168f8050b3db4a84d7e2f.png /upload/asset/16a67eaa598e95b11edb9.png /upload/asset/169c6c4c3d28d658795b3e.png /upload/asset/169e8dcdbf9c0c544png

QQ89520Bagaimana parameternya diatur?

khotbahJika Anda ingin melihat apa yang ada di dalam mata uang digital, Anda bisa melihat apa yang ada di dalam mata uang digital.

Dsaidasi 666

Mimpi kecilAnda harus memiliki python.

Spada bermain kuantitatifIni adalah bug sistem retest, dan sudah diperbaiki.

Mimpi kecilUntuk informasi lebih lanjut mengenai keakuratan pada dokumen API, coba lihat di bawah ini.

Mimpi kecilAnda perlu memahami artikel, kode. Ini adalah tentang menggunakan file CSV sebagai sumber data untuk memberikan data ke sistem backtesting.

Mimpi kecilLihat deskripsi di dokumentasi API.

WeixxApakah data kustom menggunakan metode exchange.GetData (()) untuk melakukan retesting sehingga baris K menjadi data kustom?

Mimpi kecilLayanan yang menyediakan sumber data yang disesuaikan harus berada di server dan harus IP publik. Sistem penelusuran layanan lokal tidak dapat diakses.

WeixxSilahkan tanyakan bagaimana cara membuat data ulangan lokal di server http, apakah ulangan lokal tidak mendukung ulangan sumber data kustom? Saya menambahkan exchanges pada ulangan lokal: [{ "eid":"Huobi","currency":"ETH_USDT","feeder":"http://127.0.0.1:9090"} parameter ini, dan mengubahnya menjadi IP robot juga tidak meminta server

Mimpi kecilData terlalu besar. Halaman web tidak dapat dimuat, selain itu DEMO Anda sedang meneliti, seharusnya tidak ada masalah, saya kira Anda telah mengaturnya dengan salah.

WeixxSaya data csv adalah satu menit K garis data mata uang lainnya, kemudian karena saat retargeting pasangan transaksi tidak dapat dipilih secara acak, maka robot dan pertukaran yang dipilih retargeting diatur untuk huobi, pasangan transaksi untuk BTC-USDT, data permintaan ini saya kadang-kadang di sisi robot dapat menerima permintaan, tetapi retargeting tidak dapat memperoleh data, dan saya mengubah timestamp csv dari detik menjadi milidetik juga tidak dapat memperoleh data.

Mimpi kecilApakah ada persyaratan untuk data yang didefinisikan ini? Misalnya, apakah bagian waktu bisa dilihat dalam milidetik dan detik?

Mimpi kecilData yang besar juga bisa, saya sudah mencobanya saat saya mengujinya.

WeixxData yang sangat kecil dapat diperoleh, tetapi ketika saya menentukan file CSV dengan data lebih dari satu menit, apakah data yang terlalu besar akan berdampak?

WeixxSaya saat ini mengkonfigurasi pada robot saya adalah pertukaran HUOBI, maka pasangan perdagangan yang juga diatur adalah BTC-USDT, dan ketika di-retest juga dikonfigurasi seperti itu, maka kode yang di-retest adalah menggunakan fungsi exchange.GetRecords (), apakah data yang didefinisikan ini memiliki persyaratan?

Mimpi kecilAnda bisa berada di ujung browser karena parameter query yang Anda tulis, sistem respons tidak dapat memicu bot menjawab, menunjukkan bahwa robot tidak menerima permintaan, menjelaskan bahwa saat respon, tempat itu dikonfigurasi dengan salah, memeriksa, debug dan menemukan masalah.

Mimpi kecilJika Anda ingin membaca file CSV Anda sendiri, Anda dapat mengatur jalur untuk file ini sesuai dengan gambar di bawah ini.