//+------------------------------------------------------------------+ //| ModalityVolume.mqh | //| AnimateDread | //| | //| PARTICIPATION AND EFFORT-VERSUS-RESULT, from tick volume with the | //| hour-of-day removed (Mind\VolumeProfile.mqh). | //| | //| Wyckoff's third law asks whether the result matches the effort. | //| ABSORB is that comparison in log units: volume well above normal | //| for the hour on a bar that made little progress (a small range in | //| ATR) reads high - somebody is taking the other side. Volume that | //| is normal for the hour with a wide bar reads low - the move had no | //| one to fight. Tick volume is a proxy for participation on a CFD, | //| not the real thing; the book records what it turned out to be. | //+------------------------------------------------------------------+ #ifndef WARRIOR_MODALITYVOLUME_MQH #define WARRIOR_MODALITYVOLUME_MQH #include "Modality.mqh" #include "VolumeProfile.mqh" class CModalityVolume : public CModality { protected: CBarCache *m_cache; // not owned CVolumeProfile *m_profile; // not owned public: CModalityVolume(CBarCache *cache, CVolumeProfile *profile) : m_cache(cache), m_profile(profile) {} virtual string Name(void) const override { return "volume"; } virtual bool Read(const SBarRef &bar, SMarketContext &ctx) override; }; //+------------------------------------------------------------------+ bool CModalityVolume::Read(const SBarRef &bar, SMarketContext &ctx) { if(m_cache == NULL || m_profile == NULL) return false; m_profile.Update(); const double raw = m_profile.Raw(bar.idx); const double det = m_profile.Detrended(bar.idx); if(raw == VOLPROF_NA) return false; ctx.v[CTX_RVOL_RAW] = raw; if(det != VOLPROF_NA) ctx.v[CTX_RVOL] = det; const double range = ctx.Has(CTX_RANGE_ATR) ? ctx.v[CTX_RANGE_ATR] : CTX_NA; if(det != VOLPROF_NA && range != CTX_NA) ctx.v[CTX_ABSORB] = det - MathLog(MathMax(range, 0.1)); return true; } #endif // WARRIOR_MODALITYVOLUME_MQH