avatar of 发明者量化-小小梦 发明者量化-小小梦
پر توجہ دیں نجی پیغام
4
پر توجہ دیں
1271
پیروکار

FMZ مقداری پلیٹ فارم پر مبنی حقیقی مارکیٹ سے چلنے والے نقلی تجارتی نظام کا ڈیزائن

میں تخلیق کیا: 2025-04-30 16:59:01, تازہ کاری: 2025-04-30 17:53:06
comments   1
hits   661

[TOC]

FMZ مقداری پلیٹ فارم پر مبنی حقیقی مارکیٹ سے چلنے والے نقلی تجارتی نظام کا ڈیزائن

دیباچہ

یہ مضمون PaperTrader کے ڈیزائن اور نفاذ کو متعارف کرایا گیا ہے، FMZ مقداری پلیٹ فارم پر مبنی ایک نقلی تجارتی نظام اور مارکیٹ کے حقیقی حالات کے مطابق۔ یہ نظام حقیقی وقت کی گہرائی سے مارکیٹ کے حالات کے ذریعے آرڈرز سے میل کھاتا ہے، ٹریڈنگ کے عمل کو مکمل طور پر نقل کرتا ہے جیسے کہ حکمت عملی کے آرڈر کی جگہ کا تعین، لین دین، اثاثوں میں تبدیلی اور فیس پروسیسنگ، مارکیٹ/حد کے آرڈرز، اثاثوں کو منجمد کرنے اور منسوخی کے آرکائیونگ کو سپورٹ کرتا ہے، اور حقیقی ٹریڈنگ سے پہلے حکمت عملی کی جانچ اور حقیقی رویے کی تصدیق کے لیے موزوں ہے۔ یہ مضمون تفصیل سے اس کے ڈیزائن کے تصور اور کلیدی نفاذ کی نظام کے فن تعمیر، میچنگ میکانزم، انٹرفیس کی مطابقت، وغیرہ کے نقطہ نظر سے وضاحت کرے گا، اور آن لائن جانے سے پہلے ایک محفوظ اور قابل اعتماد “انٹرمیڈیٹ سینڈ باکس” بنانے میں مقداری حکمت عملیوں کی مدد کے لیے مکمل عملی مظاہرے کے استعمال کے معاملات فراہم کرے گا۔

پیپر ٹریڈر کی ضروریات کا تجزیہ اور ڈیزائن

ڈیمانڈ درد پوائنٹس:

  • ایکسچینج سمولیشن ٹریڈنگ افراتفری اور غیر حقیقی ہے۔
  • ایکسچینج پر نقلی اکاؤنٹ کے لیے درخواست بوجھل ہے، اور ٹیسٹ فنڈز حاصل کرنا بوجھل ہے۔
  • بہت سے تبادلے ٹیسٹنگ ماحول فراہم نہیں کرتے ہیں۔

بیک ٹیسٹنگ اور حقیقی تجارت کے درمیان “گرے ایریا” کا مسئلہ

آپ کو نقلی تجارتی نظام کی ضرورت کیوں ہے؟

مقداری حکمت عملی کی ترقی کے پورے عمل میں، ہم عام طور پر “تاریخی بیک ٹیسٹنگ → ماحولیاتی جانچ → حقیقی تجارت” کے مراحل سے گزرتے ہیں۔ تاہم، تاریخی بیک ٹیسٹنگ اعداد و شمار کے اعداد و شمار کا استعمال کرتی ہے اور مارکیٹ کے حقیقی حالات میں حکمت عملی کی تاثیر پر کارروائی نہیں کر سکتی۔ تاہم، حقیقی تجارت کا مطلب فنڈز کی پرواز ہے، اور درمیانی جانچ کے ماحول کا فقدان ہماری تلاش میں ایک دردناک مقام بن گیا ہے۔ اس مسئلے کو حل کرنے کے لیے، ہمیں ایک ہلکا پھلکا سمولیشن ٹریڈنگ سسٹم ڈیزائن کرنے کی ضرورت ہے - PaperTrader، جو ریئل ٹائم مارکیٹ کے حالات (گہرائی، مارکیٹ کی قیمت) کو استعمال کر کے پورے تجارتی عمل کو نقل کر سکتا ہے جس میں آرڈر دینے، زیر التواء آرڈرز، لین دین، آرڈر کی واپسی، اثاثہ میں تبدیلیاں، اور کمیشن کٹوتیاں شامل ہیں، اور بالآخر حقیقی تجارتی سطح کے قریب حکمت عملی کی مکمل تصدیق۔

ڈیزائن کے اہداف اور کلیدی خصوصیات

    1. ریئل ٹائم مارکیٹ ڈرائیو GetTicker()، GetDepth() اور درجنوں دیگر انٹرفیس سمیت حقیقی تبادلے کی قیمتوں تک رسائی کے لیے FMZ استعمال کریں۔
    1. نقلی آرڈر کی جگہ کا تعین اور کٹوتی حد/مارکیٹ آرڈرز کو سپورٹ کرتا ہے، میکر/ٹیکر فیس کی الگ کٹوتی کرتا ہے، اور مارکیٹ خرید آرڈرز کے لیے اصل حساب شدہ ان پٹ رقم کا استعمال کرتا ہے۔
    1. اثاثہ/پوزیشن/آرڈر مینجمنٹ یہ آرڈر دیتے وقت اثاثوں کو منجمد کرنے اور آرڈر کینسل کرتے وقت انہیں واپس کرنے کی حمایت کرتا ہے، اور علامت کی سطح کی بنیاد پر متعدد اثاثوں/پوزیشنز/آرڈرز کی دیکھ بھال کی حمایت کرتا ہے۔
    1. آرڈر سائیکل مکمل کریں۔ آرڈرز کو تخلیق، انتظار، عملدرآمد سے لے کر منسوخی تک واضح طور پر منظم کیا جاتا ہے۔ عمل درآمد کے بعد، وہ خود بخود مقامی ڈیٹا بیس میں محفوظ ہو جاتے ہیں تاکہ مستقبل کے استفسار اور تجزیہ کی حمایت کی جا سکے۔
    1. صارف کا تجربہ حکمت عملی کے لیے کسی بھی آرڈر کال کو تبدیل کرنے کی ضرورت نہیں ہے، اور صرف ایکسچینج آبجیکٹ کو PaperTrader سے بدل کر نقلی تجارت کو نافذ کر سکتی ہے۔

لائبریری ڈیزائن کا جائزہ

نظام بنیادی طور پر تین حصوں پر مشتمل ہے:

  • پیپر ٹریڈر کلاس بنیادی نقلی اکاؤنٹ میں ڈیٹا کی دیکھ بھال جیسے اثاثے، آرڈرز، پوزیشنز، مارکیٹ کے حالات اور کنفیگریشن شامل ہیں۔

  • [سم انجن مماثل انجن]: بیک گراؤنڈ تھریڈ، مارکیٹ کی گہرائی کے مطابق موجودہ آرڈر کو اسکین کرتا ہے اور آپریشن کرتا ہے۔

  • 【ڈیٹا بیس آرکائیو】: بعد میں تجزیہ اور جائزہ لینے کے لیے مقامی ڈیٹا بیس میں مکمل/منسوخ شدہ آرڈرز لکھیں۔

مماثل انجن ڈیزائن

simEngine(ڈیٹا، لاک) پورے سمولیشن سسٹم کا مرکز ہے۔ یہ موجودہ زیر التواء آرڈرز سے لین دین کے لیے درست نقلی نتائج فراہم کرنے کے لیے حقیقی مارکیٹ گہرائی کے اعداد و شمار کے مطابق ایک لوپ میں ملتا ہے۔

اہم عمل میں شامل ہیں:

  • موجودہ زیر التواء آرڈرز، عہدوں، اثاثوں اور مارکیٹ کے حالات حاصل کریں۔
  • تمام استعمال شدہ علامتوں کی گہرائی حاصل کریں GetDepth؛
  • زیر التواء آرڈرز کو عبور کریں اور سمت (خرید/فروخت) کے مطابق آپریشن کی گہرائی (پوچھیں/بولیاں) منتخب کریں۔
  • اس بات کا تعین کریں کہ آیا لین دین اس بنیاد پر مکمل ہوا ہے کہ آیا قیمت آپریٹنگ شرائط کو پورا کرتی ہے۔
  • اگر لین دین مکمل ہو گیا ہے تو، آرڈر کی معلومات کو اپ ڈیٹ کریں اور شمار کریں جیسے کہ AvgPrice/DealAmount؛
  • میکر / لینے والے کی بنیاد پر ہینڈلنگ فیس کٹوتی کریں۔
  • اگر تمام احکامات پر عمل درآمد ہو چکا ہے، تو آرڈر کو محفوظ کر لیا جائے گا۔ دوسری صورت میں، یہ زیر التواء رہے گا.

انٹرفیس کی معلومات کی مطابقت

PaperTrader کو FMZ پلیٹ فارم کے حقیقی تجارتی انٹرفیس کے ساتھ زیادہ سے زیادہ سیدھ میں لانے کے لیے ڈیزائن کیا گیا ہے، بشمول لیکن ان تک محدود نہیں:

درجہ بندی انٹرفیس بیان کریں
آرڈر انٹرفیس Buy(price, amount) / Sell(price, amount) / CreateOrder(symbol, side, price, amount) آرڈر آپریشن
مارکیٹ انٹرفیس GetTicker() / GetDepth() / GetRecords() / GetTrades() براہ راست ایکسچینج کی حقیقی مارکیٹ قیمت کی درخواست کریں۔
آرڈر انٹرفیس GetOrders() / CancelOrder(id) / GetOrder(id) آرڈر آپریشنز کے لیے
اکاؤنٹ اور پوزیشن انٹرفیس GetAccount() / GetAssets() / GetPositions() اکاؤنٹ آپریشنز کے لیے
دیگر ترتیبات کا انٹرفیس SetCurrency() / SetDirection() دیگر ترتیبات

یہ ڈیزائن حکمت عملی کی منطق کو بغیر کسی ترمیم کے نقلی تجارتی ماحول میں براہ راست چلانے کی اجازت دیتا ہے۔ ایک کلک کے ساتھ تبادلے کو PaperTrader سے بدل کر، آپ بیک ٹیسٹنگ اور حقیقی تجارت کے درمیان حکمت عملی کو “درمیانی تہہ” میں منتقل کر سکتے ہیں۔

پیپر ٹریڈر ڈیزائن سورس کوڈ

class PaperTrader {
    constructor(exIdx, realExchange, assets, fee) {
        this.exIdx = exIdx
        this.e = realExchange
        this.name = realExchange.GetName() + "_PaperTrader"
        this.currency = realExchange.GetCurrency()
        this.baseCurrency = this.currency.split("_")[0]
        this.quoteCurrency = this.currency.split("_")[1]        
        this.period = realExchange.GetPeriod()
        this.fee = fee
        
        // 数据同步锁
        this.data = threading.Dict()
        this.dataLock = threading.Lock()
        // 初始化this.data
        this.data.set("assets", assets)
        this.data.set("orders", [])
        this.data.set("positions", [])

        // exchangeData
        let exchangeData = {
            "exIdx": this.exIdx,
            "fee": this.fee
        }

        // exchange Type
        if (this.name.includes("Futures_")) {
            this.exchangeType = "Futures"
            this.direction = "buy"
            this.marginLevel = 10
            this.contractType = "swap"
            this.e.SetContractType(this.contractType)

            // set exchangeData
            exchangeData["exchangeType"] = this.exchangeType
            exchangeData["marginLevel"] = this.marginLevel
        } else {            
            this.exchangeType = "Spot"
            
            // set exchangeData
            exchangeData["exchangeType"] = this.exchangeType
        }

        // 记录交易所相关信息,用于传入撮合引擎
        this.data.set("exchangeData", exchangeData)

        // database
        this.historyOrdersTblName = "HISTORY_ORDER"
        this.data.set("historyOrdersTblName", this.historyOrdersTblName)

        // init
        this.init()
    }
    
    // export
    SetCurrency(currency) {        
        let arrCurrency = currency.split("_")
        if (arrCurrency.length != 2) {
            this.e.Log(3, null, null, `invalid currency: ${currency}`)
            return 
        }

        this.currency = currency
        this.baseCurrency = arrCurrency[0]
        this.quoteCurrency = arrCurrency[1]

        return this.e.SetCurrency(currency)
    }

    SetContractType(contractType) {
        if (this.exchangeType == "Spot") {
            this.e.Log(3, null, null, `not support`)
            return 
        }

        if (!this.isValidContractType(contractType)) {
            this.e.Log(3, null, null, `invalid contractType: ${contractType}`)
            return 
        }

        this.contractType = contractType
        return this.e.SetContractType(contractType)
    }

    SetDirection(direction) {
        if (this.exchangeType == "Spot") {
            this.e.Log(3, null, null, `not support`)
            return 
        }

        if (direction != "buy" && direction != "sell" && direction != "closebuy" && direction != "closesell") {
            this.e.Log(3, null, null, `invalid direction: ${direction}`)
            return 
        }

        this.direction = direction
        return this.e.SetDirection(direction)
    }

    GetTicker(...args) {
        return this.e.GetTicker(...args)
    }

    GetDepth(...args) {
        return this.e.GetDepth(...args)
    }

    GetTrades(...args) {
        return this.e.GetTrades(...args)
    }

    GetRecords(...args) {
        return this.e.GetRecords(...args)
    }

    GetMarkets() {
        return this.e.GetMarkets()
    }

    GetTickers() {
        return this.e.GetTickers()
    }

    GetFundings(...args) {
        if (this.exchangeType == "Spot") {
            this.e.Log(3, null, null, `not support`)
            return 
        }

        return this.e.GetFundings(...args)
    }

    GetAccount() {
        let assets = this.data.get("assets")
        let acc = {"Balance": 0, "FrozenBalance": 0, "Stocks": 0, "FrozenStocks": 0}
        for (let asset of assets) {
            if (this.exchangeType == "Futures") {
                if (this.quoteCurrency == "USDT" || this.quoteCurrency == "USDC") {
                    if (asset["Currency"] == this.quoteCurrency) {
                        return {"Balance": asset["Amount"], "FrozenBalance": asset["FrozenAmount"], "Stocks": 0, "FrozenStocks": 0}
                    }
                } else if (this.quoteCurrency == "USD") {
                    if (asset["Currency"] == this.baseCurrency) {
                        return {"Balance": 0, "FrozenBalance": 0, "Stocks": asset["Amount"], "FrozenStocks": asset["FrozenAmount"]}
                    }                
                }
            } else if (this.exchangeType == "Spot") {
                if (asset["Currency"] == this.baseCurrency) {
                    // Stocks
                    acc["Stocks"] = asset["Amount"]
                    acc["FrozenStocks"] = asset["FrozenAmount"]
                } else if (asset["Currency"] == this.quoteCurrency) {
                    // Balance
                    acc["Balance"] = asset["Amount"]
                    acc["FrozenBalance"] = asset["FrozenAmount"]
                }
            }
        }

        return acc
    }

    GetAssets() {
        let assets = this.data.get("assets")
        return assets
    }

    GetOrders(symbol) {
        let ret = []
        let orders = this.data.get("orders")
        if (this.exchangeType == "Spot") {
            if (typeof(symbol) == "undefined") {
                return orders
            } else {
                let arrCurrency = symbol.split("_")
                if (arrCurrency.length != 2) {
                    this.e.Log(3, null, null, `invalid symbol: ${symbol}`)
                    return null 
                }

                for (let o of orders) {
                    if (o.Symbol == symbol) {
                        ret.push(o)
                    }
                }
                return ret 
            }
        } else if (this.exchangeType == "Futures") {
            if (typeof(symbol) == "undefined") {
                for (let o of orders) {
                    if (o.Symbol.includes(`${this.quoteCurrency}.${this.contractType}`)) {
                        ret.push(o)
                    }
                }
                return ret 
            } else {
                let arr = symbol.split(".")
                if (arr.length != 2) {
                    this.e.Log(3, null, null, `invalid symbol: ${symbol}`)
                    return null 
                }

                let currency = arr[0]
                let contractType = arr[1]
                let arrCurrency = currency.split("_")
                if (arrCurrency.length != 2) {
                    for (let o of orders) {
                        if (o.Symbol.includes(`${arrCurrency[0]}.${contractType}`)) {
                            ret.push(o)
                        }
                    }
                } else {
                    for (let o of orders) {
                        if (o.Symbol == symbol) {
                            ret.push(o)
                        }
                    }
                }
                return ret 
            }            
        } else {
            this.e.Log(3, null, null, `invalid exchangeType: ${this.exchangeType}`)
            return null 
        }
    }

    GetOrder(orderId) {
        let data = DBExec(`SELECT ORDERDATA FROM ${this.historyOrdersTblName} WHERE ID = ?`, orderId)
        // {"columns":["ORDERDATA"],"values":[]}
        if (!data) {
            this.e.Log(3, null, null, `Order not found: ${orderId}`)
            return null 
        }

        if (data && Array.isArray(data["values"]) && data["values"].length <= 0) {
            this.e.Log(3, null, null, `Order not found: ${orderId}`)
            return null 
        } else if (data["values"].length != 1) {
            this.e.Log(3, null, null, `invalid data: ${data["values"]}`)
            return null 
        } else {
            let ret = this.parseJSON(data["values"][0])
            if (!ret) {
                this.e.Log(3, null, null, `invalid data: ${data["values"]}`)
                return null 
            }

            return ret 
        }
    }

    Buy(price, amount) {
        return this.trade("Buy", price, amount)
    }

    Sell(price, amount) {
        return this.trade("Sell", price, amount)
    }

    trade(tradeType, price, amount) {
        if (this.exchangeType == "Spot") {
            let side = ""
            if (tradeType == "Buy") {
                side = "buy"
            } else if (tradeType == "Sell") {
                side = "sell"
            } else {
                this.e.Log(3, null, null, `invalid tradeType: ${tradeType}`)
                return null 
            }
            let symbol = this.currency
            return this.createOrder(symbol, side, price, amount)
        } else if (this.exchangeType == "Futures") {
            let compose = `${tradeType}_${this.direction}`
            if (compose != "Sell_closebuy" && compose != "Sell_sell" && compose != "Buy_buy" && compose != "Buy_closesell") {
                this.e.Log(3, null, null, `${tradeType}, invalid direction: ${this.direction}`)
                return null 
            }

            let side = this.direction
            let symbol = `${this.currency}.${this.contractType}`
            return this.createOrder(symbol, side, price, amount)
        } else {
            this.e.Log(3, null, null, `invalid exchangeType: ${this.exchangeType}`)
            return 
        }
    }

    CreateOrder(symbol, side, price, amount) {
        if (side != "buy" && side != "sell" && side != "closebuy" && side != "closesell") {
            this.e.Log(3, null, null, `invalid direction: ${side}`)
            return null 
        }
        if (this.exchangeType == "Spot") {
            if (side == "closebuy") {
                side = "sell"
            } else if (side == "closesell") {
                side = "buy"
            }
        }
        return this.createOrder(symbol, side, price, amount)
    }

    createOrder(symbol, side, price, amount) {
        this.dataLock.acquire()
        let isError = false 
        let orders = this.data.get("orders")
        let positions = this.data.get("positions")
        let assets = this.data.get("assets")

        // 检查amount
        if (amount <= 0) {
            this.e.Log(3, null, null, `invalid amount: ${amount}`)
            return null 
        }

        // 构造订单
        let order = {
            "Info": null,
            "Symbol": symbol,
            "Price": price,
            "Amount": amount,
            "DealAmount": 0,
            "AvgPrice": 0,
            "Status": ORDER_STATE_PENDING,
            "ContractType": symbol.split(".").length == 2 ? symbol.split(".")[1] : ""
        }

        let logType = null 
        switch (side) {
            case "buy":
                order["Type"] = ORDER_TYPE_BUY
                order["Offset"] = ORDER_OFFSET_OPEN
                logType = LOG_TYPE_BUY
                break
            case "sell":
                order["Type"] = ORDER_TYPE_SELL
                order["Offset"] = ORDER_OFFSET_OPEN
                logType = LOG_TYPE_SELL
                break
            case "closebuy":
                order["Type"] = ORDER_TYPE_SELL
                order["Offset"] = ORDER_OFFSET_CLOSE
                logType = LOG_TYPE_SELL
                break
            case "closesell":
                order["Type"] = ORDER_TYPE_BUY
                order["Offset"] = ORDER_OFFSET_CLOSE
                logType = LOG_TYPE_BUY
                break
            default:
                this.e.Log(3, null, null, `invalid direction: ${side}`)
                isError = true 
        }
        if (isError) {
            return null 
        }

        // 检查资产/持仓,资产/持仓不足报错
        let needAssetName = ""
        let needAsset = 0
        if (this.exchangeType == "Futures") {
            // 检查资产、持仓
            // to do 
        } else if (this.exchangeType == "Spot") {
            // 检查资产
            let arr = symbol.split(".")
            if (arr.length == 2) {
                this.e.Log(3, null, null, `invalid symbol: ${symbol}`)
                return null 
            }
            let currency = arr[0]

            let arrCurrency = currency.split("_")
            if (arrCurrency.length != 2) {
                this.e.Log(3, null, null, `invalid symbol: ${symbol}`)
                return null 
            }
            let baseCurrency = arrCurrency[0]
            let quoteCurrency = arrCurrency[1]
            needAssetName = side == "buy" ? quoteCurrency : baseCurrency            
            if (side == "buy" && price <= 0) {
                // market order of buy, amount is quantity by quoteCurrency
                needAsset = amount
            } else {
                // limit order, amount is quantity by baseCurrency
                needAsset = side == "buy" ? price * amount : amount
            }

            let canPostOrder = false 
            for (let asset of assets) {
                if (asset["Currency"] == needAssetName && asset["Amount"] >= needAsset) {
                    canPostOrder = true 
                }
            }
            if (!canPostOrder) {
                this.e.Log(3, null, null, `insufficient balance for ${needAssetName}, need: ${needAsset}, Account: ${JSON.stringify(assets)}`)
                return null 
            }
        } else {
            this.e.Log(3, null, null, `invalid exchangeType: ${this.exchangeType}`)
            return null 
        }

        // 生成订单ID, UnixNano() 使用纳秒时间戳
        let orderId = this.generateOrderId(symbol, UnixNano())
        order["Id"] = orderId

        // 更新pending中的订单记录
        orders.push(order)
        this.data.set("orders", orders)
        
        // 输出日志记录
        if (this.exchangeType == "Futures") {
            this.e.SetDirection(side)
        }   
        this.e.Log(logType, price, amount, `orderId: ${orderId}`)

        // 更新资产
        for (let asset of assets) {
            if (asset["Currency"] == needAssetName) {
                asset["Amount"] -= needAsset
                asset["FrozenAmount"] += needAsset
            }
        }
        this.data.set("assets", assets)

        this.dataLock.release()
        return orderId
    }

    CancelOrder(orderId) {
        this.dataLock.acquire()
        let orders = this.data.get("orders")
        let assets = this.data.get("assets")
        let positions = this.data.get("positions")

        let targetIdx = orders.findIndex(item => item.Id == orderId)
        if (targetIdx != -1) {
            // 目标订单
            let targetOrder = orders[targetIdx]

            // 更新资产
            if (this.exchangeType == "Futures") {
                // 合约交易所资产更新
                // to do
            } else if (this.exchangeType == "Spot") {
                let arrCurrency = targetOrder.Symbol.split("_")
                let baseCurrency = arrCurrency[0]
                let quoteCurrency = arrCurrency[1]

                let needAsset = 0
                let needAssetName = ""
                if (targetOrder.Type == ORDER_TYPE_BUY && targetOrder.Price <= 0) {
                    needAssetName = quoteCurrency
                    needAsset = targetOrder.Amount - targetOrder.DealAmount                    
                } else {
                    needAssetName = targetOrder.Type == ORDER_TYPE_BUY ? quoteCurrency : baseCurrency
                    needAsset = targetOrder.Type == ORDER_TYPE_BUY ? targetOrder.Price * (targetOrder.Amount - targetOrder.DealAmount) : (targetOrder.Amount - targetOrder.DealAmount)
                }

                for (let asset of assets) {
                    if (asset["Currency"] == needAssetName) {
                        asset["FrozenAmount"] -= needAsset
                        asset["Amount"] += needAsset
                    }
                }

                // 更新 assets
                this.data.set("assets", assets)
            } else {
                this.e.Log(3, null, null, `invalid exchangeType: ${this.exchangeType}`)
                return false 
            }

            // 更新撤销状态
            orders.splice(targetIdx, 1)
            targetOrder.Status = ORDER_STATE_CANCELED
            
            // 归档,写入数据库
            let strSql = [
                `INSERT INTO ${this.historyOrdersTblName} (ID, ORDERDATA)`,
                `VALUES ('${targetOrder.Id}', '${JSON.stringify(targetOrder)}');`
            ].join("")
            let ret = DBExec(strSql)
            if (!ret) {
                e.Log(3, null, null, `Order matched successfully, but failed to archive to database: ${JSON.stringify(o)}`)
            }
        } else {
            // 撤单失败
            this.e.Log(3, null, null, `Order not found: ${orderId}`)
            this.dataLock.release()
            return false 
        }
        this.data.set("orders", orders)
        this.e.Log(LOG_TYPE_CANCEL, orderId)

        this.dataLock.release()
        return true 
    }

    GetHistoryOrders(symbol, since, limit) {
        // 查询历史订单
        // to do
    }

    SetMarginLevel(symbol) {
        // 设置杠杆值
        // 同步 this.marginLevel 和 this.data 中的 exchangeData["marginLevel"]
        // to do    
    }

    GetPositions(symbol) {
        // 查询持仓
        // to do
        
        /*
        if (this.exchangeType == "Spot") {
            this.e.Log(3, null, null, `not support`)
            return 
        }

        let pos = this.data.get("positions")
        */
    }


    // engine
    simEngine(data, lock) {
        while (true) {
            lock.acquire()

            // get orders / positions / assets / exchangeData 
            let orders = data.get("orders")
            let positions = data.get("positions")
            let assets = data.get("assets")
            let exchangeData = data.get("exchangeData")
            let historyOrdersTblName = data.get("historyOrdersTblName")
            

            // get exchange idx and fee
            let exIdx = exchangeData["exIdx"]
            let fee = exchangeData["fee"]
            let e = exchanges[exIdx]

            // get exchangeType 
            let exchangeType = exchangeData["exchangeType"]
            let marginLevel = 0
            if (exchangeType == "Futures") {
                marginLevel = exchangeData["marginLevel"]
            }


            // get Depth 
            let dictTick = {}
            for (let order of orders) {
                dictTick[order.Symbol] = {}
            }
            for (let position of positions) {
                dictTick[position.Symbol] = {}
            }
            // 更新行情
            for (let symbol in dictTick) {
                dictTick[symbol] = e.GetDepth(symbol)
            }

            // 撮合
            let newPendingOrders = []
            for (let o of orders) {

                // 只处理pending订单
                if (o.Status != ORDER_STATE_PENDING) {
                    continue 
                }

                // 盘口无数据 
                let depth = dictTick[o.Symbol]
                if (!depth) {
                    e.Log(3, null, null, `Order canceled due to invalid order book data: ${JSON.stringify(o)}`)
                    continue 
                }

                // 根据订单方向,确定订单薄撮合方向
                let matchSide = o.Type == ORDER_TYPE_BUY ? depth.Asks : depth.Bids
                if (!matchSide || matchSide.length == 0) {
                    e.Log(3, null, null, `Order canceled due to invalid order book data: ${JSON.stringify(o)}`)
                    continue 
                }

                let remain = o.Amount - o.DealAmount
                let filledValue = 0
                let filledAmount = 0
                for (let level of matchSide) {
                    let levelAmount = level.Amount 
                    let levelPrice = level.Price
                    if ((o.Price > 0 && ((o.Type == ORDER_TYPE_BUY && o.Price >= levelPrice) || (o.Type == ORDER_TYPE_SELL && o.Price <= levelPrice))) || o.Price <= 0) {
                        if (exchangeType == "Spot" && o.Type == ORDER_TYPE_BUY && o.Price <= 0) {
                            // 现货市价单买单
                            let currentFilledQty = Math.min(levelAmount * levelPrice, remain)
                            remain -= currentFilledQty
                            filledValue += currentFilledQty
                            filledAmount += currentFilledQty / levelPrice
                        } else {
                            // 限价单,价格符合撮合;市价单,直接盘口撮合
                            let currentFilledAmount = Math.min(levelAmount, remain)
                            remain -= currentFilledAmount
                            filledValue += currentFilledAmount * levelPrice
                            filledAmount += currentFilledAmount
                        }
                        
                        // 初次判断,如果直接撮合,判定为 taker
                        if (typeof(o.isMaker) == "undefined") {
                            o.isMaker = false 
                        }
                    } else {
                        // 价格不符合撮合,初次判断,判定为 maker
                        if (typeof(o.isMaker) == "undefined") {
                            o.isMaker = true 
                        }
                        break
                    }

                    if (remain <= 0) {
                        // 订单成交完成
                        break 
                    }
                }

                // 订单有变动
                if (filledAmount > 0) {
                    // 更新订单变动
                    if (exchangeType == "Spot" && o.Type == ORDER_TYPE_BUY && o.Price <= 0) {
                        if (o.AvgPrice == 0) {
                            o.AvgPrice = filledValue / filledAmount
                            o.DealAmount += filledValue
                        } else {
                            o.AvgPrice = (o.DealAmount + filledValue) / (filledAmount + o.DealAmount / o.AvgPrice)
                            o.DealAmount += filledValue
                        }
                    } else {
                        o.AvgPrice = (o.DealAmount * o.AvgPrice + filledValue) / (filledAmount + o.DealAmount)
                        o.DealAmount += filledAmount
                    }

                    // 处理持仓更新
                    if (exchangeType == "Futures") {
                        // 期货,查找对应订单方向上的持仓,更新
                        // to do 

                        /*
                        if () {
                            // 查到对应持仓,更新
                        } else {
                            // 没有对应持仓,新建
                            let pos = {
                                "Info": null,
                                "Symbol": o.Symbol,
                                "MarginLevel": marginLevel,
                                "Amount": o.Amount,
                                "FrozenAmount": 0,
                                "Price": o.Price,
                                "Profit": 0,
                                "Type": o.Type == ORDER_TYPE_BUY ? PD_LONG : PD_SHORT,
                                "ContractType": o.Symbol.split(".")[1],
                                "Margin": o.Amount * o.Price / marginLevel  // to do USDT/USD contract Multiplier
                            }

                            positions.push(pos)
                        }
                        */ 
                    }

                    // 处理资产更新
                    if (exchangeType == "Futures") {
                        // 处理期货资产更新
                        // to do 
                    } else if (exchangeType == "Spot") {
                        // 处理现货资产更新
                        
                        let arrCurrency = o.Symbol.split("_")
                        let baseCurrency = arrCurrency[0]
                        let quoteCurrency = arrCurrency[1]
                        let minusAssetName = o.Type == ORDER_TYPE_BUY ? quoteCurrency : baseCurrency
                        let minusAsset = o.Type == ORDER_TYPE_BUY ? filledValue : filledAmount
                        let plusAssetName = o.Type == ORDER_TYPE_BUY ? baseCurrency : quoteCurrency
                        let plusAsset = o.Type == ORDER_TYPE_BUY ? filledAmount : filledValue
                        
                        // 手续费扣除
                        if (o.isMaker) {
                            plusAsset = (1 - fee["maker"]) * plusAsset
                        } else {
                            plusAsset = (1 - fee["taker"]) * plusAsset
                        }

                        for (let asset of assets) {
                            if (asset["Currency"] == minusAssetName) {
                                // asset["FrozenAmount"] -= minusAsset
                                asset["FrozenAmount"] = Math.max(0, asset["FrozenAmount"] - minusAsset)                                
                            } else if (asset["Currency"] == plusAssetName) {
                                asset["Amount"] += plusAsset
                            }
                        }
                    }
                }

                // 检测remain更新订单状态
                if (remain <= 0) {
                    // 订单完成,更新订单状态,更新均价,更新完成量
                    o.Status = ORDER_STATE_CLOSED
                    
                    // 完成的订单归档,记录到数据库
                    let strSql = [
                        `INSERT INTO ${historyOrdersTblName} (ID, ORDERDATA)`,
                        `VALUES ('${o.Id}', '${JSON.stringify(o)}');`
                    ].join("")
                    let ret = DBExec(strSql)
                    if (!ret) {
                        e.Log(3, null, null, `Order matched successfully, but failed to archive to database: ${JSON.stringify(o)}`)
                    }
                } else {
                    newPendingOrders.push(o)
                }
            }

            // 更新当前挂单数据
            data.set("orders", newPendingOrders)
            data.set("assets", assets)
            lock.release()
            Sleep(1000)
        }
    }

    // other
    isValidContractType(contractType) {
        // only support swap 
        let contractTypes = ["swap"]
        if (contractTypes.includes(contractType)) {
            return true 
        } else {
            return false 
        }
    }

    generateOrderId(symbol, ts) {
        let uuid = '', i, random
        for (i = 0; i < 36; i++) {
            if (i === 8 || i === 13 || i === 18 || i === 23) {
                uuid += '-'
            } else if (i === 14) {
                // 固定为4
                uuid += '4'
            } else if (i === 19) {
                // 高2位固定为10
                random = (Math.random() * 16) | 0
                uuid += ((random & 0x3) | 0x8).toString(16)
            } else {
                random = (Math.random() * 16) | 0
                uuid += random.toString(16)
            }
        }
        return `${symbol},${uuid}-${ts}`
    }

    parseJSON(strData) {
        let ret = null 
        try {
            ret = JSON.parse(strData)
        } catch (err) {
            Log("err.name:", err.name, ", err.stack:", err.stack, ", err.message:", err.message, ", strData:", strData)
        }
        return ret 
    }

    init() {
        threading.Thread(this.simEngine, this.data, this.dataLock)
        
        // 删除数据库 历史订单表
        DBExec(`DROP TABLE IF EXISTS ${this.historyOrdersTblName};`)
        
        // 重建 历史订单表
        let strSql = [
            `CREATE TABLE IF NOT EXISTS ${this.historyOrdersTblName} (`,
            "ID VARCHAR(255) NOT NULL PRIMARY KEY,",
            "ORDERDATA TEXT NOT NULL",
            ")"
        ].join("");
        DBExec(strSql)
    }
}

// extport
$.CreatePaperTrader = function(exIdx, realExchange, assets, fee) {
    return new PaperTrader(exIdx, realExchange, assets, fee)
}

// 用真实行情打造高效 Paper Trader
function main() {
    // create PaperTrader
    let simulateAssets = [{"Currency": "USDT", "Amount": 10000, "FrozenAmount": 0}]
    let fee = {"taker": 0.001, "maker": 0.0005}
    paperTraderEx = $.CreatePaperTrader(0, exchange, simulateAssets, fee)
    Log(paperTraderEx)

    // test GetTicker
    Log("GetTicker:", paperTraderEx.GetTicker())

    // test GetOrders
    Log("GetOrders:", paperTraderEx.GetOrders())

    // test Buy/Sell
    let orderId = paperTraderEx.Buy(-1, 0.1)
    Log("orderId:", orderId)

    // test GetOrder
    Sleep(1000)
    Log(paperTraderEx.GetOrder(orderId))

    Sleep(6000)
}

عملی مظاہرہ اور ٹیسٹ کیسز

فرم پیشکش

مندرجہ بالا کوڈ کو ایف ایم زیڈ پلیٹ فارم کی “ٹیمپلیٹ لائبریری” کے طور پر محفوظ کیا جا سکتا ہے۔mainفنکشن ٹیسٹ فنکشن ہے:

FMZ مقداری پلیٹ فارم پر مبنی حقیقی مارکیٹ سے چلنے والے نقلی تجارتی نظام کا ڈیزائن

اس طرح، جب آپ اصل میں ٹریڈنگ کر رہے ہوتے ہیں، تو آپ ایکسچینج آبجیکٹ کو کنفیگر کرتے وقت API KEY سٹرنگ لکھ سکتے ہیں۔ اس وقت، آرڈر دینے جیسی کارروائیاں دراصل ایکسچینج انٹرفیس تک رسائی حاصل نہیں کریں گی، لیکن نقلی نظام کے اثاثوں، آرڈرز، پوزیشنز اور دیگر ڈیٹا کو تخروپن کے لیے استعمال کریں گی۔ لیکن مارکیٹ کے حالات ایکسچینج کی حقیقی مارکیٹ کے حالات ہیں۔

توسیع اور اصلاح کی سمت

حکمت عملی کی ترقی میں نقلی نظام کی قدر PaperTrader ایک آزمائشی ماحول فراہم کرتا ہے جو حقیقی مارکیٹ کے بہت قریب ہے، جس سے ڈویلپرز کو بغیر کسی خطرے کے عمل درآمد کے رویے، آرڈر کی منطق، مماثل کارکردگی اور حکمت عملی کی سرمایہ کی تبدیلیوں کی تصدیق کرنے کی اجازت دیتا ہے۔ یہ مندرجہ ذیل منظرناموں کے لیے خاص طور پر موزوں ہے:

  • ملٹی اسٹریٹجی ڈیبگنگ کنکرنٹ ٹیسٹنگ
  • مارکیٹ کے مختلف حالات کے تحت حکمت عملیوں کی کارکردگی کی فوری تصدیق کریں۔
  • نقصانات سے بچنے کے لیے ڈیبگنگ کے دوران براہ راست ریئل ٹائم آرڈرز سے گریز کریں۔
  • کچھ روایتی تاریخی بیک ٹیسٹنگ توثیقی طریقوں کو تبدیل کریں۔

خالص بیک ٹیسٹنگ سے فرق

روایتی بیک ٹیسٹنگ تاریخی ڈیٹا پر مبنی ہے اور K کے ذریعے K چلاتی ہے، حقیقی لین دین کی تفصیلات کو نظر انداز کرتے ہوئے جیسے زیر التواء آرڈرز، جزوی لین دین، مماثل سلپیج، اور فیس کا ڈھانچہ۔ نقلی نظام:

  • ریئل ٹائم اقتباسات کا استعمال کریں (جامد تاریخی ڈیٹا نہیں)
  • حقیقی آرڈر لائف سائیکل کی تقلید کریں (بنائیں → پلیس آرڈر → میچ → عملدرآمد → منسوخ کریں)
  • درست طریقے سے فیس، slippage، اور اوسط لین دین کی قیمت کا حساب لگائیں
  • صرف “حکمت عملی ماڈل” کے بجائے “حکمت عملی کے طرز عمل” کی جانچ کرنا بہتر ہے۔
  • ریئل ٹائم تعیناتی کے درمیان ایک پل کے طور پر کام کرنا

پیپر ٹریڈر پر نوٹس مندرجہ بالا پیپر ٹریڈر صرف ایک ابتدائی ڈیزائن ہے (صرف ابتدائی کوڈ کا جائزہ اور جانچ کی گئی ہے)، اور مقصد ایک ڈیزائن آئیڈیا اور حل کا حوالہ فراہم کرنا ہے۔ پیپر ٹریڈر کو یہ جانچنے کے لیے بھی جانچنے کی ضرورت ہے کہ آیا مماثل منطق، آرڈر سسٹم، پوزیشن سسٹم، کیپٹل سسٹم اور دیگر ڈیزائنز معقول ہیں۔ وقت کی پابندیوں کی وجہ سے، صرف اسپاٹ ٹریڈنگ کو نسبتاً مکمل طور پر نافذ کیا گیا ہے، اور فیوچر کنٹریکٹس کے کچھ کام ابھی تک کرنے کی حالت میں ہیں۔

ممکنہ ممکنہ مسائل:

  • فلوٹنگ پوائنٹ کیلکولیشن کی خرابی۔
  • منطقی پروسیسنگ کی حدود۔
  • ترسیل کے معاہدوں کے لیے سپورٹ زیادہ پیچیدہ ہے۔
  • لیکویڈیشن میکانزم کا ڈیزائن زیادہ پیچیدہ ہے۔

اگلی ارتقاء کی سمت

PaperTrader کی درخواست کی قدر کو مزید بڑھانے کے لیے، اگلے مرحلے میں توسیع کے لیے درج ذیل ہدایات پر غور کیا جا سکتا ہے:

  • کنٹریکٹ سمولیشن کے لیے سپورٹ کو بہتر بنائیں (کوڈ میں حصہ لینے کے لیے نامکمل)۔
  • کنٹریکٹ پوزیشن اور لیوریج فنڈ مینجمنٹ (پوزیشن کے لحاظ سے، مکمل پوزیشن) کی حمایت کرتا ہے۔
  • فلوٹنگ منافع اور نقصان کا حساب کتاب اور جبری لیکویڈیشن میکانزم متعارف کروائیں۔

PaperTrader کے ذریعے، ہم نہ صرف حکمت عملیوں کے لیے ایک محفوظ جانچ کا ماحول فراہم کر سکتے ہیں، بلکہ “ریسرچ ماڈلز” سے “حقیقی پیداواری صلاحیت” تک حکمت عملیوں کے کلیدی لنک کو مزید فروغ دے سکتے ہیں۔

قارئین پیغامات چھوڑنے کے لیے خوش آمدید ہیں، پڑھنے کے لیے آپ کا شکریہ۔