4
ফোকাস
1271
অনুসারী

ডিজিটাল কারেন্সি ফিউচারের ট্রেডিং লজিক নিয়ে কিছু চিন্তা

তৈরি: 2020-06-01 09:52:45, আপডেট করা হয়েছে: 2023-10-08 19:41:25
comments   6
hits   2406

ডিজিটাল কারেন্সি ফিউচারের ট্রেডিং লজিক নিয়ে কিছু চিন্তা

ডিজিটাল কারেন্সি ফিউচারের ট্রেডিং লজিক নিয়ে কিছু চিন্তা

সমস্যা দৃশ্যকল্প

দীর্ঘদিন ধরে, ডিজিটাল কারেন্সি এক্সচেঞ্জ পজিশন এপিআই ইন্টারফেসের ডেটা বিলম্ব সমস্যা আমাকে সর্বদা বিরক্ত করেছে। আমি এটি মোকাবেলা করার জন্য একটি উপযুক্ত উপায় খুঁজে পাইনি, তাই আমি এই সমস্যার দৃশ্যকল্প পুনরুত্পাদন করব। সাধারণত কন্ট্রাক্ট এক্সচেঞ্জ দ্বারা প্রদত্ত মার্কেট প্রাইস অর্ডার আসলে কাউন্টারপার্টি প্রাইস, তাই কখনও কখনও এই তথাকথিত “মার্কেট প্রাইস অর্ডার” ব্যবহার করা কিছুটা অবিশ্বস্ত হয়। তাই, যখন আমরা ডিজিটাল কারেন্সি ফিউচার ট্রেডিং কৌশল লিখি, তখন আমরা বেশিরভাগ সীমা অর্ডার ব্যবহার করি। প্রতিটি অর্ডার দেওয়ার পরে, অর্ডারটি সম্পূর্ণ হয়েছে কিনা এবং সংশ্লিষ্ট অবস্থানটি অনুষ্ঠিত হয়েছে কিনা তা দেখতে আমাদের অবস্থানটি পরীক্ষা করতে হবে। সমস্যাটি এই অবস্থানের তথ্যের মধ্যে রয়েছে যদি অর্ডারটি সম্পূর্ণ হয়, এক্সচেঞ্জ পজিশন ইনফরমেশন ইন্টারফেস দ্বারা প্রত্যাবর্তিত ডেটা (যা এক্সচেঞ্জ ইন্টারফেস যা আমরা যখন এক্সচেঞ্জ. গেটপজিশন কল করি তখন প্রকৃতপক্ষে অ্যাক্সেস করে) নতুন খোলা অবস্থানের তথ্য থাকা উচিত। কিন্তু যদি এক্সচেঞ্জ দ্বারা প্রত্যাবর্তিত ডেটা পুরানো ডেটা হয়, অর্থাৎ, অর্ডারটি কার্যকর করার আগে অবস্থানের তথ্য, সেখানে একটি সমস্যা হবে৷ ট্রেডিং লজিক মনে করতে পারে যে অর্ডারটি পূরণ করা হয়নি এবং অর্ডার দেওয়া চালিয়ে যেতে পারে। যাইহোক, এক্সচেঞ্জের অর্ডার ইন্টারফেস দেরি করে না, তবে লেনদেনটি খুব দ্রুত সম্পন্ন হয় এবং অর্ডারটি অবিলম্বে সম্পন্ন হয়। এর একটি গুরুতর পরিণতি হল যে কৌশলটি বারবার অর্ডার দেবে যখন একটি অবস্থান খোলার অপারেশন শুরু হয়।

বাস্তব অভিজ্ঞতা

এই সমস্যার কারণে, আমি এমন একটি কৌশল দেখেছি যা সৌভাগ্যবশত, সেই সময়ে বাজারটি বেড়ে গিয়েছিল এবং ফ্লোটিং লাভ একবার 10 বিটিসি ছাড়িয়ে গিয়েছিল। সৌভাগ্যবশত, বাজার দ্রুত বেড়েছে, যদি এটি হ্রাস পায়, তাহলে ফলাফলটি কল্পনা করা যেতে পারে।

সমাধান করার চেষ্টা করুন

  • বিকল্প 1 অর্ডার লজিকটি শুধুমাত্র একটি অর্ডার দেওয়ার কৌশলের জন্য ডিজাইন করা যেতে পারে, এবং অর্ডারের মূল্য হল সেই সময়ে প্রতিপক্ষের মূল্য এবং একটি বড় স্লিপ মূল্য, যাতে একটি নির্দিষ্ট গভীরতার প্রতিপক্ষের অর্ডার নেওয়া যায়। এর সুবিধা হল যে আপনি শুধুমাত্র একবার অর্ডার দেন এবং অবস্থানের তথ্যের উপর ভিত্তি করে কোন বিচার হয় না। এটি বারবার অর্ডারের সমস্যা এড়াতে পারে, কিন্তু কখনও কখনও যখন দামের পরিবর্তন তুলনামূলকভাবে বড় হয় তখন একটি অর্ডার দেওয়া এক্সচেঞ্জের মূল্য সীমা প্রক্রিয়াকে ট্রিগার করবে, এবং এটি সম্ভব যে এমনকি একটি বড় মূল্যের স্লিপ দিয়েও, লেনদেন এখনও সম্পূর্ণ হয়নি, এবং সুযোগ মিস করা হয়.

  • বিকল্প 2 এক্সচেঞ্জের মার্কেট অর্ডার ফাংশন ব্যবহার করে, FMZ-এ মূল্যে -1 আপলোড করা হল একটি মার্কেট অর্ডার বর্তমানে, OKEX ফিউচার ইন্টারফেসটি রিয়েল মার্কেট অর্ডার সমর্থন করার জন্য আপগ্রেড করা হয়েছে।

  • সমাধান ৩ আমরা এখনও আগের ট্রেডিং লজিক ব্যবহার করি এবং অর্ডার দেওয়ার জন্য লিমিট অর্ডার ব্যবহার করি, কিন্তু পজিশন ডেটাতে বিলম্বের কারণে সৃষ্ট সমস্যা সমাধানের চেষ্টা করার জন্য আমরা ট্রেডিং লজিকে কিছু সনাক্তকরণ যোগ করি। অর্ডার দেওয়ার পরে বাতিল না করে মুলতুবি অর্ডার তালিকা থেকে সরাসরি অদৃশ্য হয়ে গেছে কিনা তা শনাক্ত করুন (পেন্ডিং অর্ডার তালিকা থেকে অদৃশ্য হওয়ার দুটি সম্ভাবনা রয়েছে: 1. অর্ডার বাতিল করা, 2. যদি এমন পরিস্থিতি সনাক্ত করা হয়) , অর্ডার ভলিউম এবং অর্ডারের পরিমাণ আবার রাখা হবে এই সময়ে, আপনার অবস্থানের ডেটা বিলম্বিত হয়েছে কিনা সেদিকে মনোযোগ দেওয়া উচিত এবং অবস্থানের তথ্য পুনরায় প্রাপ্ত করা উচিত আপনি এমনকি ট্রিগার অপেক্ষার সংখ্যা বাড়াতে পারেন, এর মানে হল যে পজিশন ইন্টারফেস ডেটা বিলম্বিত হয়েছে এবং লেনদেন লজিক বন্ধ হয়ে গেছে।

বিকল্প 3 এর উপর ভিত্তি করে ডিজাইন

// 参数
/*
var MinAmount = 1
var SlidePrice = 5
var Interval = 500
*/

function GetPosition(e, contractType, direction) {
    e.SetContractType(contractType)
    var positions = _C(e.GetPosition);
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType == contractType && positions[i].Type == direction) {
            return positions[i]
        }
    }

    return null
}

function Open(e, contractType, direction, opAmount) {
    var initPosition = GetPosition(e, contractType, direction);
    var isFirst = true;
    var initAmount = initPosition ? initPosition.Amount : 0;
    var nowPosition = initPosition;
    var directBreak = false 
    var preNeedOpen = 0
    var timeoutCount = 0
    while (true) {
        var ticker = _C(e.GetTicker)
        var needOpen = opAmount;
        if (isFirst) {
            isFirst = false;
        } else {
            nowPosition = GetPosition(e, contractType, direction);
            if (nowPosition) {
                needOpen = opAmount - (nowPosition.Amount - initAmount);
            }
            // 检测directBreak 并且持仓未变的情况
            if (preNeedOpen == needOpen && directBreak) {
                Log("疑似仓位数据延迟,等待30秒", "#FF0000")
                Sleep(30000)
                nowPosition = GetPosition(e, contractType, direction);
                if (nowPosition) {
                    needOpen = opAmount - (nowPosition.Amount - initAmount);
                }
                /*
                timeoutCount++
                if (timeoutCount > 10) {
                    Log("连续10次疑似仓位延迟,下单失败!", "#FF0000")
                    break
                }
                */
            } else {
                timeoutCount = 0
            }
        }
        if (needOpen < MinAmount) {
            break;
        }
        
        var amount = needOpen;
        preNeedOpen = needOpen
        e.SetDirection(direction == PD_LONG ? "buy" : "sell");
        var orderId;
        if (direction == PD_LONG) {
            orderId = e.Buy(ticker.Sell + SlidePrice, amount, "开多仓", contractType, ticker);
        } else {
            orderId = e.Sell(ticker.Buy - SlidePrice, amount, "开空仓", contractType, ticker);
        }

        directBreak = false
        var n = 0
        while (true) {
            Sleep(Interval);
            var orders = _C(e.GetOrders);
            if (orders.length == 0) {
                if (n == 0) {
                    directBreak = true
                }
                break;
            }
            for (var j = 0; j < orders.length; j++) {
                e.CancelOrder(orders[j].Id);
                if (j < (orders.length - 1)) {
                    Sleep(Interval);
                }
            }
            n++
        }
    }

    var ret = {
        price: 0,
        amount: 0,
        position: nowPosition
    };
    if (!nowPosition) {
        return ret;
    }
    if (!initPosition) {
        ret.price = nowPosition.Price;
        ret.amount = nowPosition.Amount;
    } else {
        ret.amount = nowPosition.Amount - initPosition.Amount;
        ret.price = _N(((nowPosition.Price * nowPosition.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount);
    }
    return ret;
}

function Cover(e, contractType, opAmount, direction) {
    var initPosition = null;
    var position = null;
    var isFirst = true;

    while (true) {
        while (true) {
            Sleep(Interval);
            var orders = _C(e.GetOrders);
            if (orders.length == 0) {
                break;
            }
            for (var j = 0; j < orders.length; j++) {
                e.CancelOrder(orders[j].Id);
                if (j < (orders.length - 1)) {
                    Sleep(Interval);
                }
            }
        }

        position = GetPosition(e, contractType, direction)
        if (!position) {
            break
        }
        if (isFirst == true) {
            initPosition = position;
            opAmount = Math.min(opAmount, initPosition.Amount)
            isFirst = false;
        }

        var amount = opAmount - (initPosition.Amount - position.Amount)
        if (amount <= 0) {
            break
        }

        var ticker = _C(exchange.GetTicker)
        if (position.Type == PD_LONG) {
            e.SetDirection("closebuy");
            e.Sell(ticker.Buy - SlidePrice, amount, "平多仓", contractType, ticker);
        } else if (position.Type == PD_SHORT) {
            e.SetDirection("closesell");
            e.Buy(ticker.Sell + SlidePrice, amount, "平空仓", contractType, ticker);
        }

        Sleep(Interval)
    }

    return position
}

$.OpenLong = function(e, contractType, amount) {
    if (typeof(e) == "string") {
        amount = contractType
        contractType = e
        e = exchange
    }

    return Open(e, contractType, PD_LONG, amount);
}

$.OpenShort = function(e, contractType, amount) {
    if (typeof(e) == "string") {
        amount = contractType
        contractType = e
        e = exchange
    }

    return Open(e, contractType, PD_SHORT, amount);
};

$.CoverLong = function(e, contractType, amount) {
    if (typeof(e) == "string") {
        amount = contractType
        contractType = e
        e = exchange
    }

    return Cover(e, contractType, amount, PD_LONG);
};

$.CoverShort = function(e, contractType, amount) {
    if (typeof(e) == "string") {
        amount = contractType
        contractType = e
        e = exchange
    }

    return Cover(e, contractType, amount, PD_SHORT);
};


function main() {
    Log(exchange.GetPosition())
    var info = $.OpenLong(exchange, "quarter", 100)
    Log(info, "#FF0000")

    Log(exchange.GetPosition())
    info = $.CoverLong(exchange, "quarter", 30)
    Log(exchange.GetPosition())
    Log(info, "#FF0000")

    info = $.CoverLong(exchange, "quarter", 80)
    Log(exchange.GetPosition())
    Log(info, "#FF0000")
}

টেমপ্লেট ঠিকানা: https://www.fmz.com/strategy/203258

টেমপ্লেট ইন্টারফেস কল করার উপায় ঠিক উপরের প্রধান ফাংশনের মত$.OpenLong$.CoverLong。 টেমপ্লেটটি একটি বিটা সংস্করণ প্রস্তাবনাগুলিকে স্বাগত জানাই এবং অবস্থান ডেটা বিলম্বের সমস্যা সমাধানের জন্য অপ্টিমাইজ করা অব্যাহত থাকবে৷