Ana içeriğe geç

Sembol kuru dönüşümü

Uygulamanızın P&L hesaplamalarıyla ilgili işlevler içermesini istiyorsanız sembol kuru dönüşümü temel bir işlemdir.

Kullanım senaryoları

Komisyon, swap ve temettü hesaplanırken sembol kuru dönüşümü yapılması gerekir. Komisyonlar, swaplar ve temettüler genellikle USD cinsinden ifade edilir. Ancak brokerınıza ve tercihlerinize bağlı olarak hesabınızın mevduat para birimi USD dışında başka bir varlık olabilir. Komisyonları, swapları ve temettüleri doğru şekilde belirlemek için USD ile hesabınızın mevduat para birimi arasındaki döviz kurunu bilmeniz gerekir.

Dönüşüm zincirleri

Her bir sembolün fiyatını, temel varlığı ile kotasyon varlığı arasındaki dönüşüm kuru olarak düşünün. Belirli bir varlığı başka bir varlığa doğrudan bağlayan semboller yoksa dönüşüm kurlarını alma işlemi daha karmaşık hâle gelir. Neyse ki cTrader arka ucu, bir varlıktan diğerine mümkün olan en kısa dönüşüm yolları olarak çalışan dönüşüm zincirlerini otomatik olarak oluşturabilir.

Örneğin EUR'yu NZD'ye dönüştürmek istiyorsanız ve alım satım sunucusunda EURNZD sembolü yoksa cTrader, dönüşüm sürecinde ara adımlar olarak kullanılacak bir zincir önerir. Sembol kullanılabilirliğine bağlı olarak bu zincir EURUSD-USDCAD-CADNZD, EURCFH-CFHUSD-USDNZD veya hatta EURCAD-CADUSD-USDCFH-CFHNZD şeklinde olabilir.

İki varlık arasında dönüşüm yapmak için uygulamanızın aşağıdaki işlemleri gerçekleştirmesi gerekir:

  • Aralarında dönüşüm yapmak istediğiniz varlıkların kimliklerine erişin.
  • Bir veya daha fazla hafif sembol içeren bir dönüşüm zinciri alın.
  • Spot olaylarına abone olun ve bunları işleyin.
  • Dönüşüm zincirini izleyin ve nihai dönüşüm kurunu döndürün.

Aşağıda bu işlemlerin her birini ayrıntılı olarak açıklıyoruz.

Varlık kimliklerine erişin

Varlık kimliklerine, hafif sembol varlığının (ProtoOALightSymbol model mesajı ile temsil edilir) özellikleri üzerinden doğrudan erişilebilir. Ayrıca bir hesabın mevduat para biriminin kimliğini ilgili ProtoOaTrader varlığından doğrudan alabilirsiniz.

Aşağıdaki örneğin yalnızca bir sembol için varlık kimliklerini aldığını unutmayın. Aynı işlemi birden fazla sembol için gerçekleştirmek üzere bir döngü eklemeniz veya başka uygun bir çözüm (örneğin map() yöntemi ya da eşdeğeri) kullanmanız gerekir.

JSON ile çalışma

JSON ile çalışırken aşağıdaki kodu ve bu eğitimdeki diğer kod parçacıklarını yine kullanabilirsiniz. Ancak Proto... sınıflarının adlarını kendi özel sınıflarınızın adlarıyla değiştirdiğinizden ve tercih ettiğiniz TCP ve WebSocket istemcisine göre gerekli değişiklikleri yaptığınızdan emin olun.

symbol, ProtoOALightSymbol türünde bir değişkendir. JSON kullanıyorsanız ProtoOALightSymbol mesajını temsil eden herhangi bir özel tür olabilir.

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

Aşağıdaki örnekte ProtoOATrader türündeki account nesnesinin mevduat para birimi kimliğini alıyoruz.

1
int depositAssetId = account.DepositAssetId;

symbol, ProtoOALightSymbol türünde bir mesajı temsil eden değişkendir.

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

Aşağıdaki örnekte ProtoOATrader model mesajını temsil eden account nesnesinin mevduat para birimi kimliğini alıyoruz.

1
depositAssetId = account.depositAssetId

Dönüşüm zinciri alın

Geçerli bir dönüşüm zinciri almak için uygulamanızın, firstAssetId ve lastAssetId alanları dönüşüm yapmak istediğiniz varlıkların kimliklerine ayarlanmış ProtoOASymbolsForConversionReq mesajını göndermesi gerekir. ProtoOASymbolsForConversionRes türünde bir yanıt aldığınızda tekrarlanan symbol alanını bir koleksiyonda saklayın.

Bu işlemi resmi SDK'lar aracılığıyla şu şekilde gerçekleştirebilirsiniz.

 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

Aşağıdaki koşulu onMessageReceived geri çağırmasına da ekleyin.

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

Not

Sunucudan varlık verilerini ilk kez talep edip aldığınızda her varlık için tüm dönüşüm zinciri sembollerini almanızı önemle tavsiye ederiz. Aksi takdirde kurları her dönüştürmeniz gerektiğinde ProtoOASymbolsForConversionReq mesajını göndermeniz gerekir; bu da bazı durumlarda saniyede birkaç kez olabilir.

Not

ProtoOALightSymbol varlıkları satış veya alış fiyatlarını temsil eden alanlar içermediğinden, program aracılığıyla kolayca yeni semboller oluşturabilmek ve belirli olaylar tetiklendiğinde özelliklerini güncelleyebilmek için özel Symbol sınıfları veya eşdeğerlerini oluşturmanızı önemle tavsiye ederiz. Hafif sembolleri ayrıca bu sınıfın nesnelerine dönüştürmeniz gerekir.

Spot olaylarına abone olun ve bunları işleyin

Dönüşüm zincirini baştan sona izlemek için zincire dâhil olan tüm semboller için ProtoOASpotEvent olayına abone olmanız gerekir. Bu işlem resmi SDK'lar aracılığıyla aşağıdaki şekilde yapılır.

Aşağıda listelenen işlemleri gerçekleştirmeden önce, program aracılığıyla kolayca yeni semboller oluşturabilmek ve belirli olaylar tetiklendiğinde özelliklerini güncelleyebilmek için bir Symbol sınıfı veya eşdeğerini oluşturun. Aşağıdaki örneklerde Symbol sınıfımız, özellik olarak ProtoOASymbol ve ProtoOALightSymbol nesnelerini içerir.

 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;
}

Spot olaylarına da abone olup bunları işlememiz gerekir. Aşağıdaki örnekte hafif sembollerimizi saklayan symbols koleksiyonuna erişiyoruz. Kimliği, az önce aldığımız ProtoOASpotEvent olayının symbolId alanıyla eşleşen sembol nesnesini buluyoruz. Son olarak GetPriceFromRelative yardımcı yöntemini kullanarak symbol nesnesinin bid ve ask özelliklerini güncelliyoruz.

 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));
    }

}

Belirli bir sembol için ProtoOASpotEvent olayına abone olmak üzere aşağıdaki işlevi kullanabiliriz. Bu işlev yalnızca bir sembole abone olur; dolayısıyla dönüşüm sembolleri koleksiyonunuzdaki her hafif sembol için işlevi çağırmanız gerekir.

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)

Spot olaylarını işlemek için onMessageReceived geri çağırmamıza aşağıdaki koşulları ekleyebiliriz.

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

Son olarak onSpotEvent geri çağırmamız aşağıdaki gibidir.

 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

Symbol sınıfımızda getPipsFromRelative işlevi aşağıdaki şekilde tanımlanmıştır:

1
2
3
4
    import math

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

pipPosition ve digits özelliklerinin değerlerinin ayrıca alınması gerektiğini unutmayın.

Dönüşüm zincirini izleyin ve nihai dönüşüm kurunu döndürün

Bu aşamada dönüşüm zincirinizdeki tüm semboller spot kurları alıyor olmalı ve her sembol için doğru satış ve alış fiyatlarına erişebilmelisiniz. Geriye yalnızca dönüşüm zincirini takip edip nihai kuru döndürmek kalır. Bu işlem aşağıdaki gibi yapılabilir:

 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