예제 #1
0
        public void MemoizePrevTest()
        {
            var test1 = new { a = new { b = 1 } };
            var test2 = new { a = new { b = 100 } };
            var test3 = new { a = new { b = 2 } };
            var func  = MonoidsCore.ToFunc(test1, a => a.a.b).MemoizePrev(a => a > 10);

            Assert.AreEqual(1, func(test1));
            Assert.AreEqual(1, func(test2));
            Assert.AreEqual(2, func(test3));
        }
        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()))
                   );
        }
        public static IEnumerable <TOut> GetActiveSettings2 <TKey, TValue, TOut>(bool excludeNotStrategy, TradingMacro tm, Func <string, int, TKey> grouper, Func <string, object, TValue> valuer, Func <TKey, IEnumerable <TValue>, TOut> outer)
        {
            var gr   = MonoidsCore.ToFunc("", 0, (Category, s) => new { Category, s });
            var type = typeof(TradingMacro);
            var cat  = type.GetPropertiesByAttibute(() => (CategoryAttribute)null, (c, pi) => new { c.Category, pi, s = 0 });
            var dm   = type.GetPropertiesByAttibute(() => (EdmScalarPropertyAttribute)null, (c, pi) => new { Category = "DataMember", pi, s = 1, key = c.EntityKeyProperty })
                       .Where(x => !x.key /*&& !IsMemberExcluded(x.pi.Name)*/)
                       .Select(x => new { x.Category, x.pi, x.s });
            var nonStrategy = GetNotStrategyActiveSettings();

            return
                (from setting in cat.Concat(dm).Distinct(x => x.pi)
                 join ns in nonStrategy on setting.pi.Name equals ns into gss
                 where !excludeNotStrategy || gss.IsEmpty()
                 where IsNotDnr(setting.pi) && (!excludeNotStrategy || IsStrategy(setting.pi))
                 group setting by grouper(setting.Category, setting.s) into gs
                 select outer(gs.Key, gs.Select(v => valuer(v.pi.Name, v.pi.GetValue(tm, null))))
                );
        }
예제 #4
0
        void SubscribeToEntryOrderRelatedEvents()
        {
            var bsThrottleTimeSpan    = 0.1.FromSeconds();
            var cpThrottleTimeSpan    = 0.25.FromSeconds();
            var buySelPropsExceptions = new[] { "CanTradeEx", "IsGhost" };
            Func <IReactivePropertyChangedEventArgs <SuppRes>, bool> buySellPropsFilter = _ => !buySelPropsExceptions.Contains(_.PropertyName);

            //ISubject<Action> fxWraper = new Subject<Action>();
            //fxWraper.ObserveOn(TradesManagerStatic.TradingScheduler).Subscribe(a => a(), () => { Debugger.Break(); });

            #region SetTradeNet
            Action <Trade, double, double> SetTradeNet = (trade, limit, stop) => {
                //fxWraper.OnNext(() => {
                var fw = TradesManager;
                if (!limit.IsNaN())
                {
                    try {
                        if (fw.GetNetOrderRate(Pair, false).Abs(limit) > InPoints(1))
                        {
                            //Log = new Exception("FixOrderSetLimit:" + new { trade.Pair, limit = limit.Round(Digits()) });
                            fw.FixOrderSetLimit(trade.Id, limit, "");
                        }
                    } catch (Exception exc) { Log = exc; }
                }
                if (!stop.IsNaN())
                {
                    try {
                        if (fw.GetNetOrderRate(Pair, true).Abs(stop) > InPoints(1))
                        {
                            //Log = new Exception("FixOrderSetStop:" + new { trade.Pair, stop = stop.Round(Digits()) });
                            fw.FixOrderSetStop(trade.Id, stop, "");
                        }
                    } catch (Exception exc) { Log = exc; }
                }
                TradeLastChangeDate = DateTime.Now;
                //});
            };
            Action <Trade, double> SetTradeNetLimit = (trade, limit) => SetTradeNet(trade, limit, double.NaN);
            Action <Trade, double> SetTradeNetStop  = (trade, stop) => SetTradeNet(trade, double.NaN, stop);
            Action CloseAllNetLimits = () => Trades.Take(1).ForEach(trade => SetTradeNetLimit(trade, 0));
            Action CloseAllNetStops  = () => Trades.Take(1).ForEach(trade => SetTradeNetStop(trade, 0));
            #endregion

            #region startBuySellLevelsTracking
            Action startBuySellLevelsTracking = () => {
                #region updateEntryOrders
                Action <string> updateEntryOrders = (reason) => {
                    try {
                        var buySellLevels = new[] { BuyLevel, SellLevel };
                        GetEntryOrders().GroupBy(eo => eo.IsBuy).SelectMany(eog => eog.Skip(1)).ForEach(OnDeletingOrder);
                        Func <SuppRes, bool> canTrade = (sr) =>/* IsTradingHour() &&*/
                                                        sr.CanTrade && sr.TradesCount <= 0 &&
                                                        !Trades.IsBuy(sr.IsBuy).Any();
                        Func <bool, int> lotSize = isBuy =>
                                                   (buySellLevels.Where(sr => sr.IsBuy == isBuy).Any(canTrade) ? (isBuy ? LotSizeByLossBuy : LotSizeByLossSell) : 0)
                                                   + (TradesManager.GetNetOrderRate(Pair, true) > 0 ? 0 : Trades.IsBuy(!isBuy).Lots());
                        buySellLevels.Select(sr => new { sr.IsBuy, sr.Rate, lotSize = lotSize(sr.IsBuy) })
                        .Do(sr => GetEntryOrders(sr.IsBuy).Where(a => sr.lotSize == 0).ForEach(OnDeletingOrder))
                        .Where(sr => sr.lotSize > 0 && !GetEntryOrders(sr.IsBuy).Any())
                        .ForEach(level => OnCreateEntryOrder(level.IsBuy, level.lotSize, level.Rate));

                        Action <Order> changeLimit = eo => TradesManager.YieldIf(!IsInVirtualTrading && eo.Lot.Ratio(lotSize(eo.IsBuy)) > 1.025)
                                                     .ForEach(fw => {
                            //Log = new Exception("ChangeEntryOrderLot:" + reason);
                            fw.ChangeEntryOrderLot(eo.OrderID, lotSize(eo.IsBuy));
                        });

                        Func <bool, double> orderRate  = isBuy => buySellLevels.Where(sr => sr.IsBuy == isBuy).First().Rate;
                        Action <Order>      changeRate = eo => TradesManager.YieldIf(!IsInVirtualTrading && eo.Rate.Abs(orderRate(eo.IsBuy)) > PointSize)
                                                         .ForEach(fw => {
                            //Log = new Exception("ChangeEntryOrderRate:" + reason);
                            fw.ChangeEntryOrderRate(eo.OrderID, orderRate(eo.IsBuy));
                        });

                        GetEntryOrders().ForEach(eo => {
                            changeLimit(eo);
                            changeRate(eo);
                        });
                    } catch (Exception exc) { Log = exc; }
                };
                #endregion
                _reactiveBuySellLevels = new[] { BuyLevel, SellLevel, BuyCloseLevel, SellCloseLevel }.CreateDerivedCollection(sr => sr);
                _reactiveBuySellLevels.ChangeTrackingEnabled = true;
                _reactiveBuySellLevelsSubscribtion           = (CompositeDisposable)
                                                               _reactiveBuySellLevels.ItemChanged
                                                               .Where(buySellPropsFilter)
                                                               .Sample(bsThrottleTimeSpan)
                                                               //.Do(_ => Log = new Exception(new { Name = "startBuySellLevelsTracking", _.PropertyName, Value = _.Value + "" } + ""))
                                                               .Select(_ => _.Sender.IsBuy ? "Buy" + (_.Sender.IsExitOnly ? "Close" : "") + "Level" : "Sell" + (_.Sender.IsExitOnly ? "Close" : "") + "Level")
                                                               .Merge(ReactiveTrades.ItemChanged.Where(_ => _.PropertyName == "Stop").Select(_ => _.Sender.IsBuy ? "BuyTrade" : "SellTrade"))
                                                               .Merge(Observable.FromEventPattern <EventHandler <OrderEventArgs>, OrderEventArgs>(
                                                                          h => TradesManager.OrderAdded += h, h => TradesManager.OrderAdded -= h).Select(e => "OrderAdded"))
                                                               .Merge(Observable.FromEventPattern <EventHandler <OrderEventArgs>, OrderEventArgs>(
                                                                          h => TradesManager.OrderChanged += h, h => TradesManager.OrderChanged -= h).Select(e => "OrderChanged"))
                                                               .Merge(Observable.FromEvent <OrderRemovedEventHandler, Order>(h => TradesManager.OrderRemoved += h, h => TradesManager.OrderRemoved -= h).Select(_ => "OrderRemoved"))
                                                               .Merge(this.WhenAny(tm => tm.CurrentPrice, tm => "CurrentPrice").Sample(cpThrottleTimeSpan))
                                                               .Merge(this.WhenAny(tm => tm.CanDoEntryOrders, tm => "CanDoEntryOrders"))
                                                               .Merge(this.WhenAny(tm => tm.CanDoNetStopOrders, tm => "CanDoNetStopOrders"))
                                                               .Subscribe(reason => _updateEntryOrdersBuffer.Push(() => updateEntryOrders(reason)));
                updateEntryOrders("Start Tracking");
            };
            #endregion
            #region startBuySellCloseLevelsTracking
            #region Net Update Implementations
            var    bsCloseLevels          = MonoidsCore.ToFunc(() => new[] { BuyCloseLevel, SellCloseLevel }.Where(sr => sr != null));
            Action updateTradeLimitOrders = () => {
                Func <Trade, double[]> levelRate  = trade => bsCloseLevels().Where(sr => sr.IsBuy == !trade.IsBuy).Select(sr => sr.Rate).Take(1).ToArray();
                Action <Trade>         changeRate = trade => levelRate(trade)
                                                    .Where(_ => !IsInVirtualTrading)
                                                    .Where(lr => trade.Limit.Abs(lr) > PointSize)
                                                    .ForEach(lr => SetTradeNetLimit(trade, lr));
                Trades.Take(1).ForEach(changeRate);
            };
            Func <Trade, double[]> getDefaultStop = trade => // Take care of reversed corridor
                                                    bsCloseLevels()
                                                    .Where(_ => SellLevel.Rate > BuyLevel.Rate)
                                                    .Where(bs => bs.IsBuy != trade.IsBuy)
                                                    .Select(bs => trade.Open.Abs(bs.Rate) * 2)
                                                    .Select(stop => trade.IsBuy ? -stop : stop)
                                                    .Select(stop => trade.Open + stop)
                                                    .ToArray();
            Action updateTradeStopOrders = () => {
                var bsLevels = new[] { BuyLevel, SellLevel }.Where(sr => sr != null);
                Func <Trade, IEnumerable <double> > levelRate = trade =>
                                                                getDefaultStop(trade)
                                                                .Concat(bsLevels
                                                                        .Where(sr => sr.IsBuy == !trade.IsBuy && (!CanDoEntryOrders || !sr.CanTrade))
                                                                        .Select(t => t.Rate)
                                                                        .Take(1)
                                                                        )
                                                                .Take(1);
                Action <Trade> changeRate = trade => TradesManager.YieldNotNull(levelRate(trade).Any(rate => trade.Stop.Abs(rate) > PointSize))
                                            .ForEach(fw => levelRate(trade).ForEach(rate => SetTradeNetStop(trade, rate)));
                Trades.Take(1).ForEach(changeRate);
            };
            #endregion
            // New Limit
            Action startBuySellCloseLimitTracking = () => {
                _reactiveBuySellLimitLevels = new[] { BuyCloseLevel, SellCloseLevel, BuyLevel, SellLevel }
                .CreateDerivedCollection(sr => sr);
                _reactiveBuySellLimitLevels.ChangeTrackingEnabled = true;
                _reactiveBuySellCloseLimitSubscribtion            = (CompositeDisposable)_reactiveBuySellLimitLevels
                                                                    .ItemChanged
                                                                    .Where(buySellPropsFilter)
                                                                    .Sample(bsThrottleTimeSpan)
                                                                    //.Do(_ => Log = new Exception(new { Name = "startBuySellCloseLevelsTracking", _.PropertyName, Value = _.Value + "" } + ""))
                                                                    .Select(_ => _.Sender.IsBuy ? "Buy(Close)Level" : "Sell(Close)Level")
                                                                    .Merge(this.WhenAny(tm => tm.CurrentPrice, tm => "CurrentPrice").Sample(cpThrottleTimeSpan))
                                                                    .Merge(this.WhenAny(tm => tm.CanDoEntryOrders, tm => "CanDoEntryOrders"))
                                                                    .Merge(this.WhenAny(tm => tm.CanDoNetStopOrders, tm => "CanDoNetStopOrders"))
                                                                    .Merge(this.WhenAny(tm => tm.IsTrader, tm => "IsTrader"))
                                                                    .Subscribe(_ => {
                    if (CanDoNetLimitOrders)
                    {
                        updateTradeLimitOrders();
                    }
                    else
                    {
                        CloseAllNetLimits();
                    }
                });
            };
            // New Stop
            Action startBuySellCloseStopTracking = () => {
                _reactiveBuySellStopLevels = new[] { BuyCloseLevel, SellCloseLevel, BuyLevel, SellLevel }
                .CreateDerivedCollection(sr => sr);
                _reactiveBuySellStopLevels.ChangeTrackingEnabled = true;
                _reactiveBuySellCloseStopSubscribtion            = (CompositeDisposable)_reactiveBuySellStopLevels
                                                                   .ItemChanged
                                                                   .Where(buySellPropsFilter)
                                                                   .Sample(bsThrottleTimeSpan)
                                                                   //.Do(_ => Log = new Exception(new { Name = "startBuySellCloseLevelsTracking", _.PropertyName, Value = _.Value + "" } + ""))
                                                                   .Select(_ => _.Sender.IsBuy ? "Buy(Close)Level" : "Sell(Close)Level")
                                                                   .Merge(this.WhenAny(tm => tm.CurrentPrice, tm => "CurrentPrice").Sample(cpThrottleTimeSpan))
                                                                   .Merge(this.WhenAny(tm => tm.CanDoEntryOrders, tm => "CanDoEntryOrders"))
                                                                   .Merge(this.WhenAny(tm => tm.CanDoNetStopOrders, tm => "CanDoNetStopOrders"))
                                                                   .Merge(this.WhenAny(tm => tm.IsTrader, tm => "IsTrader"))
                                                                   .Subscribe(_ => {
                    if (CanDoNetStopOrders)
                    {
                        updateTradeStopOrders();
                    }
                    else
                    {
                        CloseAllNetStops();
                    }
                });
            };
            #endregion

            #region Init BuySellLevels
            this.WhenAny(tm => tm.Strategy
                         , tm => tm.TrailingDistanceFunction
                         , tm => tm.HasBuyLevel
                         , tm => tm.HasSellLevel
                         , tm => tm.CanDoEntryOrders
                         , tm => tm.CanDoNetLimitOrders
                         , tm => tm.MustStopTrading
                         , (s, t, eo, no, ta, bl, sl) =>
                         Strategy == Strategies.Universal && HasBuyLevel && HasSellLevel && CanDoEntryOrders && !MustStopTrading && !IsInVirtualTrading
                         )
            .DistinctUntilChanged()
            .Sample(bsThrottleTimeSpan)
            .Subscribe(st => { // Turn on/off live entry orders
                try {
                    if (st)    // Subscribe to events in order to update live entry orders
                    //Log = new Exception("startBuySellLevelsTracking");
                    {
                        startBuySellLevelsTracking();
                    }
                    else if (_reactiveBuySellLevelsSubscribtion != null)
                    {
                        try {
                            GetEntryOrders().ToList().ForEach(order => OnDeletingOrder(order.OrderID));
                        } catch (Exception exc) { Log = exc; }
                        CleanReactiveBuySell(ref _reactiveBuySellLevelsSubscribtion, ref _reactiveBuySellLevels);
                    }
                } catch (Exception exc) { Log = exc; }
            });
            #endregion
            #region Init BuySellCloseLevels
            //this.WhenAny(
            //    tm => tm.BuyCloseLevel
            //  , tm => tm.SellCloseLevel
            //  , tm => tm.CanDoNetLimitOrders
            //  , tm => tm.CanDoNetStopOrders
            //  , tm => tm.CanDoEntryOrders
            //  , (b, s, non, nos, eo) =>
            //    BuyCloseLevel != null && SellCloseLevel != null && CanDoNetOrders && !IsInVitualTrading)
            //    .DistinctUntilChanged()
            //    .Sample(bsThrottleTimeSpan)
            //    .Subscribe(st => {// Turn on/off live net orders
            //      try {
            //        CleanReactiveBuySell(ref _reactiveBuySellCloseLevelsSubscribtion, ref _reactiveBuySellCloseLevels);
            //        if (!CanDoNetLimitOrders) CloseAllNetLimits();
            //        if (!CanDoNetStopOrders) CloseAllNetStops();
            //        if (st) {// (Re)Subscribe to events in order to update live net orders
            //          Log = new Exception("startBuySellCloseLevelsTracking");
            //          startBuySellCloseLevelsTracking();
            //        }
            //      } catch (Exception exc) { Log = exc; }
            //    });
            // New Limit
            this.WhenAny(
                tm => tm.HasBuyCloseLevel
                , tm => tm.HasSellCloseLevel
                , tm => tm.CanDoNetLimitOrders
                , (b, s, non) =>
                HasBuyCloseLevel && HasSellCloseLevel && CanDoNetLimitOrders && !IsInVirtualTrading)
            .DistinctUntilChanged()
            .Sample(bsThrottleTimeSpan)
            .Subscribe(st => {// Turn on/off live net orders
                try {
                    CleanReactiveBuySell(ref _reactiveBuySellCloseLimitSubscribtion, ref _reactiveBuySellLimitLevels);
                    if (!CanDoNetLimitOrders)
                    {
                        //Log = new Exception("Stop Limit Tracking");
                        CloseAllNetLimits();
                    }
                    if (st)// (Re)Subscribe to events in order to update live net orders
                    //Log = new Exception("Start Limit Tracking");
                    {
                        startBuySellCloseLimitTracking();
                    }
                } catch (Exception exc) { Log = exc; }
            });
            // Net Stop
            this.WhenAny(
                tm => tm.HasBuyCloseLevel
                , tm => tm.HasSellCloseLevel
                , tm => tm.CanDoNetStopOrders
                , (b, s, non) =>
                HasBuyCloseLevel && HasSellCloseLevel && CanDoNetStopOrders && !IsInVirtualTrading)
            .DistinctUntilChanged()
            .Sample(bsThrottleTimeSpan)
            .Subscribe(st => {// Turn on/off live net orders
                try {
                    CleanReactiveBuySell(ref _reactiveBuySellCloseStopSubscribtion, ref _reactiveBuySellStopLevels);
                    if (!CanDoNetStopOrders)
                    {
                        //Log = new Exception("Stop Stop Tracking");
                        CloseAllNetStops();
                    }
                    if (st)// (Re)Subscribe to events in order to update live net orders
                    //Log = new Exception("Start Stop Tracking");
                    {
                        startBuySellCloseStopTracking();
                    }
                } catch (Exception exc) { Log = exc; }
            });
            #endregion
        }
예제 #5
0
        private void ScanForWaveRanges2(List <Rate> rates)
        {
            try {
                #region Average Wave Height Calc
                var makeWaves = MonoidsCore.ToFunc((List <Rate>)null, 0.0, 0, (rateses, period, cmaPasses) => {
                    List <WaveRange> wr = GetWaveRanges(rateses, period, cmaPasses);

                    #region Split First Wave
                    var splitIndex = wr.Take(1)
                                     .Where(w => !w.IsEmpty)
                                     .Select(w => w.Range.IndexOf((w.Slope > 0
              ? w.Range.MaxBy(r => r.AskHigh)
              : w.Range.MinBy(r => r.AskLow)
                                                                   ).First()))
                                     .DefaultIfEmpty(-1)
                                     .First();
                    WaveRange wTail = new WaveRange(0);
                    wr.Take(1)
                    .Where(w => !w.IsEmpty)
                    .ToArray()
                    .Select(w => w.Count)
                    .Where(wrCount => splitIndex.Div(wrCount) > .1)
                    .Select(wrCount => {
                        var range     = wr[0].Range;
                        var wr0       = range.GetRange(0, splitIndex + 1).ToList();
                        var rangeTail = range.GetRange(splitIndex + 1, range.Count - (splitIndex + 1));
                        if (rangeTail.Any() && rangeTail.Last().StartDate.Subtract(rangeTail[0].StartDate).TotalSeconds > 3)
                        {
                            return new { wr0, wrTail = new WaveRange(rangeTail, PointSize, BarPeriod)
                                         {
                                             IsTail = true
                                         } }
                        }
                        ;
                        return(null);
                    })
                    .Where(x => x != null)
                    .ForEach(x => {
                        wr    = new[] { x.wr0 }.Select(w => new WaveRange(w, PointSize, BarPeriod)).Concat(wr.Skip(1)).ToList();
                        wTail = x.wrTail;
                    });
                    if (wTail.Count < 3 || wTail.TotalSeconds < 3)
                    {
                        wTail = new WaveRange();
                    }
                    #endregion

                    #region Wave Stats
                    #region Stats Funcs
                    Func <IList <WaveRange>, Func <WaveRange, double>, double> summ = (wrs0, v) => wrs0.Select(v).DefaultIfEmpty(double.NaN).Sum();
                    Func <IList <WaveRange>, Func <WaveRange, double>, double> avg  = (wrs0, v) =>
                                                                                      summ(wrs0, w => v(w) * w.HSDRatio) / summ(wrs0, w => w.HSDRatio);
                    Func <IList <WaveRange>, Func <WaveRange, double>, Func <WaveRange, double>, double> avg2 = (wrs0, v, d) =>
                                                                                                                summ(wrs0, w => v(w) * d(w)) / summ(wrs0, w => d(w));
                    Func <Func <WaveRange, double>, double> avgUp = value => wr.Select(value).DefaultIfEmpty().ToArray().AverageByAverageUp();
                    Func <IList <WaveRange>, Func <WaveRange, double>, double> avgStd = (wrs0, v) => {
                        var sd = wrs0.StandardDeviation(v) * 2;
                        var a  = wrs0.Average(v);
                        return(wrs0.Select(w => v(w).Abs()).Where(d => d.Between(a - sd, a + sd)).Average());
                    };
                    Func <Func <WaveRange, double>, double> rsd = value => wr.Select(value).DefaultIfEmpty().Sum();
                    #endregion

                    var wrs = wr.SkipLast(wr.Count > 4 ? 1 : 0).Where(w => !w.Distance.IsNaNOrZero()).ToArray();
                    Func <Func <WaveRange, double>, double, double> pwmp = (w, power) => wrs.Select(w).DefaultIfEmpty().RootMeanPower(power);


                    var hasCalm3        = TradeConditionsHave(Calm3Ok);
                    var TrendHeightPerc = 2.0;
                    var ws = new WaveRange(1)
                    {
                        Distance             = pwmp(w => w.Distance, 1 / TrendHeightPerc),
                        DistanceCma          = avg2(wrs, w => w.DistanceCma, w => w.Distance),
                        DistanceByRegression = avg2(wrs, w => w.DistanceByRegression, w => w.Distance),
                        WorkByHeight         = rsd(w => w.WorkByHeight),
                        WorkByTime           = rsd(w => w.WorkByTime),
                        Angle        = hasCalm3 ? pwmp(w => w.Angle.Abs(), 1 / TrendHeightPerc) : wrs.Select(w => w.Angle.Abs()).RelativeStandardDeviation().ToPercent(),
                        TotalMinutes = pwmp(w => w.TotalMinutes, 1 / TrendHeightPerc),
                        HSDRatio     = wrs.Select(w => w.StDev).RelativeStandardDeviation().ToPercent(),//avg2(wrs, w => w.HSDRatio, w => 1 / w.Distance),
                        Height       = rsd(w => w.Height),
                        StDev        = wrs.Select(w => w.StDev).RootMeanPowerByPosition(WaveStDevPowerS)
                    };
                    ws.PipsPerMinute = ws.Distance / ws.TotalMinutes;


                    var wa = new WaveRange(1)
                    {
                        DistanceCma          = avg2(wrs, w => w.DistanceCma, w => 1 / Math.Pow(w.Distance, 1 / 3.0)),
                        DistanceByRegression = avg2(wrs, w => w.DistanceByRegression, w => 1 / Math.Pow(w.Distance, 1 / 3.0)),
                        WorkByHeight         = avg(wrs, w => w.WorkByHeight),
                        WorkByTime           = avg(wrs, w => w.WorkByTime),
                        HSDRatio             = avg2(wrs, w => w.HSDRatio, w => w.Distance),
                        Height = avg(wrs, w => w.Height),
                        StDev  = wrs.Select(w => w.StDev).RootMeanPowerByPosition(WaveStDevPower)
                    };
                    try {
                        wa.Distance      = pwmp(w => w.Distance, TrendHeightPerc);
                        wa.Angle         = pwmp(w => w.Angle.Abs(), TrendHeightPerc);
                        wa.TotalMinutes  = pwmp(w => w.TotalMinutes, TrendHeightPerc);
                        wa.PipsPerMinute = wa.Distance / wa.TotalMinutes;
                    } catch (Exception exc) {
                        Log = exc;
                        return(null);
                    }
                    #endregion
                    #region Conditions
                    wr.ForEach(w => w.IsFatnessOk     = w.StDev <= wa.StDev);
                    wr.ForEach(w => w.IsDistanceCmaOk = w.DistanceCma >= wa.DistanceCma);
                    #endregion

                    return(new { wr, wTail, wa, ws });
                });

                #endregion

                if (!IsCorridorFrozen())
                {
                    var wrwt = makeWaves(rates, PriceCmaLevels, CmaPasses);
                    if (wrwt == null)
                    {
                        return;
                    }
                    WaveRanges    = wrwt.wr;
                    WaveRangeTail = wrwt.wTail;
                    WaveRangeSum  = wrwt.ws;
                    WaveRangeAvg  = wrwt.wa;

                    #region Adjust CmaPasses
                    Action setCmaPasses = () => {
                        try {
                            Func <WaveRange, double> stDever = WaveSmoothFunc();

                            var makeWaveses = MonoidsCore.ToFunc((IEnumerable <int>)null, ups =>
                                                                 Partitioner.Create(ups.ToArray(), true)
                                                                 .AsParallel().Select(cmaPasses => new { sd = stDever(makeWaves(rates, PriceCmaLevels, cmaPasses).ws), cmaPasses }));

                            var up          = makeWaveses(Lib.IteratonSequence(1, 600, i => i.Div(200).Ceiling())).OrderBy(x => x.cmaPasses).ToList();
                            var bufferCount = rates.Count.Div(100).ToInt();
                            var cma         = Enumerable.Range(0, up.Count - bufferCount)
                                              .Select(i => up.GetRange(i, bufferCount).AsIList())
                                              .Where(b => b.Count == bufferCount)
                                              .Select(b => new { b, avg = b.Average(y => y.sd) })
                                              .Aggregate((b1, b2) => b1.avg < b2.avg ? b1 : b2)
                                              .b.MinBy(x => x.sd)
                                              .OrderBy(x => x.cmaPasses)
                                              .First().cmaPasses;

                            CmaPassesCalc = cma;
                            CmaPasses     = CmaPassesCalc;
                        } catch (Exception exc) {
                            Log = exc;
                        }
                    };
                    if (CmaPassesMin > 0)
                    {
                        //setCmaPasses();
                        _addHistoryOrdersBuffer.Push(() => {
                            var sw = Stopwatch.StartNew();
                            setCmaPasses();
                            Log = new Exception(new { CmaPassesCalc, CmaPasses, sw.ElapsedMilliseconds } +"");
                        });
                    }
                    #endregion
                }
                else
                {
                    var firstWaveRange = WaveRanges.Take(1).Select(wr =>
                                                                   rates.BackwardsIterator().TakeWhile(r => r.StartDate >= wr.StartDate).Reverse().ToList());
                    WaveRanges    = firstWaveRange.Select(wr => new WaveRange(wr, PointSize, BarPeriod)).Concat(WaveRanges.Skip(1)).ToList();
                    WaveRangeTail = new WaveRange(0);
                }

                Func <WaveRange, double> bfp = w => w.DistanceByRegression;// WaveRangeAvg.BestFitProp();
                WaveFirstSecondRatio = WaveRanges.Take(1).Select(w1 => bfp(w1) / bfp(WaveRangeAvg)).FirstOrDefault();

                WaveHeightAverage = WaveRangeAvg.Height;
                WaveHeightPower   = new[] {
                    WaveRangeAvg.WorkByHeight,
                    WaveRangeAvg.Height,
                    WaveRangeAvg.DistanceByRegression
                }.StandardDeviation();
            } catch (Exception exc) {
                Log = exc;
            }
        }
예제 #6
0
        public TradingMacro()
        {
            GroupRates = MonoidsCore.ToFunc((IList <Rate> rates) => GroupRatesImpl(rates, GroupRatesCount)).MemoizeLast(r => r.Last().StartDate);
            this.ObservableForProperty(tm => tm.Pair, false, false)
            .Where(_ => !IsInVirtualTrading)
            .Select(oc => oc.GetValue())
            .Scan((prev: "", curr: ""), (prev, curr) => (prev.curr, curr))
            .Where(pair => !pair.curr.IsNullOrWhiteSpace())
            .Throttle(1.FromSeconds())
            .ObserveOn(Application.Current?.Dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher)
            .Subscribe(oc => {
                _inPips       = null;
                _pointSize    = double.NaN;
                _BaseUnitSize = 0;
                _mmr          = 0;
                LoadActiveSettings();
                _Rates.Clear();
                if (!oc.prev.IsNullOrWhiteSpace() && TradesManager != null)
                {
                    TradesManager.CoreFX.SetSymbolSubscription(Pair);
                    OnLoadRates();
                }
                _pendingEntryOrders = null;
                OnPropertyChanged(nameof(CompositeName));
                SubscribeToEntryOrderRelatedEvents();
                TradingMacrosByPair(oc.prev).Where(tm => tm.BarPeriod > BarsPeriodType.t1)
                .ForEach(tm => tm.Pair = oc.curr);
            });

            this.WhenAnyValue(
                tm => tm.CorridorSDRatio,
                tm => tm.IsRatesLengthStable,
                tm => tm.TrendBlue,
                tm => tm.TrendRed,
                tm => tm.TrendPlum,
                tm => tm.TrendGreen,
                tm => tm.TrendLime,
                tm => tm.TimeFrameTreshold,
                tm => tm.CorridorCalcMethod,
                (v1, rls, v3, v4, v5, v6, v7, v8, v9) => new { v1, rls, v3, v4, v5, v6, v7, v8, v9 }
                )
            .Where(x => !IsAsleep && x.rls)
            .Subscribe(_ => {
                _mustResetAllTrendLevels = true;
                OnScanCorridor(RatesArray, () => { }, false);
            });
            this.WhenAnyValue(
                tm => tm.RatesMinutesMin,
                tm => tm.BarsCountMax,
                (rmm, bcm) => new { rmm, bcm }
                ).Subscribe(_ => UseRatesInternal(ri => ri.SideEffect(__ => { Log = new Exception($"{Pair}: InternalRates cleared."); }).Clear()));
            this.WhenAnyValue(
                tm => tm.RatesMinutesMin,
                tm => tm.BarsCount,
                tm => tm.BarsCountMax,
                tm => tm.PairHedge,
                tm => tm.RatesLengthBy,
                tm => tm.HedgeCorrelation,
                (v1, rls, v3, ph, rlb, hc) => true).Subscribe(_ => SyncHedgedPair());
            this.WhenAnyValue(
                tm => tm.PairHedge
                )
            .Subscribe(_ => TradingMacrosByPair(tm => tm != this).ForEach(tm => tm.PairHedge = _));

            _newsCaster.CountdownSubject
            .Where(nc => IsActive && Strategy != Strategies.None && nc.AutoTrade && nc.Countdown <= _newsCaster.AutoTradeOffset)
            .Subscribe(nc => {
                try {
                    if (!RatesArray.Any())
                    {
                        return;
                    }
                    var height = CorridorStats.StDevByHeight;
                    if (CurrentPrice.Average > MagnetPrice)
                    {
                        BuyLevel.Rate  = MagnetPrice + height;
                        SellLevel.Rate = MagnetPrice;
                    }
                    else
                    {
                        BuyLevel.Rate  = MagnetPrice;
                        SellLevel.Rate = MagnetPrice - height;
                    }
                    new[] { BuyLevel, SellLevel }.ForEach(sr => {
                        sr.ResetPricePosition();
                        sr.CanTrade = true;
                        //sr.InManual = true;
                    });
                    DispatcherScheduler.Current.Schedule(5.FromSeconds(), () => nc.AutoTrade = false);
                } catch (Exception exc) { Log = exc; }
            });
            _waveShort = new WaveInfo(this);
            WaveShort.DistanceChanged += (s, e) => {
                OnPropertyChanged(() => WaveShortDistance);
                OnPropertyChanged(() => WaveShortDistanceInPips);
                _broadcastCorridorDateChanged();
            };
            //SuppRes.AssociationChanged += new CollectionChangeEventHandler(SuppRes_AssociationChanged);
            GalaSoft.MvvmLight.Messaging.Messenger.Default.Register <RequestPairForHistoryMessage>(this
                                                                                                   , a => {
                Debugger.Break();
                a.Pairs.Add(new Tuple <string, int>(this.Pair, this.BarPeriodInt));
            });
            GalaSoft.MvvmLight.Messaging.Messenger.Default.Register <CloseAllTradesMessage <TradingMacro> >(this, a => {
                if (a.Sender.YieldNotNull().Any(tm => tm.Pair == Pair))
                {
                    return;
                }
                if (IsActive && TradesManager != null)
                {
                    if (Trades.Any())
                    {
                        CloseTrading("CloseAllTradesMessage sent by " + a.Sender.Pair);
                    }
                    a.OnClose(this);
                }
            });
            GalaSoft.MvvmLight.Messaging.Messenger.Default.Register <TradeLineChangedMessage>(this, a => {
                if (a.Target == this && _strategyOnTradeLineChanged != null)
                {
                    _strategyOnTradeLineChanged(a);
                }
            });
            GalaSoft.MvvmLight.Messaging.Messenger.Default.Register <ShowSnapshotMatchMessage>(this, m => {
                if (SnapshotArguments.IsTarget && !m.StopPropagation)
                {
                    m.StopPropagation           = true;
                    SnapshotArguments.DateStart = m.DateStart;
                    SnapshotArguments.DateEnd   = null;
                    SnapshotArguments.IsTarget  = false;
                    SnapshotArguments.Label     = m.Correlation.ToString("n2");
                    //if (BarsCount != m.BarCount) BarsCount = m.BarCount;
                    if (BarPeriodInt != m.BarPeriod)
                    {
                        BarPeriod = (BarsPeriodType)m.BarPeriod;
                    }
                    UseRatesInternal(ri => ri.Clear());
                    RatesArray.Clear();
                    CorridorStartDate = null;
                    ShowSnaphot(m.DateStart, m.DateEnd);
                    Scheduler.Default.Schedule(1.FromSeconds(), () => {
                        try {
                            CorridorStartDate = m.DateStart;
                            CorridorStopDate  = DateTime.MinValue;// RatesArray.SkipWhile(r => r.StartDate < CorridorStartDate).Skip(m.DateEnd - 1).First().StartDate;
                        } catch (Exception exc) {
                            Log = exc;
                        }
                    });
                    Scheduler.Default.Schedule(10.FromSeconds(), () => SnapshotArguments.IsTarget = true);
                }
            });
            //MessageBus.Current.Listen<AppExitMessage>().Subscribe(_ => SaveActiveSettings());
        }
예제 #7
0
        private void StrategyEnterUniversal()
        {
            if (!RatesArray.Any() || !IsTrader)
            {
                return;
            }

            #region ============ Local globals ============
            #region Loss/Gross
            Func <double> currentGrossInPips = () => CurrentGrossInPipTotal;
            Func <double> currentLoss        = () => CurrentLoss;
            Func <double> currentGross       = () => CurrentGross;
            Func <bool>   isCurrentGrossOk   = () => CurrentGrossInPips >= -SpreadForCorridorInPips;
            #endregion
            var reverseStrategy          = new ObservableValue <bool>(false);
            Func <SuppRes, bool> isBuyR  = sr => reverseStrategy.Value ? !sr.IsBuy : sr.IsBuy;
            Func <SuppRes, bool> isSellR = sr => reverseStrategy.Value ? !sr.IsSell : sr.IsSell;
            Action resetCloseAndTrim     = () => CloseAtZero = false;
            Func <bool, double> enter    = isBuy => CalculateLastPrice(RateLast, GetTradeEnterBy(isBuy));
            #endregion

            #region ============ Init =================
            bool _useSms = false;
            if (_strategyExecuteOnTradeClose == null)
            {
                Func <SuppRes, IObservable <EventPattern <EventArgs> > > onCanTrade = sr => Observable.FromEventPattern <EventHandler <EventArgs>, EventArgs>(h => h, h => sr.CanTradeChanged += h, h => sr.CanTradeChanged -= h);
                if (IsInVirtualTrading && Trades.Any())
                {
                    throw new Exception("StrategyEnterUniversal: All trades must be closed befor strategy init.");
                }
                if (IsInVirtualTrading)
                {
                    TurnOffSuppRes(RatesArray.Select(r => r.PriceAvg).DefaultIfEmpty().Average());
                }
                Func <bool, Func <Rate, double> > tradeExit = isBuy => MustExitOnReverse ? _priceAvg : GetTradeExitBy(isBuy);
                Action onEOW = () => { };
                #region Exit Funcs
                #region exitOnFriday
                Func <bool> exitOnFriday = () => {
                    if (!SuppRes.Any(sr => sr.InManual))
                    {
                        bool isEOW = IsAutoStrategy && IsEndOfWeek();
                        if (isEOW)
                        {
                            if (Trades.Any())
                            {
                                CloseTrades(Trades.Lots(), "exitOnFriday");
                            }
                            BuyLevel.CanTrade = SellLevel.CanTrade = false;
                            return(true);
                        }
                    }
                    return(false);
                };
                Func <bool?> exitOnCorridorTouch = () => {
                    var mustSell    = false;
                    var mustBuy     = false;
                    var currentBuy  = CalculateLastPrice(_priceAvg);
                    var currentSell = currentBuy;
                    if (currentBuy >= RateLast.PriceAvg2)
                    {
                        mustSell = true;
                    }
                    if (currentSell <= RateLast.PriceAvg3)
                    {
                        mustBuy = true;
                    }
                    if (Trades.HaveBuy() && mustSell || Trades.HaveSell() && mustBuy)
                    {
                        MustExitOnReverse = true;
                    }
                    return(mustBuy ? mustBuy : mustSell?false : (bool?)null);
                };
                #endregion
                #region exitByLimit
                Action exitByLimit = () => {
                    if (!exitOnFriday() && CurrentGross > 0 && TradesManager.MoneyAndLotToPips(OpenTradesGross, Trades.Lots(), Pair) >= CalculateTakeProfitInPips() * ProfitToLossExitRatio)
                    {
                        CloseAtZero = true;
                    }
                };
                #endregion

                //TradesManager.MoneyAndLotToPips(-currentGross(), LotSizeByLossBuy, Pair)
                #region exitVoid
                Action exitVoid = () => { };
                #endregion
                #region exitFunc
                Func <Action> exitFunc = () => {
                    if (!Trades.Any())
                    {
                        return () => { }
                    }
                    ;
                    switch (ExitFunction)
                    {
                    case Store.ExitFunctions.Void:
                        return(exitVoid);

                    case Store.ExitFunctions.Friday:
                        return(() => exitOnFriday());

                    case Store.ExitFunctions.Limit:
                        return(exitByLimit);

                    case Store.ExitFunctions.CorrTouch:
                        return(() => exitOnCorridorTouch());
                    }
                    throw new NotSupportedException(ExitFunction + " exit function is not supported.");
                };
                #endregion
                #endregion
                #region TurnOff Funcs
                Action <double, Action> turnOffIfCorridorInMiddle_ = (sections, a) => {
                    var segment    = RatesHeight / sections;
                    var rest       = (RatesHeight - segment) / 2;
                    var bottom     = _RatesMin + rest;
                    var top        = _RatesMax - rest;
                    var tradeLevel = (BuyLevel.Rate + SellLevel.Rate) / 2;
                    if ((BuyLevel.CanTrade || SellLevel.CanTrade) && IsAutoStrategy && tradeLevel.Between(bottom, top))
                    {
                        a();
                    }
                };
                Action <Action> turnOffByCrossCount = a => { if (BuyLevel.TradesCount.Min(SellLevel.TradesCount) < TradeCountMax)
                                                             {
                                                                 a();
                                                             }
                };
                Action <Action> turnOffByWaveHeight = a => { if (WaveShort.RatesHeight < RatesHeight * .75)
                                                             {
                                                                 a();
                                                             }
                };
                Action <Action> turnOffByWaveShortLeft = a => { if (WaveShort.Rates.Count < WaveShortLeft.Rates.Count)
                                                                {
                                                                    a();
                                                                }
                };
                Action <Action> turnOffByWaveShortAndLeft = a => { if (WaveShortLeft.Rates.Count < CorridorDistanceRatio && WaveShort.Rates.Count < WaveShortLeft.Rates.Count)
                                                                   {
                                                                       a();
                                                                   }
                };
                Action <Action> turnOff = a => {
                    switch (TurnOffFunction)
                    {
                    case Store.TurnOffFunctions.Void:
                        return;

                    case Store.TurnOffFunctions.WaveHeight:
                        turnOffByWaveHeight(a);
                        return;

                    case Store.TurnOffFunctions.WaveShortLeft:
                        turnOffByWaveShortLeft(a);
                        return;

                    case Store.TurnOffFunctions.WaveShortAndLeft:
                        turnOffByWaveShortAndLeft(a);
                        return;

                    case Store.TurnOffFunctions.InMiddle_4:
                        turnOffIfCorridorInMiddle_(4, a);
                        return;

                    case Store.TurnOffFunctions.InMiddle_5:
                        turnOffIfCorridorInMiddle_(5, a);
                        return;

                    case Store.TurnOffFunctions.InMiddle_6:
                        turnOffIfCorridorInMiddle_(6, a);
                        return;

                    case Store.TurnOffFunctions.InMiddle_7:
                        turnOffIfCorridorInMiddle_(7, a);
                        return;

                    case Store.TurnOffFunctions.CrossCount:
                        turnOffByCrossCount(a);
                        return;
                    }
                    throw new NotSupportedException(TurnOffFunction + " Turnoff function is not supported.");
                };
                #endregion
                if (_adjustEnterLevels != null)
                {
                    _adjustEnterLevels.GetInvocationList().Cast <Action>().ForEach(d => _adjustEnterLevels -= d);
                }
                if (BuyLevel != null)
                {
                    BuyLevel.Crossed -= null;
                }
                if (SellLevel != null)
                {
                    SellLevel.Crossed -= null;
                }
                Action <Trade> onCloseTradeLocal = null;
                Action <Trade> onOpenTradeLocal  = null;

                #region Levels
                if (SuppResLevelsCount < 2)
                {
                    SuppResLevelsCount = 2;
                }
                if (SuppRes.Do(sr => sr.IsExitOnly = false).Count() == 0)
                {
                    return;
                }
                ;

                if (!IsInVirtualTrading)
                {
                    var buySellCanTradeObservable = onCanTrade(BuyLevel).Merge(onCanTrade(SellLevel))
                                                    .Select(e => e.Sender as SuppRes)
                                                    .DistinctUntilChanged(sr => sr.CanTrade)
                                                    .Where(sr => _useSms && sr.CanTrade)
                                                    .Subscribe(sr => SendSms(Pair + "::", new { sr.CanTrade }, false));
                }
                if (BuyLevel.Rate.Min(SellLevel.Rate) == 0)
                {
                    BuyLevel.RateEx = SellLevel.RateEx = RatesArray.Middle();
                }
                BuyLevel.CanTrade = SellLevel.CanTrade = false;
                var                        _buySellLevels = new[] { BuyLevel, SellLevel }.ToList();
                Action <Action>            onCorridorCrossesMaximumExeeded = a => _buySellLevels.Where(bs => - bs.TradesCount >= TradeCountMax).Take(1).ForEach(_ => a());
                ObservableValue <double>   ghostLevelOffset                = new ObservableValue <double>(0);
                Action <Action <SuppRes> > _buySellLevelsForEach           = a => _buySellLevels.ForEach(sr => a(sr));
                Action <Func <SuppRes, bool>, Action <SuppRes> > _buySellLevelsForEachWhere = (where, a) => _buySellLevels.Where(where).ToList().ForEach(sr => a(sr));
                _buySellLevelsForEach(sr => sr.ResetPricePosition());
                Action <SuppRes, bool> setCloseLevel = (sr, overWrite) => {
                    if (!overWrite && sr.InManual)
                    {
                        return;
                    }
                    sr.InManual = false;
                    sr.CanTrade = false;
                    if (sr.TradesCount != 9)
                    {
                        sr.TradesCount = 9;
                    }
                };
                Func <SuppRes, SuppRes> suppResNearest = supRes => _suppResesForBulk().Where(sr => sr.IsSupport != supRes.IsSupport).OrderBy(sr => (sr.Rate - supRes.Rate).Abs()).First();
                Action <bool>           setCloseLevels = (overWrite) => setCloseLevel(BuyCloseLevel, overWrite);
                setCloseLevels += (overWrite) => setCloseLevel(SellCloseLevel, overWrite);
                ForEachSuppRes(sr => {
                    if (IsInVirtualTrading)
                    {
                        sr.InManual = false;
                    }
                    sr.ResetPricePosition();
                    sr.ClearCrossedHandlers();
                });
                setCloseLevels(true);
                #region updateTradeCount
                Action <SuppRes, SuppRes> updateTradeCount = (supRes, other) => {
                    if (supRes.TradesCount <= other.TradesCount && new[] { supRes, other }.Any(sr => sr.CanTrade))
                    {
                        other.TradesCount = supRes.TradesCount - 1;
                    }
                };
                Func <SuppRes, SuppRes> updateNeares = supRes => {
                    var other = suppResNearest(supRes);
                    updateTradeCount(supRes, other);
                    return(other);
                };
                #endregion
                Func <bool, bool>    onCanTradeLocal = canTrade => canTrade;
                Func <SuppRes, bool> canTradeLocal   = sr => {
                    var ratio      = CanTradeLocalRatio;
                    var corr       = BuyLevel.Rate.Abs(SellLevel.Rate);
                    var tradeDist  = (sr.IsBuy ? (CurrentPrice.Ask - BuyLevel.Rate) : (SellLevel.Rate - CurrentPrice.Bid));
                    var tradeRange = ((sr.IsBuy ? (CurrentPrice.Ask - SellLevel.Rate) : (BuyLevel.Rate - CurrentPrice.Bid))) / corr;
                    var canTrade   = IsPrimaryMacro && (tradeRange < ratio || tradeDist <= PriceSpreadAverage);
                    //if (!canTrade)
                    //Log = new Exception(canTrade + "");
                    return(onCanTradeLocal(canTrade));
                };
                Func <bool>          isTradingHourLocal = () => IsTradingHour() && IsTradingDay();
                Func <bool>          isLastRateHeightOk = () => RateLast.Yield().All(rl => (rl.AskHigh - rl.BidLow) / RatesHeight < 0.05);
                Func <SuppRes, bool> suppResCanTrade    = (sr) =>
                                                          !CanDoEntryOrders && IsTradingActive &&
                                                          isTradingHourLocal() &&
                                                          sr.CanTrade &&
                                                          sr.TradesCount <= 0 &&
                                                          canTradeLocal(sr) &&
                                                          IsPriceSpreadOk &&
                                                          !IsEndOfWeek();
                Func <bool> isProfitOk = () => Trades.HaveBuy() && RateLast.BidHigh > BuyCloseLevel.Rate ||
                                         Trades.HaveSell() && RateLast.AskLow < SellCloseLevel.Rate;
                #endregion

                #region SuppRes Event Handlers
                Func <bool>          isCrossActive   = () => _buySellLevels.Any(sr => !sr.CanTrade) || BuyLevel.Rate.Abs(SellLevel.Rate) > InPoints(1);
                Func <SuppRes, bool> isCrossDisabled = sr =>
                                                       //!isCrossActive() ||
                                                       !IsTradingActive ||
                                                       !IsPrimaryMacro ||
                                                       (!sr.IsExitOnly && !sr.InManual && !CanOpenTradeByDirection(sr.IsBuy));
                Action <double> onTradesCount = tc => { };
                if (_strategyTradesCountHandler == null)
                {
                    _strategyTradesCountHandler = BuyLevel.WhenAnyValue(x => x.TradesCount).Merge(SellLevel.WhenAnyValue(x => x.TradesCount))
                                                  .Throttle(TimeSpan.FromSeconds(.5))
                                                  .Subscribe(tc => onTradesCount(tc));
                }
                #region enterCrossHandler
                Func <SuppRes, bool> enterCrossHandler = (suppRes) => {
                    if (CanDoEntryOrders || CanDoNetStopOrders || (reverseStrategy.Value && !suppRes.CanTrade) || isCrossDisabled(suppRes) || HaveTrades(suppRes.IsBuy) || isHedgeChild || IsHedgedTrading)
                    {
                        return(false);
                    }
                    if (suppRes.InManual || BuyLevel.Rate > SellLevel.Rate || suppRes.CanTrade)
                    {
                        var isBuy    = isBuyR(suppRes);
                        var lot      = Trades.IsBuy(!isBuy).Lots();
                        var canTrade = suppResCanTrade(suppRes);
                        if (canTrade)
                        {
                            lot += AllowedLotSizeCore(isBuy);
                            suppRes.TradeDate = ServerTime;
                        }
                        //var ghost = SuppRes.SingleOrDefault(sr => sr.IsExitOnly && sr.IsBuy == isBuy && sr.InManual && sr.CanTrade && sr.TradesCount <= 0);
                        //if (ghost != null) {
                        //  var real = _buySellLevels.Single(sr => sr.IsBuy == isBuy);
                        //  if (real.IsBuy && real.Rate < ghost.Rate || real.IsSell && real.Rate > ghost.Rate)
                        //    real.Rate = ghost.Rate;
                        //}
                        OpenTrade(isBuy, lot, "enterCrossHandler:" + new { isBuy, suppRes.IsExitOnly });
                        return(canTrade);
                    }
                    else
                    {
                        return(false);
                    }
                };
                #endregion
                #region exitCrossHandler
                Action <SuppRes> exitCrossHandler = (sr) => {
                    if ((!IsInVirtualTrading && CanDoNetLimitOrders) || isCrossDisabled(sr) || IsHedgedTrading || isHedgeChild || HaveHedgedTrades())
                    {
                        return;
                    }
                    var lot = Trades.Lots();
                    resetCloseAndTrim();
                    if (TradingStatistics.TradingMacros.Distinct(tm => tm.Pair).Count() > 1 && (
                            CurrentGrossInPipTotal > PriceSpreadAverageInPips || CurrentGrossInPipTotal >= _tradingStatistics.GrossToExitInPips)
                        )
                    {
                        CloseTrading(new { exitCrossHandler = "", CurrentGrossInPipTotal, _tradingStatistics.GrossToExitInPips } +"");
                    }
                    else
                    {
                        CloseTrades(lot, "exitCrossHandler:" + new { sr.IsBuy, sr.IsExitOnly, CloseAtZero });
                    }
                };
                #endregion
                #endregion

                #region Crossed Events
                #region Enter Levels
                #region crossedEnter
                EventHandler <SuppRes.CrossedEvetArgs> crossedEnter = (s, e) => {
                    var sr = (SuppRes)s;
                    if (sr.IsBuy && e.Direction == -1 || sr.IsSell && e.Direction == 1)
                    {
                        return;
                    }
                    var srNearest = new Lazy <SuppRes>(() => suppResNearest(sr));
                    updateTradeCount(sr, srNearest.Value);
                    if (enterCrossHandler(sr))
                    {
                        setCloseLevels(true);
                    }
                };
                #endregion
                BuyLevel.Crossed  += crossedEnter;
                SellLevel.Crossed += crossedEnter;
                #endregion
                #region ExitLevels
                Action <SuppRes> handleActiveExitLevel = (srExit) => {
                    updateNeares(srExit);
                    if (enterCrossHandler(srExit))
                    {
                        setCloseLevels(true);
                    }
                };
                EventHandler <SuppRes.CrossedEvetArgs> crossedExit = (s, e) => {
                    var sr = (SuppRes)s;
                    //if (reverseStrategy.Value && Trades.Any(t => t.IsBuy != sr.IsSell)) {
                    //  exitCrossHandler(sr);
                    //  return;
                    //}
                    if (sr.IsBuy && e.Direction == -1 || sr.IsSell && e.Direction == 1)
                    {
                        return;
                    }
                    if (sr.CanTrade)
                    {
                        if (sr.InManual)
                        {
                            handleActiveExitLevel(sr);
                        }
                        else
                        {
                            crossedEnter(s, e);
                        }
                    }
                    else if (Trades.Any(t => t.IsBuy == sr.IsSell))
                    {
                        exitCrossHandler(sr);
                    }
                };
                BuyCloseLevel.Crossed  += crossedExit;
                SellCloseLevel.Crossed += crossedExit;
                #endregion
                #endregion

                #region adjustExitLevels
                Action <double, double> adjustExitLevels = AdjustCloseLevels();
                Action adjustExitLevels0 = () => adjustExitLevels(double.NaN, double.NaN);
                Action adjustExitLevels1 = () => {
                    if (DoAdjustExitLevelByTradeTime)
                    {
                        AdjustExitLevelsByTradeTime(adjustExitLevels);
                    }
                    else
                    {
                        adjustExitLevels(BuyLevel.Rate, SellLevel.Rate);
                    }
                };
                Action adjustExitLevels2 = () => {
                    if (DoAdjustExitLevelByTradeTime)
                    {
                        AdjustExitLevelsByTradeTime(adjustExitLevels);
                    }
                    else
                    {
                        adjustExitLevels0();
                    }
                };
                #endregion

                #region adjustLevels
                var firstTime = true;
                #region Watchers
                var watcherCanTrade = new ObservableValue <bool>(true, true);

                var corridorMovedTrigger = new ValueTrigger <ValueTrigger <bool> >(false)
                {
                    Value = new ValueTrigger <bool>(false)
                };
                var      dateTrigger = new ValueTrigger <DateTime>(false);
                object[] bag         = null;
                #region Workflow tuple factories
                Func <List <object> > emptyWFContext = () => new List <object>();
                Func <List <object>, Tuple <int, List <object> > > tupleNext       = e => Tuple.Create(1, e ?? new List <object>());
                Func <object, Tuple <int, List <object> > >        tupleNextSingle = e => tupleNext(new List <object> {
                    e
                });
                Func <Tuple <int, List <object> > > tupleNextEmpty                 = () => tupleNext(emptyWFContext());
                Func <List <object>, Tuple <int, List <object> > > tupleStay       = e => Tuple.Create(0, e ?? new List <object>());
                Func <object, Tuple <int, List <object> > >        tupleStaySingle = e => tupleStay(new[] { e }.ToList());
                Func <Tuple <int, List <object> > > tupleStayEmpty                 = () => tupleStay(emptyWFContext());
                Func <List <object>, Tuple <int, List <object> > > tuplePrev       = e => Tuple.Create(-1, e ?? new List <object>());
                Func <List <object>, Tuple <int, List <object> > > tupleCancel     = e => Tuple.Create(int.MaxValue / 2, e ?? new List <object>());
                Func <Tuple <int, List <object> > > tupleCancelEmpty               = () => tupleCancel(emptyWFContext());
                Func <IList <object>, Dictionary <string, object> > getWFDict      = l => l.OfType <Dictionary <string, object> >().SingleOrDefault();
                Func <IList <object>, Dictionary <string, object> > addWFDict      = l => { var d = new Dictionary <string, object>(); l.Add(d); return(d); };
                Func <IList <object>, Dictionary <string, object> > getAddWFDict   = l => getWFDict(l) ?? addWFDict(l);
                #endregion
                Func <bool> cancelWorkflow     = () => CloseAtZero;
                var         workflowSubject    = new Subject <IList <Func <List <object>, Tuple <int, List <object> > > > >();
                var         workFlowObservable = workflowSubject
                                                 .Scan(new { i = 0, o = emptyWFContext(), c = cancelWorkflow }, (i, wf) => {
                    if (i.i >= wf.Count || i.c() || i.o.OfType <WF.MustExit>().Any(me => me()))
                    {
                        i.o.OfType <WF.OnExit>().ForEach(a => a());
                        i.o.Clear();
                        i = new { i = 0, o = i.o, i.c };
                    }
                    var o = wf[i.i](i.o);// Side effect
                    o.Item2.OfType <WF.OnLoop>().ToList().ForEach(ol => ol(o.Item2));
                    try {
                        return(new { i = (i.i + o.Item1).Max(0), o = o.Item2, i.c });
                    } finally {
                        if (o.Item1 != 0)
                        {
                            workflowSubject.Repeat(1);
                        }
                    }
                });

                var workflowSubjectDynamic    = new Subject <IList <Func <ExpandoObject, Tuple <int, ExpandoObject> > > >();
                var workFlowObservableDynamic = workflowSubjectDynamic.WFFactory(cancelWorkflow);
                #endregion

                #region Funcs
                Func <Func <Rate, double>, double> getRateLast = (f) => f(RateLast) > 0 ? f(RateLast) : f(RatePrev);
                Func <bool> runOnce = null;
                #endregion

                #region adjustEnterLevels
                Func <Trade, bool> minPLOk         = t => IsAutoStrategy && false ? CurrentGrossInPips == 0 : t.NetPL2 > 0;
                Action <Action>    firstTimeAction = a => {
                    if (firstTime)
                    {
                        Log = new Exception(new { CorrelationMinimum, CorridorDistance } +"");
                        workFlowObservableDynamic.Subscribe();
                        #region onCloseTradeLocal
                        onCanTradeLocal    = canTrade => canTrade || Trades.Any();
                        onCloseTradeLocal += t => {
                            var tpByGross = InPoints(_tradingStatistics.GetNetInPips()).Min(0).Abs() / 3;
                            TakeProfitManual = (t.PL < 0 ? InPoints(t.PL.Abs() * 1.4).Max(TakeProfitManual) : double.NaN).Max(tpByGross);
                            TakeProfitManual = double.NaN;
                            BroadcastCloseAllTrades(this, tm => OnCloseTradeLocal(new[] { t }, tm));
                        };
                        #endregion
                        if (a != null)
                        {
                            a();
                        }
                    }
                };
                Action adjustEnterLevels = () => {
                    if (firstTime)
                    {
                        onOpenTradeLocal += t => { };
                    }
                    switch (TrailingDistanceFunction)
                    {
                        #region SimpleMove
                    case TrailingWaveMethod.SimpleMove: {
                        var conditions = MonoidsCore.ToFunc(() => new { TrailingDistanceFunction });
                        var tci_       = MonoidsCore.ToFunc(() => TradeConditionsInfo((d, p, n) => new { n, v = d(), d }).ToArray());

                        var    toai      = MonoidsCore.ToFunc(() => TradeOpenActionsInfo((d, n) => new { n, d }).ToArray());
                        Action setLevels = () => {
                            if (IsAsleep || IsHedgedTrading || isHedgeChild)
                            {
                                TradingMacrosByPair()
                                .OrderByDescending(tm => tm._RatesMax - tm._RatesMin)
                                .Take(1)
                                .ForEach(tm => {
                                        var offset     = (tm._RatesMax - tm._RatesMin) / 20;
                                        SellLevel.Rate = tm._RatesMax + offset;
                                        BuyLevel.Rate  = tm._RatesMin - offset;
                                    });
                            }
                            else if (!TradeConditionsHaveSetCorridor())
                            {
                                TradeConditionsCanSetCorridor()
                                .Where(td => td.HasAny())
                                .ForEach(_ => SetTradeLevelsToLevelBy(GetTradeLevel)());
                            }
                            if (IsTrader)
                            {
                                adjustExitLevels2();
                            }
                        };
                        #region FirstTime
                        if (firstTime && IsTrader)
                        {
                            WorkflowStep = "";
                            Log          = new Exception(conditions() + "");
                            ResetTakeProfitManual();

                            #region onCloseTradeLocal
                            onCanTradeLocal = canTrade => canTrade || Trades.Any();
                            var canTradeOff = !IsAutoStrategy;
                            #region turnItOff
                            Action <bool, Action> turnItOff = (should, a) => _buySellLevels
                                                              .Where(_ => should)
                                                              .Do(sr => {
                                    //if(!TradeConditionsHave(Tip2Ok))
                                    sr.InManual    = false;
                                    sr.CanTrade    = !IsAutoStrategy;
                                    sr.TradesCount = TradeCountStart;
                                })
                                                              .ToArray()
                                                              .Take(1)
                                                              .Do(_ => {
                                    UnFreezeCorridorStartDate();
                                })
                                                              .Where(_ => a != null)
                                                              .ForEach(_ => a());
                            #endregion
                            var hasTradeCountOff = _buySellLevels.Where(sr => sr.TradesCount < -TradeCountMax);
                            onCloseTradeLocal += t => {
                                //if(!HaveTrades()) {
                                if (_buySellLevels.All(sr => sr.InManual && !sr.CanTrade))
                                {
                                    _buySellLevelsForEach(sr => sr.InManual = false);
                                }
                                if (minPLOk(t) || hasTradeCountOff.Any())
                                {
                                    BuyLevel.InManual = SellLevel.InManual = false;
                                    turnItOff(true, () => {
                                            if (canTradeOff)
                                            {
                                                IsTradingActive = false;
                                            }
                                        });
                                }
                                if (false && CurrentGrossInPipTotal > 0)
                                {
                                    BroadcastCloseAllTrades();
                                }
                                BuyCloseLevel.InManual = SellCloseLevel.InManual = false;
                                CorridorStartDate      = null;
                                //}
                                setLevels();
                            };
                            #endregion

                            Action <Trade> canTradeByTradeCount = t =>
                                                                  hasTradeCountOff
                                                                  .Take(1)
                                                                  .ForEach(_ => {
                                    _buySellLevels
                                    .ForEach(sr => {
                                        sr.CanTrade    = false;
                                        sr.TradesCount = TradeCountStart;
                                    });
                                });
                            onOpenTradeLocal += t => {
                                canTradeByTradeCount(t);
                                toai().ForEach(x => {
                                        Log = new Exception(nameof(TradeOpenAction) + ": " + x.n);
                                        x.d(t);
                                    });
                            };
                        }
                        #endregion
                        setLevels();
                        if (IsTrader)
                        {
                            exitFunc();
                            try {
                                TradeDirectionTriggersRun();
                                TradeConditionsTrigger();
                            } catch (Exception exc) {
                                Log = exc;
                            }
                        }
                    }
                    break;
                        #endregion
                    }
                    if (firstTime)
                    {
                        firstTime = false;
                        ResetBarsCountCalc();
                        ForEachSuppRes(sr => sr.ResetPricePosition());
                        LogTrades = !IsInVirtualTrading;
                    }
                };
                #endregion
                #endregion

                #region On Trade Close
                _strategyExecuteOnTradeClose = t => {
                    if (!Trades.Any() && isCurrentGrossOk())
                    {
                        CorridorStartDate = null;
                        CorridorStopDate  = DateTime.MinValue;
                    }

                    if (onCloseTradeLocal != null)
                    {
                        onCloseTradeLocal(t);
                    }
                    if (TurnOffOnProfit && t.PL >= PriceSpreadAverageInPips)
                    {
                        Strategy = Strategy & ~Strategies.Auto;
                    }
                    CloseAtZero = false;
                };
                #endregion

                #region On Trade Open
                _strategyExecuteOnTradeOpen = trade => {
                    SuppRes.ForEach(sr => sr.ResetPricePosition());
                    onOpenTradeLocal?.Invoke(trade);
                    IsTradingActive = IsTradingActive || IsInVirtualTrading;
                };
                #endregion

                #region _adjustEnterLevels
                Action setLevelPrices = () => {
                    try {
                        if (IsTradingActive)
                        {
                            BuyLevel.SetPrice(enter(true));
                            SellLevel.SetPrice(enter(false));
                            BuyCloseLevel.SetPrice(CurrentExitPrice(true));
                            SellCloseLevel.SetPrice(CurrentExitPrice(false));
                        }
                        else
                        {
                            SuppRes.ForEach(sr => sr.ResetPricePosition());
                        }
                    } catch (Exception exc) { Log = exc; }
                };
                _adjustEnterLevels += setLevelPrices;
                _adjustEnterLevels += adjustEnterLevels;
                _adjustEnterLevels += () => turnOff(() => _buySellLevelsForEach(sr => { if (IsAutoStrategy)
                                                                                        {
                                                                                            sr.CanTradeEx = false;
                                                                                        }
                                                                                }));
                _adjustEnterLevels += () => exitFunc()();
                _adjustEnterLevels += setLevelPrices;
                _adjustEnterLevels += () => { if (runOnce != null && runOnce())
                                              {
                                                  runOnce = null;
                                              }
                };
                #endregion
            }

            #region if (!IsInVitualTrading) {
            if (!IsInVirtualTrading)
            {
                this.TradesManager.TradeClosed += TradeCloseHandler;
                this.TradesManager.TradeAdded  += TradeAddedHandler;
            }
            #endregion

            #endregion

            #region ============ Run =============
            _adjustEnterLevels();
            #endregion
        }
예제 #8
0
        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);
        }
    }