You need to enable JavaScript in order to visit our site!
 
Logo PTMC
NAVIGATION
CANCEL
 
or via PTMC account
Restore password
or via PTMC account
Logo PTMC
NAVIGATION
or via PTMC account
Restore password
or via PTMC account
or via PTMC account
Restore password
or via PTMC account
Ray Ray 17.04 2017

串接經紀商。如何使用 PTMC 的整合 API 來串接經紀商。

歡迎大家自行進行 API 整合,並願意的話,在這邊分享你的程式碼。如果需要,歡迎隨時聯繫我們,或寫信到 service@kcdatanet.com

歡迎各位使用 PTMC!

我們很高興向大家介紹 PTMC 的 Integration API。透過 Integration API,用戶可以將 PTMC 平台與任何券商 API 或是報價源進行整合。 要使用此 API,用戶必需清楚瞭解交易中的報價/下單概念,並且需要一定的 C#語言的基本實作能力。目前發佈的 Integration API 是 V1 版本,在未來,我們將參考用戶的需求及回饋,不斷的加以擴充及改善。所以如果各位有什麼想法,歡迎在這邊發表或是論壇中反應。

從技術角度來說,報價及下單整合必需實作 API 裡面的方法 (Method),這樣才能將 PTMC 平台與經紀商的報價/下單及報價源彼此串連。這樣才能更好的將用戶在券商中的帳戶資料、委託記錄、商品報價等完美的整合到 PTMC 平台之中。用戶可以在您自行開發的整合 API 中結合任何外部 API,如 REST,FIX,任何特定的專屬協議。基本上沒有限制,一切取決於您自己的偏好及經紀商 API 與報價源所提供的規範或限制。

在這邊,我們將透過 Oanda 的新 v20 REST API 的整合範例來展示如何使用 Integration API 來做整合。關於 Oanda API 的訊息可以在其官方網站找到。本文的主要重點將放在 Integration API 上,但是如果有興趣了解 PTMC 和 Oanda API 間的整合細節,請參閱 GitHub 中的原始碼。

準備工作

首先,執行下列步驟:

  • 在 Visual Studio 中建立一個動態程式庫 (DLL) 專案
  • 將位於 PTMC 安裝路徑中的 PlatformAPI.dll 加入專案的「參考 (Reference)」之中
  • 建立一個從 Vendor 類別中繼承的經紀商類別,並準備覆寫類別中的 Method

要測試整合,必需將你建立的程式庫,及相依的檔案 (DLL 或其它必需資料) 複製到 PTMC 的文件夾中。之後,它將會在 “連接設定” 視窗中的經紀商列表中出現,並可供使用。

現在一切準備就緒。讓我們開始吧!

連結

任何 API 整合始於如何初始化經紀商 API 並連接其系統。Intergration API 內的 Connect 方法的目的就在於此。用戶必需實做此方法才能將 PTMC 與經紀商或報價商的 API 串連。有些時候可能還需要其它設定,像是帳戶類型為模擬或是真實交易帳戶。這些設定位於 PTMC 登入視窗的“連接設定”視窗中:



GetConnectionParameters 方法定義了 API 登入時所需要的必需設定參數。在 Oanda 的範例中,我們提供了選擇連接類型選項:

public override List<SettingItem> GetConnectionParameters()
{
    List<SettingItem> parameters = new List<SettingItem>();
    // Demo/Real connection type
    var settingItem = new SettingItemComboBox(CONNECTION, CONNECTION_DEMO, new List<string> { CONNECTION_DEMO, CONNECTION_REAL });
    parameters.Add(settingItem);
    return parameters;
}

此方法會將結果儲存到 SettingItem 陣列物件,每個物件都定義了設定中的一個類型,名稱和數值。

在這邊,我們來實做一個連接方法。在前面的 GetConnectionParameters 方法中定義的值會做為傳入參數,因此在這邊會有一組相同的設定值:

public override ConnectionResult Connect(List<SettingItem> parameters)
{
    ConnectionResult result = new ConnectionResult();
    bool isLive = false;                        
    string user = string.Empty;
    string password = string.Empty;
    //
    // Check passed parameters
    //
    SettingItem settingItem = parameters.GetItemByName(Vendor.LOGIN_PARAMETER_USER);
    if (settingItem != null && settingItem.Value is string)
        user = (string)settingItem.Value;
    settingItem = parameters.GetItemByName(Vendor.LOGIN_PARAMETER_PASSWORD);
    if (settingItem != null && settingItem.Value is string)
        password = (string)settingItem.Value;
    settingItem = parameters.GetItemByName(CONNECTION);
    if (settingItem != null && settingItem.Value is string)
        isLive = (string)settingItem.Value == CONNECTION_REAL;
    string returnedToken = password.Length > 20 ? password : string.Empty;
    if (string.IsNullOrEmpty(returnedToken))
    {
        result.State = ConnectionState.Fail;
        result.Message = "User/password combination is not valid.";
        return result;
    }
    //
    // Connect to Oanda via passed parameters
    //
    Credentials.SetCredentials(isLive ? EEnvironment.Trade : EEnvironment.Practice, returnedToken);
    result.State = ConnectionState.Success;
    return result;
}

帳戶及規則

帳戶是主要索引物件之一,它確保 PTMC 裡面顯示的資訊是正確的。因此我們下一步就是覆寫 GetAccounts 方法。這樣我們可以確保會傳回的是登入帳號的確實訊息。而用戶也可以帳戶來讀取交易報表,成交及委託記錄及其它相關訊息等資訊:
public override IList<Account> GetAccounts()
{
    List<Account> accounts = new List<Account>();
    //
    // Get accounts from Oanda an API
    //
    List<AccountOanda> accountsOanda = Rest.GetAccountListAsync(); ;   
    if (accountsOanda.Count == 0)
        throw new Exception("The Oanda v20 only has access to the v20 accounts. Please, contact customer service to setup a sub-account that can be used with this platform.");
    for (int i = 0; i < accountsOanda.Count; i++)
    {
        //
        // Get detailed info
        //
        AccountSummaryOanda accountSummary = Rest.GetAccountSummary(accountsOanda[i].Id);
        if (accountSummary == null)
            continue;                
        this.accountsSummaryOanda.Add(accountSummary);
        //
        // Create accounts and fill with available data
        //
        Account account = new Account();
        account.AccountId = accountSummary.Id;
        account.AccountName = accountSummary.Name;
        account.Currency = accountSummary.Currency;                
        account.marginWarningLevelPercent = accountSummary.MarginRate;
        account.Balance = accountSummary.Balance;
        account.RealizedPnl = accountSummary.Pl;
        account.UsedMargin = accountSummary.MarginUsed;
        account.AvailableMargin = accountSummary.MarginAvaliable;
        account.MaintranceMargin = accountSummary.MarginUsed;//??
        OandaV20Utils.FillAccountAditionalInfo(account, null);
        accounts.Add(account);                
        positions[accountSummary.Id] = new ConcurrentDictionary<string, Position>();
    }
    return accounts;
}


PTMC 平台支援相當多功能,以確保能夠好好的處理,如期貨,期權,股票,外匯等不同商品交易的需求。如果整合過程中,經紀商的 API 不支援像是 Level 2 報價或是 Tick 資料的話,則可以將這些用不到的功能隱藏起來。要達成此目的,您可以此使用 GetRulesTable 方法:

public override Dictionary<Rule, object> GetRulesTable(Account account)
{
    Dictionary<Rule, object> ruleSet = new Dictionary<Rule, object>();
    ruleSet[Rule.FUNCTION_OE2014] = Vendor.NOT_ALLOWED;
    ruleSet[Rule.FUNCTION_CHART] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_CHAT] = Vendor.NOT_ALLOWED;
    ruleSet[Rule.FUNCTION_NEWS] = Vendor.NOT_ALLOWED;
    ruleSet[Rule.FUNCTION_SHOW_ORDERS] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_SHOW_POSITIONS] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_ACCOUNTS] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_ACCOUNTPERFOMANCE] = Vendor.NOT_ALLOWED;
    ruleSet[Rule.FUNCTION_WATCHLIST] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_FXBOARD] = Vendor.ALLOWED;
    ruleSet[Rule.FUNCTION_LEVEL3] = Vendor.NOT_ALLOWED;
    ruleSet[Rule.FUNCTION_MATRIX] = Vendor.ALLOWED;          
    return ruleSet;
}

交易商品

這些交易物件,像是查詢歷史報價,進行交易,讀取報價等都是交易者一定會用到的。您可以在 GetInstruments 方法中指定連接時取得可交易的所有商品,以它們的相關資訊。此機制類似於讀取帳戶資訊。 實際上,絕大部分 API 的工作都是遵循這個原則:終端程式向經紀商系統發送各種資訊請求,而經紀商系統必需回應及傳回所請求的資料。

public override IList<Instrument> GetInstruments()
{    
    //
    // Get instruments from Oanda an API
    //
    List<InstrumentOanda> instrumentsOanda = Rest.GetAllInstrumentsAsync(accountsSummaryOanda[0].Id);
    List<Instrument> instruments = new List<Instrument>();
    foreach (InstrumentOanda instrumentOanda in instrumentsOanda)
    {
        instrumentOanda.Name = instrumentOanda.Name.ToUpper();
        this.instruments[instrumentOanda.Id] = instrumentOanda;
        //
        // Create instruments and fill with available data
        //
        Instrument instrument = new Instrument();
        instrument.Id = instrumentOanda.Id;
        instrument.Name = instrumentOanda.Name.ToUpper();
        instrument.InstrumentType = OandaV20Utils.GetInstrumentType(instrumentOanda.InstrumentType);
        instrument.InstrumentGroup = OandaV20Utils.GetInstrumentGroup(instrument.Name);
        instrument.DefaultHistoryType = HistoryDataTypes.Bid;
        instrument.LotSize = instrumentOanda.MinimumTradeSize;
        instrument.PointSize = Math.Pow(0.1, instrumentOanda.DisplayPrecision);
        instrument.Precision = (byte)instrumentOanda.DisplayPrecision;
        instrument.MaxLot = instrumentOanda.MaximumOrderUnits;
        OandaV20Utils.ExtractExp1Exp2(instrumentOanda.Id, out instrument.Exp1, out instrument.Exp2);
        instruments.Add(instrument);
    }
    return instruments;
}

訂閱報價

將商品新增到面板(如 “自選清單” 或“市場深度”)後,用戶預期會看到商品的報價。因此,必需實做 SubscribeSymbol UnSubscribeSymbol 方法以便察看商品報價。做為傳入參數,我們會取得一個商品名稱來訂閱與它相關的報價類型:Level 1/2、Ask/Bid 或 Trade 等資料。Oanda v20 僅提供 Level 1 和 Level 2:
public override bool SubscribeSymbol(SubscribeQuotesParameters parameters)
{
    //
    // Level1 subscription cache
    //
    if (parameters.subscribeType == SubscribeQuoteType.Level1)
    {
        if (!subscribedLevel1Instruments.Contains(parameters.symbol))                
            subscribedLevel1Instruments.Add(parameters.symbol);                                
        return true;
    }
    //
    // Level2 subscription cache
    //
    if (parameters.subscribeType == SubscribeQuoteType.Level2)
    {
        if (!subscribedLevel2Instruments.Contains(parameters.symbol))                
            subscribedLevel2Instruments.Add(parameters.symbol);                
        return true;
    }
    return false;
}

訂單、倉位、交易

訂單、倉位、交易這三個資料是一個交易平台必備的標準資訊。這就是為什麼必需實做賬戶和商品實體,然後加入訂單、倉位、交易維護。 透過 GetPendingOrdersGetPositionsGetTradesHistory 允許交易者查看相應面板中的對應訊息。平台在成功連接後會立即呼叫這些方法和資料:

public override IList<Position> GetPositions()
{
    List<Position> positions = new List<Position>();
    foreach (AccountSummaryOanda account in accountsSummaryOanda)
    {
        //
        // Get positions from Oanda an API
        //
        List<TradeOanda> openTrades = Rest.GetOpenTrades(account.Id);
        foreach (TradeOanda tradeOanda in openTrades)
        {
            //
            // Create positions and fill with available data
            //
            Position position = new Position();
            InstrumentOanda inst;
            if (!instruments.TryGetValue(tradeOanda.InstrumentId, out inst))
                continue;                    
            position.AccountId = account.Id;
            position.PositionId = tradeOanda.Id;
            position.Symbol = inst.Name.ToUpper();
            position.OpenPrice = tradeOanda.Price;
            position.Quantity = Math.Abs(tradeOanda.CurrentUnits);
            position.Side = OandaV20Utils.GetSide(tradeOanda.CurrentUnits);
            position.OpenTime = tradeOanda.OpenTime;
            position.ServerCalculationNetPL = position.ServerCalculationGrossPL = tradeOanda.UnrealizedPL;
            positions.Add(position);            
        }
    }
    return positions;
}

此外,還有一組方法用來回報給 PTMC 關於用戶的投資組合變化。這些變動可以是開倉,取消或修改訂單等。這些方法包括 OnOrderUpdatedOnOrderCancelledOnPositionUpdatedOnPositionClosed

載入歷史資料

現在我們來到了圖表及載入歷史資料。LoadTickHistory LoadBarHistory 定義了如何,及從什麼地方載入 tick 和 bar 的歷史資料的主要方法。Oanda 不提供 tick 歷史記錄,所以我們只使用 LoadBarHistory 方法。我們將 HistoryRequestParameters 物件做為傳入參數,來指定用戶要求那個商品的歷史資料,時間週期和範圍。這樣就可以傳回回一組要在圖表上顯示線圖陣列:
public override IList<BarHistoryItem> LoadBarHistory(HistoryRequestParameters requestParaeters, CancellationToken token)
{
    DateTime fromTime = requestParaeters.FromTime;
    DateTime toTime = requestParaeters.ToTime;
    List<BarHistoryItem> res = null;
    //
    // Convert period to Oanda format        
    //
    GranularityEnum? timeframe = OandaV20Utils.GetOandaTimeframe(requestParaeters.Period);
    InstrumentOanda inst = instruments.Where(x => x.Value.Name.ToUpper() == requestParaeters.Symbol).FirstOrDefault().Value;
    //
    // Prepare request and send to Oanda an API
    //                              
    CandlesRequest req = new CandlesRequest
    {
        InstrumentId = inst.Id,
        price = OandaV20Utils.GetOandaHistoryPrice(requestParaeters.HistoryType),
        granularity = timeframe,
        to = toTime
    };
    List<Candle> response = Rest.GetCandles(req);
    //
    // Process responce and create BarHistoryItem items
    //
    foreach (Candle candle in response)
    {
        long ticks = candle.Time.Ticks;
        if (requestParaeters.HistoryType == HistoryDataTypes.Ask)
            res.Add(new BarHistoryItem() { LeftHistoryTime = candle.Time, Open = candle.Ask.Open, Close = candle.Ask.Close, High = candle.Ask.High, Low = candle.Ask.Low, Volume = candle.Volume });
        else if (requestParaeters.HistoryType == HistoryDataTypes.Bid)
            res.Add(new BarHistoryItem() { LeftHistoryTime = candle.Time, Open = candle.Bid.Open, Close = candle.Bid.Close, High = candle.Bid.High, Low = candle.Bid.Low, Volume = candle.Volume });
    }
    return res;
}

歷史資料範圍區間,類型和週期取決於特定的整合方式。PTMC 平台支持所有基本類型,但如果必要的話,也透過使用 GetHistoryMetadata 方法來刪除無法存取訪問的資料型態:

public override HistoryMetadata GetHistoryMetadata()
{
    return new HistoryMetadata()
    {
        //
        // Supported history types
        //
        HistoryTypes = new HistoryDataTypes[]
        {
            HistoryDataTypes.Bid,
            HistoryDataTypes.Ask,
            HistoryDataTypes.BidAskAverage
        },
        //
        // Supported periods
        //
        Periods = new int[]
        {                  
            (int)HistoryPeriod.Minute,            
            (int)HistoryPeriod.Hour,            
            (int)HistoryPeriod.Day,
            (int)HistoryPeriod.Week,
            (int)HistoryPeriod.Month,
        }
    };
}

交易操作

PTMC 的 Integration API 提供了一個交易介面,可將平台與任何數據源及經紀商 API 進行整合。在實做交易介面功能後,則那些提供交易功能的面板,如訂單輸入面板,圖表中的視覺交易,訂單取消或修改將即可正常運作。交易介面中,其中一個主要方法就是 PlaceOrder 方法。只要正確處理 OrderParameters 物件中所傳入的參數,就可向經紀商發送訂單請求:

public override TradingOperationResult PlaceOrder(OrderParameters orderData)
{
    var result = new TradingOperationResult();
    //
    // Prepare request
    //            
    var orderRequest = new PlaceOrderRequest();
    OandaV20Utils.CreateOrderRequest(orderData, orderRequest);
    //
    // Send request to Oanda server
    //
    Rest.PostOrder(orderData.Account.AccountId, orderRequest);
    return result;
}

而開啟/修改訂單和關閉倉位則可使用:ModifyOrderCancelOrderClosePosition

報告

Intergration API 可提供了多種報告。允許用戶在 PTMC 平台中隨時查看。這一系列報告定義其所需參數,像是帳戶或是報告週期等。要取得報告需使用 GetReportsMetaData 方法:

public override IList<ReportMetaData> GetReportsMetaData()
{
    List<ReportMetaData> result = new List<ReportMetaData>();
    //
    // Oanda provides "Transaction history report" which allow us to filter data by time range and account
    //
    ReportMetaData reportType = new ReportMetaData((int)ReportTypeEnum.TransactionHistory, "Transaction history report");
    reportType.Parameters.Add(new SettingItemAccountLookup(Vendor.REPORT_TYPE_PARAMETER_ACCOUNT));
    reportType.Parameters.Add(new SettingItemDateTimePicker(Vendor.REPORT_TYPE_PARAMETER_DATETIME_FROM));
    reportType.Parameters.Add(new SettingItemDateTimePicker(Vendor.REPORT_TYPE_PARAMETER_DATETIME_TO));
    result.Add(reportType);
    return result;
}

接下來,定義特定報表的產生機制。做為參數,您將取得用戶設定的報告名稱和參數。這會讓 Report 物件以表格的方式傳回每個欄位內的數值:

public override Report GenerateReport(ReportMetaData reportType)
{
    //
    // Check passed parameters
    //    
    string account = reportType.Parameters.Single(p => p.Name == Vendor.REPORT_TYPE_PARAMETER_ACCOUNT).Value.ToString();
    account = accountsSummaryOanda.Single(a => a.Name == account).Id;
    DateTime from = (DateTime)reportType.Parameters.Single(p => p.Name == Vendor.REPORT_TYPE_PARAMETER_DATETIME_FROM).Value;
    DateTime to = (DateTime)reportType.Parameters.Single(p => p.Name == Vendor.REPORT_TYPE_PARAMETER_DATETIME_TO).Value;
    //
    // Generate "Transaction history report"
    //
    if ((ReportTypeEnum)reportType.Id == ReportTypeEnum.TransactionHistory)
    {
        //
        // Get data from Oanda an API
        //
        List<Transaction> transactions = new List<Transaction>();
        var transactionsPage = Rest.GetTransactionsPage(account, from, to);
        foreach (var page in transactionsPage.Pages)
            transactions.AddRange(Rest.GetTransactions(page));
        //
        // Specify report columns
        //
        var report = new Report();
        report.AddColumn("Time", AdditionalInfoItemComparingType.DateTime);
        report.AddColumn("Type", AdditionalInfoItemComparingType.String);
        report.AddColumn("Order id", AdditionalInfoItemComparingType.String);
        report.AddColumn("Balance", AdditionalInfoItemComparingType.Double);
        report.AddColumn("Financing", AdditionalInfoItemComparingType.Double);
        report.AddColumn("Account id", AdditionalInfoItemComparingType.String);
        //
        // Fill reports cells with received data
        //
        foreach (var transaction in transactions)
        {
            var reportRow = new ReportRow();
            reportRow.AddCell(transaction.Time);
            reportRow.AddCell(transaction.TransactionType.ToString());
            reportRow.AddCell(transaction.OrderID);
            reportRow.AddCell(transaction.AccountBalance);
            reportRow.AddCell(transaction.Financing);
            reportRow.AddCell(transaction.AccountID);
            report.Rows.Add(reportRow);
        }
        return report;
    }
    else
        return null;    
}

結論

在這篇文章裡,我們介紹了關於如何將 PTMC 平台與經紀商 API 整合的一些方式。有一些額外的方法,因為它們很簡單,而且工作方式類似,因此沒有多加說明。同時,因為 Oanda 是外匯經紀商,所以我們也沒有觸及其它商品的特殊需求。像是成交報價,或是依照類型來搜尋商品,及查詢選擇權等部分。接下來我們會實做 Metastock QuoteMedia 的 API 整合,並以其中一個為範例來涵蓋 Integration API 的其它特性。

歡迎大家自行進行 API 整合,並願意的話,在這邊分享你的程式碼。如果需要,歡迎隨時聯繫我們,或寫信到 service@kcdatanet.com

Discussion
Join PTMC community to post your comments
No comments yet. Be the first.
PTMC 是一個專業的交易平台,結合了強大的圖表和分析工具,並可在不同的金融市場進行交易。 它是由 PFSOFT UK LTD 開發的,該公司是全球銀行和經紀商交易技術提供商
© 2024. PTMC 為基於 Protrader 技術的專業交易平台
地址
臺北市大安區羅斯福路3段273號5樓
聯絡我們
電話: +886-2-2367-8583
E-mail: service@kcdatanet.com
社群
© 2024. PTMC 為基於 Protrader 技術的專業交易平台