シンボルレートの変換

API

Symbol rate conversionは、P&L計算に関連するあらゆる機能を含めるために、アプリケーションに組み込む必要がある重要なアクションです。

使用例

手数料、スワップ、および配当を計算する際には、通常、USDで表されます。ただし、ブローカーや個人の設定によっては、口座の入金通貨がUSD以外の資産である場合があります。手数料、スワップ、および配当を正確に定義するには、USDと口座の入金通貨の間の為替レートを知る必要があります。

変換チェーン

各個別のシンボルの価格を、そのベースアセットとクォートアセットとの間の変換レートと考えてください。特定の資産を別の資産に直接リンクするシンボルがない場合、変換レートを取得するタスクは複雑になります。幸いなことに、cTraderバックエンドは、1つの資産から別の資産への最短の変換パスとして機能する変換チェーンを自動的に作成できます。

たとえば、EURをNZDに変換したい場合、トレーディングサーバーにEURNZDシンボルがない場合、cTraderは、変換プロセスの中間段階として機能するチェーンを提案します。シンボルの可用性に応じて、そのようなチェーンは次のようになります:EURUSD-USDCAD-CADNZDEURCFH-CFHUSD-USDNZD、またはEURCAD-CADUSD-USDCFH-CFHNZD

2つの資産間を変換するには、アプリケーションは次のアクションを実行する必要があります。

  • 変換したい資産のIDにアクセスする。
  • 1つ以上の「ライト」シンボルを含む変換チェーンを取得する。
  • スポットイベントを購読して処理する。
  • 変換チェーンに従って最終的な変換レートを返す。

以下では、これらのアクションを詳しく説明します。

アセットIDへのアクセス¶

アセットIDは、「ライト」シンボルエンティティのプロパティとして直接アクセスできます(ProtoOALightSymbolモデルメッセージで表されます)。また、関連するProtoOaTraderエンティティから口座の入金通貨のIDを直接取得することもできます。

以下の例では、1つのシンボルのアセットIDのみを取得しています。複数のシンボルに対して同じ操作を実行するには、ループを追加するか、他の適切な解決策(例:map()メソッドまたは類似)を使用する必要があります。

JSONでの作業

JSONで作業する場合、以下のコードやこのチュートリアルの他のコードスニペットを再利用できます。ただし、Proto...クラスの名前をカスタムクラスの名前に置き換え、好みのTCP/WebSocketクライアントに対応するように必要な修正を行ってください。

symbolProtoOALightSymbolタイプの変数です。JSONを使用している場合、それはProtoOALightSymbolメッセージを表す任意のカスタムタイプです。

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

以下の例では、accountオブジェクトの預入通貨のIDを取得します。このaccountオブジェクトはProtoOATraderタイプです。

				
					int depositAssetId = account.DepositAssetId;

				
			

symbolProtoOALightSymbol タイプのメッセージを表す変数です。

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

以下の例では、account オブジェクトから預金通貨の ID を取得します。この account オブジェクトは ProtoOATrader モデルメッセージを表します。

				
					depositAssetId = account.depositAssetId
				
			

変換チェーンの取得

有効な変換チェーンを取得するには、アプリケーションは、firstAssetIdlastAssetId フィールドを対象の資産の ID に設定して、ProtoOASymbolsForConversionReq メッセージ を送信する必要があります。 ProtoOASymbolsForConversionRes タイプの応答を受信したら、その中の symbol フィールドをコレクションに格納します。

以下は、公式 SDK を使用してこのアクションを実行する方法です。

				
					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;
}
				
			
				
					def sendProtoOASymbolsForConversionReq(accountId, firstAssetId, lastAssetId):
    request = ProtoOASymbolsForConversionReq()
    request.ctidTraderAccountId = accountId
    request.firstAssedId = firstAssetId
    request.lastAssetId = lastAssetId
				
			

onMessageReceived コールバックに以下の条件を追加してください。

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

Note

サーバーからアセットデータを最初に要求して受信した際に、各アセットのすべての変換チェーンシンボルを取得することを強くお勧めします。そうしないと、レート変換が必要なたびにProtoOASymbolsForConversionReqメッセージを送信する必要があります。これは場合によっては1秒に数回行われることがあります。

Note

ProtoOALightSymbolのエンティティには入札価格や要求価格を表すフィールドが含まれていないため、カスタムのSymbolクラスまたは同等のクラスを作成し、トリガーされた特定のイベントでプロパティを簡単に更新できるようにすることを強くお勧めします。ライトシンボルをこのクラスのオブジェクトに変換する必要があります。

Spot イベントの購読と処理

開始から終了までの変換チェーンをフォローするには、チェーンに含まれるすべてのシンボルに対してProtoOASpotEventを購読する必要があります。公式の SDK を使用して、これは次のように行います。

以下の操作を実行する前に、特定のイベントが発生したときに簡単に新しいシンボルをプログラムで作成し、そのプロパティを更新できるように、Symbolクラスまたはそれに相当するものを作成してください。以下の例では、SymbolクラスにProtoOASymbolおよびProtoOALightSymbolオブジェクトをプロパティとして含めています。

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

また、スポットイベントにも登録して、それを処理する必要があります。以下の例では、ライトシンボルを格納するsymbolsコレクションにアクセスします。受信したProtoOASpotEventsymbolIdフィールドと一致するシンボルオブジェクトを見つけます。最後に、GetPriceFromRelativeヘルパーメソッドを使用して、symbolbidおよびaskプロパティを更新します。

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

}
				
			

特定のシンボルにProtoOASpotEventを購読するために、次の関数を使用できます。ただし、これは1つのシンボルにのみ購読するため、変換シンボルのコレクション内のすべてのライトシンボルに対してこの関数を呼び出す必要があります。

				
					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)
				
			

スポットイベントを処理するために、以下の条件をonMessageReceivedコールバックに追加できます。

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

最後に、以下がonSpotEventコールバックです。

				
					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クラスでは、getPipsFromRelative関数が次のように定義されています。

import math

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

pipPositionおよびdigitsプロパティの値は、個別に取得する必要があることに注意してください。

変換チェーンに従って最終的な変換レートを返す

この時点で、変換チェーン内のすべてのシンボルがスポットレートを受信し、各シンボルの正確な売値と買値にアクセスできるはずです。残されたことは、変換チェーンを ‘移動’ して最終的なレートを返すだけです。これは以下のように行うことができます。

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

このページについて