2017-05-26 40 views
4

我有一個小程序,我用於算法股票交易。代碼必須在我的8核桌面機器上循環大約192萬億次。我想過租用一臺64核心機器來運行它,但它不符合成本效益。加快此代碼 - ()循環192萬億

這只是這一點的代碼。但for循環必須在每個要計算的欄上循環(大約180萬),然後循環查看匹配項的列表大約是800k項。

我現在想辦法加快它的唯一方法是刪除匹配的項目,因爲它只發生一次(DateTime)。

其他人有沒有辦法讓代碼更快一些?它讓我的桌面野獸大約45個小時運行一個程序迭代。

基本上我正在做的是在每個欄上計算,查看當前小節日期時間是否與我手動創建的CSV文件中的DateTime匹配。然後從列表對象中,我抓住交易方向並設置一個布爾值來表示一個位置。

using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Linq; 
using PowerLanguage.Function; 
using ATCenterProxy.interop; 
using System.IO; 
using System.IO.Compression; 

namespace PowerLanguage.Strategy 
{ 
    public class Ecal_v1 : SignalObject 
    { 
     public List<Trades> tradeList = new List<Trades>(); 
     public List<string> csvList = new List<string>(); 
     public bool exitOn24 = false; 
     public string ecalPath = @"C:\Users\Skynet\OneDrive\Trading\Economic Calendars\backtest1.csv"; 
     PowerLanguage.Indicator.Bollinger_Bands bb; 

     public Ecal_v1(object _ctx):base(_ctx){} 

     //[Input] 
     //public bool exitOn24 { get; set; } 

     [Input] 
     public double bbTopOffset { get; set; } 
     775 
     [Input] 
     public double bbBotOffset { get; set; } 

     [Input] 
     public double longTPMod { get; set; } 

     [Input] 
     public double shortTPMod { get; set; } 

     [Input] 
     public double longSLMod { get; set; } 

     [Input] 
     public double shortSLMod { get; set; } 

     //[Input] 
     //public double buyTrail { get; set; } 

     //[Input] 
     //public double sellTrail { get; set; } 

     double bbUpperDiff; 
     double bbLowerDiff; 
     double bbBasis; 
     double longTP; 
     double shortTP; 
     double longSL; 
     double shortSL; 
     double ptValue; 
     public DateTime tradeTime; 

     private IOrderMarket longEntry, shortEntry, longExit, shortExit; 

     protected override void Create() 
     { 
      // create variable objects, function objects, order objects etc. 
      bb = ((PowerLanguage.Indicator.Bollinger_Bands)AddIndicator("Bollinger_Bands")); 

      longEntry = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.Buy)); 
      shortEntry = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.SellShort)); 
      longExit = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.Sell)); 
      shortExit = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.BuyToCover)); 
     } 

     protected override void StartCalc() 
     { 
      // assign inputs 
      GetEcal(); 
      ptValue = Bars.Point; 

      longTP = longTPMod; 
      longSL = longSLMod; 
      shortTP = shortTPMod; 
      shortSL = shortSLMod; 

     } 

     protected override void CalcBar() 
     { 
      bool LE = false; 
      bool SE = false; 
      bool LX = false; 
      bool SX = false; 

      for(int i=0; i<tradeList.Count; i++) 
      { 
       if(Bars.Time[0] == tradeList.ElementAt(i).time) 
       { 
        if (tradeList.ElementAt(i).direction == "Up") 
        { 
         LE = true; 
         tradeList.RemoveAt(i); 
        } 
        else if (tradeList.ElementAt(i).direction == "Down") 
        { 
         SE = true; 
         tradeList.RemoveAt(i); 
        } 
        else 
        { 

        } 
       } 
      } 

      if(exitOn24 == true) 
      { 
       if (Bars.Time[0] > tradeTime.AddHours(24)) 
       { 
        LX = true; 
        SX = true; 
       } 
      } 

      if (StrategyInfo.MarketPosition == 0) 
      { 
       if (LE) 
       { 
        longEntry.Send(); 
        tradeTime = Bars.Time[0]; 
        setLongStops();  
       } 
       else if (SE) 
       { 
        shortEntry.Send(); 
        tradeTime = Bars.Time[0]; 
        setShortStops();   
       } 
      } 

      else if (StrategyInfo.MarketPosition > 0) 
      { 
       if (LX) 
       { 
        longExit.Send(); 
       } 
       else if (LE) 
       { 
        longEntry.Send(); 
        tradeTime = Bars.Time[0]; 
        setLongStops(); 
       } 
       else 
       { 
        CurSpecOrdersMode = ESpecOrdersMode.PerPosition; 
        GenerateStopLossPt(longSL); 
        GenerateProfitTargetPt(longTP); 
        //GenerateTrailingStopPt(buyTrail); 
       } 
      } 

      else if (StrategyInfo.MarketPosition < 0) 
      { 
       if (SX) 
       { 
        shortExit.Send(); 
       } 
       else if (SE) 
       { 
        shortEntry.Send(); 
        tradeTime = Bars.Time[0]; 
        setShortStops(); 
       } 
       else 
       { 
        CurSpecOrdersMode = ESpecOrdersMode.PerPosition; 
        GenerateStopLossPt(shortSL); 
        GenerateProfitTargetPt(shortTP); 
        //GenerateTrailingStopPt(sellTrail); 
       } 
      } 
     } 

     private void GetEcal() 
     { 
      csvList = File.ReadAllLines(ecalPath).Skip(1).ToList(); 
      foreach(string line in csvList) 
      { 
       string[] values = line.Split(','); 
       tradeList.Add(new Trades { time = Convert.ToDateTime(values[0]), direction = values[1] }); 
      } 
     } 
    } 


    public class Trades 
    { 
     public DateTime time { get; set; } 
     public string direction { get; set; } 
    } 


} 

減速的罪魁禍首是CalcBar()方法中的For循環。

+0

當你在'tradeList上使用開關時,它可能會更快。ElementAt的(ⅰ).direction'。 – DrNachtschatten

+2

使用「time」字典或排序樹作爲交易而不是列表。字典查找是攤銷O(1)和樹查找是O(lgN)。在這裏,這將是100萬美元的諮詢 - 當然,每個循環迭代的百萬分之一不會過多:) –

+1

您可以嘗試使用[Parallel.For](https://msdn.microsoft.com/en-我們/ library/system.threading.tasks.parallel.for(v = vs.110).aspx) – Pikoh

回答

7

您是否嘗試過配置此方法?我們的信息太少。例如,也許最昂貴的操作是

Bars.Time[0] == tradeList.ElementAt(i).time 

我們不知道。你應該首先介紹它。

接下來是什麼...

tradeList.ElementAt(i).direction == "Up" 

不要使用字符串。字符串很慢。你可以在這裏使用枚舉,這將被優化爲整數和整數,比字符串的速度要快得多。

請勿使用ElementAt方法。只使用[]運算符。速度更快。

考慮使用Dictionary而不是List。它比列表要快得多。列表必須通過每個元素來找到你需要的東西。字典不。這可能是真正關鍵的部分。

考慮使用整數而不是dateTimes。將整數視爲秒。它將比DateTime更快。

並使用Parallel.ForEach而不是普通的for。它會使用其他內核。普通的可能只使用一個核心。

哦,還有一件事。如果是股票申請,也許你可以嘗試使用神經網絡?但這是一個完全不同的故事。

+0

這非常有幫助。我可以使用1/-1作爲交易方向,而不是字符串「上」和「下」。我沒有意識到ElementAt比[]更慢。我認爲你使用字典是正確的。我知道必須有一種方法來搜索匹配的DateTIme值,而不是迭代列表中的每一個來查找匹配。 CalcBar()方法由交易軟件自動調用,所以它實際上使用全部8個核心。此外,我正在使用神經網絡,但只是爲了創建我從中獲取交易信息的CSV文件。 – Cnote

+0

對不起,我花了這麼長時間來回應,但我花了一段時間來優化和實施。這個答案的細節可以讓我加快處理速度5倍。 – Cnote

2

對於大型列表,哈希集合通常是實現更好性能的好方法。一些更多的信息是在這裏:

https://softwareengineering.stackexchange.com/questions/280361/list-comparing-techniques-for-faster-performance

或可選擇地爲什麼不使用字典和使用日期時間爲您的密鑰(你可以只使用任何其他類型的虛值,如果你不需要任何存儲更多詳情)

然後,您基於密鑰進行了有效的匹配,因此您無論是碰撞還是未命中。

6
  • RemoveAt移除將處理列表的其餘部分卸下下一個地方一個 後,每一個項目轉移。 see here。 這對你的情況有巨大的成本。

    解決方案是使用一個臨時列表,在其中添加您將在以後刪除的元素(sourceList.Except(removedList));或者只是將您的物品標記爲已刪除 ,並且永遠不要觸摸來源列表。

  • 您正在加載CSV中的所有行,以便讀取它們並在每行中創建一個強類型對象。

    您可以逐行讀取文件,並創建對象。

  • ElementAt可能比索引器慢。由於您正在使用列表,只需使用[]訪問項目以避免疑惑。

  • 要使用更少的內存和更快的比較,請使用'Up''Down'值使direction成爲枚舉值。

如果不平行化代碼,則不利用多個內核。 一旦你得到了權利,如果程序仍然需要幾個小時,你可以嘗試Parallel.For而不是for。在這種情況下,「將項目標記爲已刪除的解決方案」比使用併發列表更簡單並且性能更高,並將其與要刪除的項目一起提供。

-1

我的建議是通過分解來優化您的流程。首先,你檢查:

Bars.Time[0] == tradeList.ElementAt(i).time; 

湯姆利用這個把它添加到LINQ語句只那些你滿足條件來篩選使用:

tradeList.Where(t => t.time == Bars.Time[0]); 

現在你有另一套如果控制條件如果您刪除的項目:

tradeList.ElementAt(i).direction == "Down" || tradeList.ElementAt(i).direction == "Up"; 

這些可以進一步使用LINQ到被簡化:

tradeList.RemoveAll(d => d.direction == "Down" || d => d.Direction == "Up"); 

現在你可以調用RemoveAll方法您篩選使用湯姆的技術後:

tradeList.Where(t => t.time == Bars.Time[0]) 
    .RemoveAll(d => d.direction == "Up" || d => d.direction == "Down"); 

本聲明的所有意圖和目的,是工作和你的一樣,它遍歷使用foreach循環名單。但是現在我們可以使用PLINQ來優化它。好吧,這樣的權利隨着PLINQ移動,你會改變這條語句爲:

tradeList.AsParallel().tradeList.Where(t => t.time != Bars.Time[0] 
    && (d => d.direction != "Up" || d => d.direction != "Down")); 

結合我從removeall過()的邏輯到哪裏()方法,這個說法應該給你所有這些酒吧的列表不應該被刪除。現在我不確定你有bool標誌(LE和SE)的目的是什麼,但它們在第一次擊中後會變成真,所以有更好的方法來做到這一點。但是這應該讓你開始。

+0

你告訴他這樣的事情:好吧,你正在做的事情是最糟糕的方式,可能不理解你寫的每行代碼的成本和意義,但你可以把它們寫得更簡潔並且並行化他們 – sam

+0

嗨Huda,我的印象是LINQ比()循環更昂貴。我錯了嗎? – Cnote