Langkau tajuk talian

Penukaran kadar simbol

Penukaran kadar simbol adalah tindakan penting jika anda ingin aplikasi anda termasuk fungsi berkaitan pengiraan P&L.

Kes penggunaan

Melakukan penukaran kadar simbol adalah perlu apabila mengira komisen, swap dan dividen. Komisen, swap dan dividen biasanya dinyatakan dalam USD. Walau bagaimanapun, bergantung pada broker dan keutamaan anda, mata wang deposit akaun anda mungkin dalam aset lain selain USD. Untuk menentukan komisen, swap dan dividen dengan tepat, anda perlu mengetahui kadar pertukaran antara USD dan mata wang deposit akaun anda.

Rantai penukaran

Bayangkan harga setiap simbol individu sebagai kadar penukaran antara aset asas dan aset kutipannya. Tugas mendapatkan kadar penukaran menjadi rumit jika tiada simbol yang secara langsung menghubungkan sesuatu aset kepada aset lain. Nasib baik, backend cTrader boleh secara automatik mencipta rantai penukaran yang bertindak sebagai laluan penukaran terpendek dari satu aset ke aset lain.

Sebagai contoh, jika anda ingin menukar EUR kepada NZD, dan tiada simbol EURNZD pada pelayan dagangan, cTrader akan mencadangkan rantai yang akan berfungsi sebagai langkah perantaraan dalam proses penukaran. Bergantung pada ketersediaan simbol, rantai tersebut mungkin kelihatan seperti EURUSD-USDCAD-CADNZD, EURCFH-CFHUSD-USDNZD, atau bahkan EURCAD-CADUSD-USDCFH-CFHNZD.

Untuk menukar antara dua aset, aplikasi anda perlu melakukan tindakan berikut:

  • Akses ID aset yang anda ingin tukar antara satu sama lain.
  • Dapatkan rantai penukaran yang termasuk satu atau lebih simbol ringan.
  • Langgan dan urus acara spot.
  • Ikuti rantai penukaran dan kembalikan kadar penukaran akhir.

Di bawah, kami menerangkan setiap tindakan ini secara terperinci.

Akses ID aset

ID aset boleh diakses secara langsung sebagai sifat entiti simbol ringan (diwakili oleh mesej model ProtoOALightSymbol). Selain itu, anda boleh mendapatkan ID mata wang deposit akaun secara langsung daripada entiti berkaitan ProtoOaTrader.

Perhatikan bahawa contoh di bawah hanya mendapatkan ID aset untuk satu simbol. Untuk melakukan operasi yang sama untuk pelbagai simbol, anda perlu menambah gelung atau menggunakan penyelesaian lain yang sesuai (contohnya, kaedah map() atau setara).

Bekerja dengan JSON

Apabila bekerja dengan JSON, anda masih boleh menggunakan semula kod di bawah dan coretan kod lain dalam tutorial ini. Walau bagaimanapun, pastikan untuk menggantikan nama kelas Proto... dengan nama kelas tersuai anda dan membuat pengubahsuaian yang diperlukan untuk mengambil kira klien TCP dan WebSocket pilihan anda.

symbol adalah pembolehubah jenis ProtoOALightSymbol. Jika anda menggunakan JSON, ia boleh menjadi sebarang jenis tersuai yang mewakili mesej ProtoOALightSymbol.

1
2
int baseAssetId = symbol.BaseAsset.AssetId; 
int quoteAssetId = symbol.QuoteAsset.AssetId;

Dalam contoh di bawah, kami mendapatkan ID mata wang deposit objek account yang merupakan jenis ProtoOATrader.

1
int depositAssetId = account.DepositAssetId;

symbol adalah pembolehubah yang mewakili mesej jenis ProtoOALightSymbol.

1
2
baseAssetId = symbol.BaseAsset.AssetId
quoteAssetId = symbol.QuoteAsset.AssetId

Dalam contoh di bawah, kami mendapatkan ID mata wang deposit objek account yang mewakili mesej model ProtoOATrader.

1
depositAssetId = account.depositAssetId

Dapatkan rantai penukaran

Untuk mendapatkan rantai penukaran yang sah, aplikasi anda perlu menghantar mesej ProtoOASymbolsForConversionReq dengan medan firstAssetId dan lastAssetId ditetapkan kepada ID aset yang anda ingin tukar antara satu sama lain. Apabila anda menerima respons jenis ProtoOASymbolsForConversionRes, simpan medan berulang symbol dalam koleksi.

Berikut adalah cara anda boleh melakukan tindakan ini melalui SDK rasmi.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
List<ProtoOALightSymbol> lightSymbolsForConversion = new List<ProtoOALightSymbol>();

var symbolsResult = await GetConversionSymbols(accountId, isLive, baseAssetId, quoteAssetId);

lightSymbolsForConversion.AddRange(symbolsResult);

public Task<ProtoOALightSymbol[]> GetConversionSymbols(long accountId, bool isLive, long baseAssetId, long quoteAssetId)
{
    VerifyConnection();

    var client = GetClient(isLive);

    var taskCompletionSource = new TaskCompletionSource<ProtoOALightSymbol[]>();

    IDisposable disposable = null;

    disposable = client.OfType<ProtoOASymbolsForConversionRes>().Where(response => response.CtidTraderAccountId == accountId)
        .Subscribe(response =>
        {
            taskCompletionSource.SetResult(response.Symbol.ToArray());

            disposable?.Dispose();
        });

    var requestMessage = new ProtoOASymbolsForConversionReq
    {
        CtidTraderAccountId = accountId,
        FirstAssetId = baseAssetId,
        LastAssetId = quoteAssetId
    };

    EnqueueMessage(requestMessage, ProtoOAPayloadType.ProtoOASymbolsForConversionReq, client);

    return taskCompletionSource.Task;
}
1
2
3
4
5
def sendProtoOASymbolsForConversionReq(accountId, firstAssetId, lastAssetId):
    request = ProtoOASymbolsForConversionReq()
    request.ctidTraderAccountId = accountId
    request.firstAssedId = firstAssetId
    request.lastAssetId = lastAssetId

Juga tambahkan syarat berikut kepada callback onMessageReceived.

1
2
3
elif message.payloadType == ProtoOASymbolsForConversionRes().payloadType:
    ProtoOASymbolsForConversionRes = Protobuf.extract(message)
    conversionSymbols = ProtoOASymbolsForConversionRes.symbol

Nota

Kami sangat mengesyorkan mendapatkan semua simbol rantai penukaran untuk setiap aset apabila anda pertama kali meminta dan menerima data aset daripada pelayan. Jika tidak, anda perlu menghantar mesej ProtoOASymbolsForConversionReq setiap kali anda perlu menukar kadar yang, dalam beberapa kes, mungkin beberapa kali sesaat.

Nota

Memandangkan entiti ProtoOALightSymbol tidak mengandungi sebarang medan yang mewakili harga bida atau tanya, kami sangat mengesyorkan mencipta kelas Symbol tersuai atau setara supaya anda boleh dengan mudah mencipta simbol baru secara pengaturcaraan dan mengemas kini sifatnya apabila acara tertentu dicetuskan. Anda perlu menukar simbol ringan kepada objek kelas ini secara berasingan.

Langgan dan urus acara spot

Untuk mengikuti rantai penukaran dari awal hingga akhir, anda perlu melanggan ProtoOASpotEvent untuk semua simbol yang termasuk dalam rantai. Ini dilakukan seperti berikut melalui SDK rasmi.

Sebelum anda melakukan operasi yang disenaraikan di bawah, cipta kelas Symbol atau setara supaya anda boleh dengan mudah mencipta simbol baru secara pengaturcaraan dan mengemas kini sifatnya apabila acara tertentu dicetuskan. Dalam contoh di bawah, kelas Symbol kami termasuk objek ProtoOASymbol dan ProtoOALightSymbol sebagai sifat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public Task<ProtoOASubscribeSpotsRes> SubscribeToSpots(long accountId, bool isLive, params long[] symbolIds)
{
    var client = GetClient(isLive);

    var taskCompletionSource = new TaskCompletionSource<ProtoOASubscribeSpotsRes>();

    IDisposable disposable = null;

    disposable = client.OfType<ProtoOASubscribeSpotsRes>().Where(response => response.CtidTraderAccountId == accountId).Subscribe(response =>
    {
        taskCompletionSource.SetResult(response);

        disposable?.Dispose();
    });

    var requestMessage = new ProtoOASubscribeSpotsReq
    {
        CtidTraderAccountId = accountId,
    };

    requestMessage.SymbolId.AddRange(symbolIds);

    EnqueueMessage(requestMessage, ProtoOAPayloadType.ProtoOaSubscribeSpotsReq, client);

    return taskCompletionSource.Task;
}

Kami juga perlu melanggan acara spot dan mengurusnya. Dalam contoh di bawah, kami mengakses koleksi symbols yang menyimpan simbol ringan kami. Kami mencari objek simbol yang IDnya sepadan dengan medan symbolId ProtoOASpotEvent yang baru kami terima. Akhirnya, kami mengemas kini sifat bid dan ask symbol menggunakan kaedah pembantu GetPriceFromRelative.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
client.OfType<ProtoOASpotEvent>().Subscribe(OnSpotEvent);

private void OnSpotEvent(ProtoOASpotEvent spotEvent) 
{
    var symbol = symbols.FirstOrDefault(iSymbol => iSymbol.Id == spotEvent.SymbolId);

    double bid;
    double ask;

    if (spotEvent.HasBid) bid = symbol.Data.GetPriceFromRelative((long)spotEvent.Bid);
    if (spotEvent.HasAsk) ask = symbol.Data.GetPriceFromRelative((long)spotEvent.Ask);

    if (bid != symbol.Bid) 
    {
        symbol.Bid = bid;
        RaisePropertyChanged(nameof(Bid));
    }

    if (ask != symbol.Ask) 
    {
        symbol.Ask = ask
        RaisePropertyChanged(nameof(Ask));
    }

}

Kami boleh menggunakan fungsi berikut untuk melanggan ProtoOASpotEvent untuk simbol tertentu. Perhatikan bahawa ia hanya melanggan satu simbol, bermakna anda perlu memanggil fungsi ini untuk setiap simbol ringan dalam koleksi simbol penukaran anda.

1
2
3
4
5
6
7
def sendProtoOASubscribeSpotsReq(symbolId, subscribeToSpotTimestamp = False, clientMsgId = None):
    request = ProtoOASubscribeSpotsReq()
    request.ctidTraderAccountId = currentAccountId
    request.symbolId.append(int(symbolId))
    request.subscribeToSpotTimestamp = subscribeToSpotTimestamp if type(subscribeToSpotTimestamp) is bool else bool(subscribeToSpotTimestamp)
    deferred = client.send(request, clientMsgId = clientMsgId)
    deferred.addErrback(onError)

Untuk mengurus acara spot, kami boleh menambah syarat berikut kepada callback onMessageReceived.

1
2
3
elif message.paloadType == ProtoOASpotEvent().payloadType:
    ProtoOASpotEvent = Protobuf.extract(message)
    onSpotEvent(ProtoOASpotEvent)

Akhir sekali, berikut adalah callback onSpotEvent kami.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def onSpotEvent(ProtoOASpotEvent):
    iterableConversionSymbols = iter(conversionSymbols)
    symbol = next(s for s in iterableConversionSymbols if s.symbolId == ProtoOASpotEvent.symbolId)

    if symbol is None:
        return

    if ProtoOASpotEvent.hasBid == true:
        bid = symbol.data.getPipsFromRelative()
    if ProtoOASpotEvent.hasAsk == true:
        ask = symbol.data.getPipsFromRelative()

    if bid != symbol.bid:
        symbol.bid = bid
    if ask != symbol.ask
        symbol.ask = ask

Dalam kelas Symbol kami, fungsi getPipsFromRelative ditakrifkan seperti berikut:

1
2
3
4
    import math

    def getPipsFromRelative(self, relative):
    return round((relative / 100000.0) / symbol.pipPosition, symbol.digits - symbol.pipPosition)

Perhatikan bahawa nilai sifat pipPosition dan digits perlu diperoleh secara berasingan.

Ikuti rantai penukaran dan kembalikan kadar penukaran akhir

Pada ketika ini, semua simbol dalam rantai penukaran anda sepatutnya menerima kadar spot dan anda sepatutnya boleh mengakses harga bida dan tanya yang tepat untuk setiap simbol. Apa yang tinggal hanyalah mengikuti rantai penukaran dan mengembalikan kadar akhir. Ini boleh dilakukan seperti berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private double GetConversionRate(List<Symbol> conversionSymbols) 
{
    double conversionRate = 1;

    var currentAsset = conversionSymbols.First().BaseAsset;

    foreach (var symbol in conversionSymbols)
    {
        var closePrice = symbol.Bid; //Replaced by symbol.Ask when calculating P&Ls for short positions

        if (symbol.BaseAsset == currentAsset)
        {
            conversionRate = conversionRate * closePrice;
            currentAsset = symbol.QuoteAsset;
        }
        else
        {
            conversionRate = conversionRate * 1 / closePrice;
            currentAsset = symbol.BaseAsset;
        }
    }

    return conversionRate;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def getConversionRate(conversionSymbols):
    conversionRate = 1
    currentAsset = conversionSymbols[0].baseAsset

    for symbol in conversionSymbols:
        closePrice = symbol.bid #Replaced by symbol.ask when calculating P&L for short positions

        if symbol.baseAsset == currentAsset:
            conversionRate = conversionRate * closePrice
            currentAsset = symbol.quoteAsset

        else:
            conversionRate = conversionRate * 1 / closePrice
            currentAsset = symbol.baseAsset