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