async Task FillTestParams(TradingMacro tmOriginal, Action <IList <KeyValuePair <string, object>[]> > paramsTransformation)
        {
            var c = _testParamValuesSeparators;

            if (!_testParamsRaw.Any())
            {
                if (!ReplayArguments.IsWww && tmOriginal.UseTestFile)
                {
                    var od = new Microsoft.Win32.OpenFileDialog()
                    {
                        FileName = "TestParams", DefaultExt = ".txt", Filter = "Text documents(.txt)|*.txt"
                    };
                    var odRes = od.ShowDialog();
                    if (!odRes.GetValueOrDefault())
                    {
                        throw new ArgumentException("Must provide test params file name.");
                    }
                    tmOriginal.TestFileName = System.IO.Path.GetFileName(od.FileName);
                    if (tmOriginal.TestFileName.Contains("skipme"))
                    {
                        tmOriginal.TestFileName = "";
                    }
                    else
                    {
                        var paramsDict = Lib.ReadTestParameters(od.FileName);
                        _testParamsRaw.AddRange(paramsDict.Select(kv => kv.Value.Split(c).Select(v => new KeyValuePair <string, object>(kv.Key, v)).ToArray()));
                    }
                }
                else if (ReplayArguments.IsWww)
                {
                    ReplayArguments.LastWwwError = "";
                    var strats = TaskMonad.RunSync(() => ReadStrategies(tmOriginal, (name, desc, content, uri, diff) => new { name, content, diff }));
                    Func <string, bool> isTest = s => s.ToLower().Trim().EndsWith("{t}");
                    await strats.Select(s => s.First())
                    .Take(2)
                    .OrderByDescending(s => isTest(s.name))
                    .Where(s => isTest(s.name) || s.diff.IsEmpty())
                    .OnEmpty(() => { LogWww(new Exception(ReplayArguments.LastWwwError = "Current settings don't match any strategy")); })
                    .Select(strategy => {
                        tmOriginal.TestFileName = strategy.name;
                        var paramsDict          = Lib.ReadParametersFromString(strategy.content);
                        _testParamsRaw.AddRange(
                            paramsDict
                            .Select(kv => kv.Value.Split(c).Select(v => new KeyValuePair <string, object>(kv.Key, v))
                                    .ToArray()));
                        return(tmOriginal.LoadActiveSettings(strategy.name, TradingMacro.ActiveSettingsStore.Gist));
                    }).WhenAll();
                }
                else
                {
                    var testParams = tmOriginal.GetPropertiesByAttibute <CategoryAttribute>(a => a.Category == TradingMacro.categoryTest);
                    var paramsDict = testParams.ToDictionary(p => p.Item2.Name.Substring(4), p => p.Item2.GetValue(tmOriginal, null).ToString().ParseParamRange());
                    _testParamsRaw.AddRange(paramsDict.Where(kv => !string.IsNullOrWhiteSpace(kv.Value))
                                            .Select(kv => kv.Value.Split(c).Select(v => new KeyValuePair <string, object>(kv.Key, v)).ToArray()));
                }
            }
            TestParams.Clear();
            paramsTransformation(_testParamsRaw);
            _testParamsRaw.CartesianProduct().ForEach(tp => TestParams.Enqueue(tp.ToArray()));
        }
        async Task StartReplay(TradingMacro tm)
        {
            TradingMacro tmOriginal = (TradingMacro)tm;

            if (!IsLoggedIn)
            {
                LogWww(new Exception("Must login first."));
                return;
            }

            try {
                while (_replayTasks.ToArray().Any(t => t.Status == TaskStatus.Running))
                {
                    if (ReplayArguments.IsWww)
                    {
                        ReplayArguments.LastWwwError = "Replay is running";
                        return;
                    }
                    Log = new Exception("Replay is running.");
                    Thread.Sleep(1000);
                    continue;
                }

                ReplayArguments.Initiator       = tmOriginal;
                ReplayArguments.StartingBalance = tmOriginal.TestBalance;
                ReplayArguments.PrevSessionUid  = tmOriginal.TestPrevSession;
                ReplayArguments.SuperSessionId  = tmOriginal.TestSuperSessionUid.ValueOrDefault(Guid.NewGuid());
                _testParamsRaw.Clear();
                tmOriginal.TestFileName = "";

                if (ReplayArguments.UseSuperSession)
                {
                    #region getDateFromSuperSession
                    Func <Task <DateTime> > getDateFromSuperSession = async() => {
                        try {
                            var sessions = GetBestSessions(ReplayArguments.SuperSessionId).ToArray();
                            if (sessions.Any())
                            {
                                await FillTestParams(tmOriginal, tpr => { });
                            }
                            else
                            {
                                throw new Exception("Either ReplayArguments.DateStart or valid Supersession Uid must be provided.");
                            }
                            return(sessions.Min(s => s.DateStart.Value).AddDays(5));
                        } catch (Exception exc) {
                            Log = exc;
                            throw;
                        }
                    };
                    #endregion
                    ReplayArguments.DateStart = ReplayArguments.DateStart ?? await getDateFromSuperSession();
                }
                await FillTestParams(tmOriginal, pt => { });

                Log = new Exception("Starting testing with {0} sets.".Formater(TestParams.Count));
                StartReplayInternal(tmOriginal, TestParams.Any() ? TestParams.Dequeue() : null, task => { ContinueReplayWith(tmOriginal, TestParams); });
            } catch (Exception exc) { Log = exc; }
        }
        void SaveTradingSettings(TradingMacro tmOriginal)
        {
            try {
                var attrs = new[] { TradingMacro.categoryActive, TradingMacro.categoryActiveFuncs };
                tmOriginal.GetPropertiesByAttibute <CategoryAttribute>(a => attrs.Contains(a.Category))
                .GroupBy(a => a.Item2.Name).ToList().ForEach(g => {
                });

                //.ForEach(p => Debug.WriteLine("{0}={1}", p.Name, p.GetValue(tmOriginal, null)));
            } catch { }
        }
Exemple #4
0
        public void IsTradingHour()
        {
            var d  = DateTime.Parse("1:00");
            var d2 = DateTime.Parse("17:00");
            var d3 = DateTime.Parse("00:00");

            Assert.IsTrue(TradingMacro.IsTradingHour("", d));
            Assert.IsTrue(TradingMacro.IsTradingHour("", d3));
            Assert.IsTrue(TradingMacro.IsTradingHour("0:29-1:01", d));
            Assert.IsFalse(TradingMacro.IsTradingHour("0:29-1:01", d2));
            Assert.IsTrue(TradingMacro.IsTradingHour("21:00-1:00", d));
            Assert.IsFalse(TradingMacro.IsTradingHour("21:00-1:00", d2));
        }
Exemple #5
0
        public void IsTradingHour2()
        {
            var range = TradingMacro.ParseTimeRangeTimeSpans("[['9:00','17:00']]");

            Assert.IsTrue(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("10:00"))));
            Assert.IsFalse(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("17:30"))));

            range = TradingMacro.ParseTimeRangeTimeSpans("[['9:00','17:00'],['17:30','17:40']]");
            Assert.IsTrue(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("17:30"))));
            Assert.IsTrue(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("9:30"))));
            Assert.IsFalse(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("17:20"))));

            Assert.IsTrue(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("17:20"))));
            Assert.IsTrue(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("9:00"))));
            Assert.IsFalse(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("16:20"))));
            Assert.IsFalse(TradingMacro.IsTradingHour2Impl((range, DateTime.Parse("9:20"))));
        }
 void ContinueReplayWith(TradingMacro tm, Queue <TestParam> testParams)
 {
     try {
         if (tm.Strategy == Strategies.None)
         {
             return;
         }
         if (testParams.Any())
         {
             StartReplayInternal(tm, testParams.Dequeue(), t => { ContinueReplayWith(tm, testParams); });
         }
         else
         {
             ReplayArguments.LastWwwErrorObservable.OnNext("Replay done");
         }
     } catch (Exception exc) { Log = exc; }
 }
Exemple #7
0
        public void GetTradeConditions()
        {
            var tcs = new TradingMacro().GetTradeConditions();

            Assert.IsTrue(tcs != null);
        }
        void StartReplayInternal(TradingMacro tmOriginal, TestParam testParameter, Action <Task> continueWith)
        {
            if (IsInVirtualTrading)
            {
                while (_replayTasks.ToArray().Any(t => t.Status == TaskStatus.Running))
                {
                    Log = new Exception("Replay is running.");
                    Thread.Sleep(1000);
                    continue;
                }
                _replayTasks.Clear();
                MasterModel.AccountModel.Balance            = MasterModel.AccountModel.Equity = 50000;
                MasterModel.AccountModel.CurrentGrossInPips = 0;
                MasterModel.AccountModel.CurrentLoss        = 0;
                TradesManager.GetAccount().Balance = TradesManager.GetAccount().Equity = 50000;
            }
            SaveTradingSettings(tmOriginal);
            var tms = GetTradingMacros().Where(t => t.IsActive).ToList();

            ReplayArguments.SetTradingMacros(tms);
            ReplayArguments.GetOriginalBalance = new Func <double>(() => MasterModel.AccountModel.OriginalBalance);
            foreach (var tm in tms)
            {
                if (IsInVirtualTrading)
                {
                    if (tm.Strategy == Strategies.None)
                    {
                        tm.Strategy = Strategies.UniversalA;
                    }
                    TradesManager.ClosePair(tm.Pair);
                    tm.ResetSessionId(ReplayArguments.SuperSessionId);
                    if (testParameter != null && tm == tmOriginal)
                    {
                        testParameter.ForEach(tp => {
                            try {
                                tm.LoadSetting(tp);
                            } catch (SetLotSizeException) {
                            } catch (Exception exc) {
                                if (!(exc.InnerException is SetLotSizeException))
                                {
                                    var e2 = new Exception("Property:" + new { tp.Key, tp.Value }, exc);
                                    LogWww(e2);
                                    throw e2;
                                }
                            }
                        });
                    }
                }
                if (!string.IsNullOrWhiteSpace(ReplayArguments.PrevSessionUid))
                {
                    TradingMacro.SessionId = Guid.Parse(ReplayArguments.PrevSessionUid);
                }
                var tmToRun = tm;
                tmToRun.ReplayCancelationToken = (_replayTaskCancellationToken = new CancellationTokenSource()).Token;
                var task = Task.Factory.StartNew(() => tmToRun.Replay(ReplayArguments), tmToRun.ReplayCancelationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);

                task.ContinueWith(t => {
                    RxApp.MainThreadScheduler.Schedule(() => _replayTasks.Remove(t));
                    if (tm == tmOriginal)
                    {
                        continueWith(t);
                    }
                });
                _replayTasks.Add(task);
                ReplayArguments.IsWww = false;
            }
        }
        public object[] ServeChart(int chartWidth, DateTimeOffset dateStart, DateTimeOffset dateEnd, TradingMacro tm)
        {
            var digits = tm.Digits();

            if (dateEnd > tm.LoadRatesStartDate2)
            {
                dateEnd = tm.LoadRatesStartDate2;
            }
            else
            {
                dateEnd = dateEnd.AddMinutes(-tm.BarPeriodInt.Min(2));
            }
            string pair = tm.Pair;
            Func <Rate, double> rateHL = rate => (rate.PriceAvg >= rate.PriceCMALast ? rate.PriceHigh : rate.PriceLow).Round(digits);

            #region map
            var rth      = new[] { new TimeSpan(9, 30, 0), new TimeSpan(16, 0, 0) };
            var rthDates = MonoidsCore.ToFunc((DateTime dt) => dt.Date.With(d => rth.Select(h => d + h)).ToArray()).MemoizeLast(dt => dt.Date);
            bool isRth(DateTime dt)
            {
                return(dt.Between(rthDates(dt)));
            }

            var doShowVolt  = tm.VoltageFunction != VoltageFunction.None;
            var doShowVolt2 = tm.VoltageFunction2 != VoltageFunction.None || tm.VoltageFunction == VoltageFunction.PPMH;
            var lastVolt    = tm.GetLastVolt().DefaultIfEmpty().Memoize();
            var lastVolt2   = tm.GetLastVolt(tm.GetVoltage2).DefaultIfEmpty().Memoize();
            var lastCma     = tm.UseRates(TradingMacro.GetLastRateCma).SelectMany(cma => cma).FirstOrDefault();
            var tsMin       = TimeSpan.FromMinutes(tm.BarPeriodInt);
            var priceHedge  = MonoidsCore.ToFunc((Rate r) => r.PriceHedge).MemoizePrev(d => d.IsZeroOrNaN());
            var map         = MonoidsCore.ToFunc((Rate)null, rate => new {
                //d = rate.StartDate2,
                d  = tm.BarPeriod == BarsPeriodType.t1 ? rate.StartDate2 : rate.StartDate2.Round().With(d => d == rate.StartDate2 ? d : d + tsMin),
                c  = rateHL(rate),
                v  = doShowVolt ? tm.GetVoltage(rate).IfNaNOrZero(lastVolt) : 0,
                v2 = doShowVolt2 ? tm.GetVoltage2(rate).IfNaNOrZero(lastVolt2) : 0,
                m  = rate.PriceCMALast.IfNaNOrZero(lastCma).Round(digits),
                a  = rate.AskHigh.Round(digits),
                b  = rate.BidLow.Round(digits),
                h  = isRth(rate.StartDate.InNewYork()),
                p  = priceHedge(rate)
            });
            #endregion
            var exit = false;// doShowVolt && lastVolt.IsEmpty() || doShowVolt2 && lastVolt2.IsEmpty();
            if (exit || tm.RatesArray.Count == 0 || tm.IsTrader && tm.BuyLevel == null)
            {
                return new[] { new { rates = new int[0] } }
            }
            ;

            var tmTrader = GetTradingMacros(tm.Pair).Where(t => t.IsTrader).DefaultIfEmpty(tm).Single();
            var tpsHigh  = tm.GetVoltageHigh().SingleOrDefault();
            var tpsLow   = tm.GetVoltageAverage().SingleOrDefault();
            var tps2High = tm.GetVoltage2High().Where(v => !v.IsNaN()).ToArray();
            var tps2Low  = tm.GetVoltage2Low().Where(v => !v.IsNaN()).ToArray();
            var tpsCurr2 = tm.UseRates(ra => ra.BackwardsIterator().Select(tm.GetVoltage2).SkipWhile(double.IsNaN).FirstOrDefault()).DefaultIfEmpty(0).Single();


            var ratesForChart = tm.UseRates(rates => rates.Where(r => r.StartDate2 >= dateEnd /* && !tm.GetVoltage(r).IsNaNOrZero()*/).ToList()).FirstOrDefault();
            if (ratesForChart == null)
            {
                return(new object[0]);
            }
            var ratesForChart2 = tm.UseRates(rates => rates.Where(r => r.StartDate2 < dateStart /* && !tm.GetVoltage(r).IsNaNOrZero()*/).ToList()).FirstOrDefault();
            if (ratesForChart2 == null)
            {
                return(new object[0]);
            }

            double cmaPeriod = tm.CmaPeriodByRatesCount();
            if (tm.IsTicks)
            {
                Action <IList <Rate>, Rate> volts = (gr, r) => {
                    if (doShowVolt)
                    {
                        tm.SetVoltage(r, gr.Select(tm.GetVoltage).Where(v => v.IsNotNaN()).DefaultIfEmpty(lastVolt.First()).Average());
                    }
                    if (doShowVolt2)
                    {
                        tm.SetVoltage2(r, gr.Select(tm.GetVoltage2).Where(v => v.IsNotNaN()).DefaultIfEmpty(lastVolt2.First()).Average());
                    }
                };
                cmaPeriod /= tm.TicksPerSecondAverage;
                if (ratesForChart.Count > 1)
                {
                    ratesForChart = TradingMacro.GroupTicksToSeconds(ratesForChart, volts).ToList();
                }
                if (ratesForChart2.Count > 1)
                {
                    ratesForChart2 = TradingMacro.GroupTicksToSeconds(ratesForChart2, volts).ToList();
                }
            }
            var getRates    = MonoidsCore.ToFunc((IList <Rate>)null, rates3 => rates3.Select(map).ToList());
            var tradeLevels = tmTrader.Strategy != Strategies.Universal || !tmTrader.HasBuyLevel ? new object { } : new {
                buy        = tmTrader.BuyLevel.Rate.Round(digits),
                buyClose   = tmTrader.BuyCloseLevel.Rate.Round(digits),
                canBuy     = tmTrader.BuyLevel.CanTrade,
                manualBuy  = tmTrader.BuyLevel.InManual,
                buyCount   = tmTrader.BuyLevel.TradesCount,
                sell       = tmTrader.SellLevel.Rate.Round(digits),
                sellClose  = tmTrader.SellCloseLevel.Rate.Round(digits).With(d => d == 0 ? tmTrader.RatesArray[0].PriceAvg : d),
                canSell    = tmTrader.SellLevel.CanTrade,
                manualSell = tmTrader.SellLevel.InManual,
                sellCount  = tmTrader.SellLevel.TradesCount,
            };

            /*
             * if (tm.IsAsleep) {
             * var o = new object();
             * var a = new object[0];
             * return new {
             *  rates = getRates(ratesForChart),
             *  rates2 = getRates(ratesForChart2),
             *  ratesCount = tm.RatesArray.Count,
             *  dateStart = tm.RatesArray[0].StartDate2,
             *  trendLines = o,
             *  trendLines2 = o,
             *  trendLines1 = o,
             *  isTradingActive = tm.IsTradingActive,
             *  tradeLevels = o,
             *  trades = a,
             *  askBid = o,
             *  hasStartDate = tm.CorridorStartDate.HasValue,
             *  cmp = cmaPeriod,
             *  tpsAvg = 0,
             *  isTrader = tm.IsTrader,
             *  canBuy = false,
             *  canSell = false,
             *  waveLines = a
             * };
             * }
             */
            Func <Lazy <IList <Rate> >, IList <Rate> > safeTLs = tls => tls.Value ?? new List <Rate>();
            var trends     = safeTLs(tm.TrendLines);
            var trendLines = new {
                dates = trends.Count > 1
        ? new DateTimeOffset[] {
                    trends[0].StartDate2,
                    trends[1].StartDate2
                }
        : new DateTimeOffset[0],
                close1 = trends.ToArray(t => t.Trends.PriceAvg1.Round(digits)),
                close2 = trends.ToArray(t => t.Trends.PriceAvg2.Round(digits)),
                close3 = trends.ToArray(t => t.Trends.PriceAvg3.Round(digits)),
                sel    = TradingMacro.IsTrendsEmpty(trends).IsSelected
                         //close21 = trends.ToArray(t => t.Trends.PriceAvg21.Round(digits)),
                         //close31 = trends.ToArray(t => t.Trends.PriceAvg31.Round(digits))
            };
            var ratesLastStartDate2 = tm.RatesArray.Last().StartDate2;

            var trends2     = safeTLs(tm.TrendLines2);
            var trendLines2 = new {
                dates = trends2.Count == 0
        ? new DateTimeOffset[0]
        : new DateTimeOffset[] {
                    trends2[0].StartDate2,
                    trends2[1].StartDate2
                },
                close1 = trends2.ToArray(t => t.Trends.PriceAvg1.Round(digits)),
                close2 = trends2.ToArray(t => t.Trends.PriceAvg2.Round(digits)),
                close3 = trends2.ToArray(t => t.Trends.PriceAvg3.Round(digits)),
                sel    = TradingMacro.IsTrendsEmpty(trends2).IsSelected
            };

            var trends0     = safeTLs(tm.TrendLines0);
            var trendLines0 = new {
                dates = trends0.Count == 0
        ? new DateTimeOffset[0]
        : new DateTimeOffset[] {
                    trends0[0].StartDate2,
                    trends0[1].StartDate2
                },
                close2 = trends0.ToArray(t => t.Trends.PriceAvg2.Round(digits)),
                close3 = trends0.ToArray(t => t.Trends.PriceAvg3.Round(digits)),
                sel    = TradingMacro.IsTrendsEmpty(trends0).IsSelected
            };

            var trends1     = safeTLs(tm.TrendLines1);
            var trendLines1 = new {
                dates = trends1.Count == 0
        ? new DateTimeOffset[0]
        : new DateTimeOffset[] {
                    trends1[0].StartDate2,
                    trends1[1].StartDate2
                },
                close2 = trends1.ToArray(t => t.Trends.PriceAvg2.Round(digits)),
                close3 = trends1.ToArray(t => t.Trends.PriceAvg3.Round(digits)),
                sel    = TradingMacro.IsTrendsEmpty(trends1).IsSelected
            };

            var trends3     = safeTLs(tm.TrendLines3);
            var trendLines3 = new {
                dates = trends3.Count == 0
        ? new DateTimeOffset[0]
        : new DateTimeOffset[] {
                    trends3[0].StartDate2,
                    trends3[1].StartDate2
                },
                close2 = trends3.ToArray(t => t.Trends.PriceAvg2.Round(digits)),
                close3 = trends3.ToArray(t => t.Trends.PriceAvg3.Round(digits)),
                sel    = TradingMacro.IsTrendsEmpty(trends3).IsSelected
            };

            var waveLines = tm.WaveRangesWithTail
                            .ToArray(wr => new {
                dates = new[] { wr.StartDate, wr.EndDate },
                isept = new[] { wr.InterseptStart, wr.InterseptEnd },
                isOk  = wr.IsFatnessOk
            });
            var tmg     = TradesManager;
            var trades0 = tmg.GetTrades();
            Func <bool, Trade[]> getTrades = isBuy => trades0.Where(t => t.IsBuy == isBuy).ToArray();
            var trades   = new ExpandoObject();
            var tradeFoo = MonoidsCore.ToFunc(false, isBuy => new { o = getTrades(isBuy).NetOpen(), t = getTrades(isBuy).Max(t => t.Time) });
            getTrades(true).Take(1).ForEach(_ => trades.Add(new { buy = tradeFoo(true) }));
            getTrades(false).Take(1).ForEach(_ => trades.Add(new { sell = tradeFoo(false) }));
            if (!tmg.TryGetPrice(pair, out var price))
            {
                return(new object[0]);
            }
            var askBid = new { ask = price.Ask.Round(digits), bid = price.Bid.Round(digits) };
            var ish    = tm.IsPairHedged;
            var hph    = !tm.PairHedge.IsNullOrWhiteSpace();
            var ret    = tm.UseRates(ratesArray => ratesArray.Take(1).ToArray(), x => x).ToArray(_ => new {
                rates      = getRates(ratesForChart),
                rates2     = getRates(ratesForChart2),
                ratesCount = tm.RatesArray.Count,
                dateStart  = tm.RatesArray[0].StartDate2,
                trendLines0,
                trendLines,
                trendLines2,
                trendLines1,
                trendLines3,
                isTradingActive = tm.IsTradingActive,
                tradeLevels     = tradeLevels,
                trades,
                askBid,
                hasStartDate = tm.CorridorStartDate.HasValue,
                cmp          = cmaPeriod,
                tpsHigh,
                tps2Low,
                tps2High,
                tpsLow,
                tpsCurr2,
                isTrader = tm.IsTrader,
                canBuy   = tmTrader.CanOpenTradeByDirection(true),
                canSell  = tmTrader.CanOpenTradeByDirection(false),
                waveLines,
                barPeriod = tm.BarPeriodInt,
                ish,
                hph,
                vfs  = tm.IsVoltFullScale ? 1 : 0,
                vfss = ish || tm.IsVoltFullScale ? tm.VoltsFullScaleMinMax : new[] { 0.0, 0.0 }
            });
            return(ret);
        }
    }
 public static async Task LoadStrategy(TradingMacro tm, string strategy)
 {
     await tm.LoadActiveSettings(strategy, TradingMacro.ActiveSettingsStore.Gist);
 }
        public static async Task UpdateStrategy(TradingMacro tm, string nick)
        {
            await tm.SaveActiveSettings(nick, TradingMacro.ActiveSettingsStore.Gist);

            //File.Delete(path);
        }
        //private static string StrategiesPath(string pathEnd = "") {
        //  return Path.Combine(Directory.GetCurrentDirectory(), "..", "Strategies", pathEnd);
        //}

        public static async Task SaveStrategy(TradingMacro tm, string nick)
        {
            await tm.SaveActiveSettings(nick, TradingMacro.ActiveSettingsStore.Gist);
        }
        public static async Task <IEnumerable <IEnumerable <T> > > ReadStrategies <T>(TradingMacro tmTrader, Func <string, string, string, Uri, string[], T> map)
        {
            var localMap       = MonoidsCore.ToFunc("", "", "", (Uri)null, (name, description, content, uri) => new { name, description, content, uri });
            var strategiesAll  = (await Cloud.GitHub.GistStrategies(localMap)).ToArray();
            var activeSettings = tmTrader.TradingMacrosByPair().ToArray(tm => tm.Serialize(true));

            return(from strategies in strategiesAll
                   let diffs = strategies.Zip(activeSettings, (strategy, activeSetting) => TradingMacro.ActiveSettingsDiff(strategy.content, activeSetting).ToDictionary())
                               .Select((diff, i) => diff.Select(kv => new { diff = i + ":" + kv.Key + "= " + kv.Value[0] + " {" + kv.Value[1] + "}", lev = Lib.LevenshteinDistance(kv.Value[0], kv.Value[1]) }).ToArray())
                               //.OrderBy(x=> x.Sum(d=>d.diff.Length))//.ThenBy(x=>x[0], diff.Sum(x => x.lev), strategy.name
                               orderby diffs.Sum(d => d.Length)
                               select strategies.Select(strategy => map(strategy.name, strategy.description, strategy.content, strategy.uri, diffs.SelectMany(a => a.Select(x => x.diff)).ToArray()))
                   );
        }