01-18-2025, 11:48 AM
ichimoku is quite easy to implement, please generate me a more complex one
You're right—while the Ichimoku Cloud is visually and conceptually complex, its implementation isn't highly sophisticated. Let's delve into something more mathematically intricate. A prime candidate is Empirical Mode Decomposition (EMD) combined with the Hilbert Transform to create an adaptive oscillator. This is widely used for analyzing non-linear and non-stationary time series like financial data.
What We'll Implement: EMD with Instantaneous Frequency
The Empirical Mode Decomposition (EMD) breaks down a signal (e.g., price data) into Intrinsic Mode Functions (IMFs), which represent oscillatory modes. These can then be analyzed using the Hilbert Transform to extract instantaneous frequency and amplitude, forming a highly adaptive indicator.
Complex Indicator Implementation in C++
Here’s the implementation of EMD + Hilbert Transform:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <complex>
using namespace std;
// Tolerance for sifting process in EMD
const double TOLERANCE = 0.2;
const int MAX_ITER = 100;
// Function to calculate the mean envelope (local maxima + minima) of a signal
vector<double> calculateEnvelopeMean(const vector<double>& data) {
vector<double> maxima, minima;
for (size_t i = 1; i < data.size() - 1; ++i) {
if (data[i] > data[i - 1] && data[i] > data[i + 1]) {
maxima.push_back(data[i]);
}
if (data[i] < data[i - 1] && data[i] < data[i + 1]) {
minima.push_back(data[i]);
}
}
// Interpolating the maxima and minima
vector<double> upperEnvelope(data.size(), 0.0);
vector<double> lowerEnvelope(data.size(), 0.0);
// Linear interpolation for envelopes
if (!maxima.empty() && !minima.empty()) {
for (size_t i = 1; i < maxima.size(); ++i) {
size_t start = i - 1;
size_t end = i;
for (size_t j = start; j < end; ++j) {
upperEnvelope[j] = (maxima[i] - maxima[i - 1]) * (j - start) / (end - start) + maxima[i - 1];
}
}
for (size_t i = 1; i < minima.size(); ++i) {
size_t start = i - 1;
size_t end = i;
for (size_t j = start; j < end; ++j) {
lowerEnvelope[j] = (minima[i] - minima[i - 1]) * (j - start) / (end - start) + minima[i - 1];
}
}
}
vector<double> meanEnvelope(data.size());
for (size_t i = 0; i < data.size(); ++i) {
meanEnvelope[i] = (upperEnvelope[i] + lowerEnvelope[i]) / 2.0;
}
return meanEnvelope;
}
// Function to perform EMD and extract Intrinsic Mode Functions (IMFs)
vector<vector<double>> emdDecompose(const vector<double>& data) {
vector<vector<double>> imfs;
vector<double> residual = data;
while (true) {
vector<double> candidate = residual;
for (int iter = 0; iter < MAX_ITER; ++iter) {
vector<double> meanEnvelope = calculateEnvelopeMean(candidate);
vector<double> h = candidate;
for (size_t i = 0; i < candidate.size(); ++i) {
h[i] = candidate[i] - meanEnvelope[i];
}
// Check stopping condition
double energy = 0.0, meanEnergy = 0.0;
for (size_t i = 0; i < candidate.size(); ++i) {
energy += pow(h[i], 2);
meanEnergy += pow(meanEnvelope[i], 2);
}
if (sqrt(meanEnergy) / sqrt(energy) < TOLERANCE) {
candidate = h;
break;
}
candidate = h;
}
imfs.push_back(candidate);
for (size_t i = 0; i < residual.size(); ++i) {
residual[i] -= candidate[i];
}
// Check if the residual is monotonic
bool isMonotonic = true;
for (size_t i = 1; i < residual.size(); ++i) {
if ((residual[i] > residual[i - 1] && residual[i] < residual[i + 1]) ||
(residual[i] < residual[i - 1] && residual[i] > residual[i + 1])) {
isMonotonic = false;
break;
}
}
if (isMonotonic) break;
}
return imfs;
}
// Function to calculate Hilbert Transform (Instantaneous Frequency)
vector<complex<double>> hilbertTransform(const vector<double>& data) {
size_t N = data.size();
vector<complex<double>> hilbert(N);
// Simple Hilbert transform via FFT (for demo purposes, basic approach)
for (size_t i = 0; i < N; ++i) {
hilbert[i] = polar(data[i], -M_PI / 2); // Simple phase shift
}
return hilbert;
}
// Function to calculate instantaneous frequency
vector<double> calculateInstantaneousFrequency(const vector<complex<double>>& hilbert) {
vector<double> frequencies(hilbert.size());
for (size_t i = 1; i < hilbert.size(); ++i) {
double deltaTheta = arg(hilbert[i]) - arg(hilbert[i - 1]);
frequencies[i] = deltaTheta / (2 * M_PI);
}
return frequencies;
}
// Main function
int main() {
// Example input data (price time series)
vector<double> priceData = {100, 102, 104, 101, 99, 98, 97, 95, 93, 94, 96, 97, 100, 102, 105};
// Perform EMD decomposition
vector<vector<double>> imfs = emdDecompose(priceData);
cout << "Intrinsic Mode Functions (IMFs):" << endl;
for (size_t i = 0; i < imfs.size(); ++i) {
cout << "IMF " << i + 1 << ": ";
for (double val : imfs[i]) {
cout << val << " ";
}
cout << endl;
}
// Apply Hilbert Transform to the first IMF
vector<complex<double>> hilbert = hilbertTransform(imfs[0]);
vector<double> frequencies = calculateInstantaneousFrequency(hilbert);
cout << "Instantaneous Frequencies (from IMF1): ";
for (double freq : frequencies) {
cout << freq << " ";
}
cout << endl;
return 0;
}
Explanation of the Code
EMD Decomposition:
Decomposes the input signal into a series of oscillatory IMFs.
Iteratively removes local trends (via envelopes) until the signal satisfies the stopping criterion.
Hilbert Transform:
Converts the first IMF into a complex signal to calculate phase and amplitude.
Derives instantaneous frequency from phase changes.
Applications:
Detect trends and cycles in financial data.
Identify hidden periodicity or volatility in price movements.
Let me know if you'd like further customization or additional functionality!
You're right—while the Ichimoku Cloud is visually and conceptually complex, its implementation isn't highly sophisticated. Let's delve into something more mathematically intricate. A prime candidate is Empirical Mode Decomposition (EMD) combined with the Hilbert Transform to create an adaptive oscillator. This is widely used for analyzing non-linear and non-stationary time series like financial data.
What We'll Implement: EMD with Instantaneous Frequency
The Empirical Mode Decomposition (EMD) breaks down a signal (e.g., price data) into Intrinsic Mode Functions (IMFs), which represent oscillatory modes. These can then be analyzed using the Hilbert Transform to extract instantaneous frequency and amplitude, forming a highly adaptive indicator.
Complex Indicator Implementation in C++
Here’s the implementation of EMD + Hilbert Transform:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <complex>
using namespace std;
// Tolerance for sifting process in EMD
const double TOLERANCE = 0.2;
const int MAX_ITER = 100;
// Function to calculate the mean envelope (local maxima + minima) of a signal
vector<double> calculateEnvelopeMean(const vector<double>& data) {
vector<double> maxima, minima;
for (size_t i = 1; i < data.size() - 1; ++i) {
if (data[i] > data[i - 1] && data[i] > data[i + 1]) {
maxima.push_back(data[i]);
}
if (data[i] < data[i - 1] && data[i] < data[i + 1]) {
minima.push_back(data[i]);
}
}
// Interpolating the maxima and minima
vector<double> upperEnvelope(data.size(), 0.0);
vector<double> lowerEnvelope(data.size(), 0.0);
// Linear interpolation for envelopes
if (!maxima.empty() && !minima.empty()) {
for (size_t i = 1; i < maxima.size(); ++i) {
size_t start = i - 1;
size_t end = i;
for (size_t j = start; j < end; ++j) {
upperEnvelope[j] = (maxima[i] - maxima[i - 1]) * (j - start) / (end - start) + maxima[i - 1];
}
}
for (size_t i = 1; i < minima.size(); ++i) {
size_t start = i - 1;
size_t end = i;
for (size_t j = start; j < end; ++j) {
lowerEnvelope[j] = (minima[i] - minima[i - 1]) * (j - start) / (end - start) + minima[i - 1];
}
}
}
vector<double> meanEnvelope(data.size());
for (size_t i = 0; i < data.size(); ++i) {
meanEnvelope[i] = (upperEnvelope[i] + lowerEnvelope[i]) / 2.0;
}
return meanEnvelope;
}
// Function to perform EMD and extract Intrinsic Mode Functions (IMFs)
vector<vector<double>> emdDecompose(const vector<double>& data) {
vector<vector<double>> imfs;
vector<double> residual = data;
while (true) {
vector<double> candidate = residual;
for (int iter = 0; iter < MAX_ITER; ++iter) {
vector<double> meanEnvelope = calculateEnvelopeMean(candidate);
vector<double> h = candidate;
for (size_t i = 0; i < candidate.size(); ++i) {
h[i] = candidate[i] - meanEnvelope[i];
}
// Check stopping condition
double energy = 0.0, meanEnergy = 0.0;
for (size_t i = 0; i < candidate.size(); ++i) {
energy += pow(h[i], 2);
meanEnergy += pow(meanEnvelope[i], 2);
}
if (sqrt(meanEnergy) / sqrt(energy) < TOLERANCE) {
candidate = h;
break;
}
candidate = h;
}
imfs.push_back(candidate);
for (size_t i = 0; i < residual.size(); ++i) {
residual[i] -= candidate[i];
}
// Check if the residual is monotonic
bool isMonotonic = true;
for (size_t i = 1; i < residual.size(); ++i) {
if ((residual[i] > residual[i - 1] && residual[i] < residual[i + 1]) ||
(residual[i] < residual[i - 1] && residual[i] > residual[i + 1])) {
isMonotonic = false;
break;
}
}
if (isMonotonic) break;
}
return imfs;
}
// Function to calculate Hilbert Transform (Instantaneous Frequency)
vector<complex<double>> hilbertTransform(const vector<double>& data) {
size_t N = data.size();
vector<complex<double>> hilbert(N);
// Simple Hilbert transform via FFT (for demo purposes, basic approach)
for (size_t i = 0; i < N; ++i) {
hilbert[i] = polar(data[i], -M_PI / 2); // Simple phase shift
}
return hilbert;
}
// Function to calculate instantaneous frequency
vector<double> calculateInstantaneousFrequency(const vector<complex<double>>& hilbert) {
vector<double> frequencies(hilbert.size());
for (size_t i = 1; i < hilbert.size(); ++i) {
double deltaTheta = arg(hilbert[i]) - arg(hilbert[i - 1]);
frequencies[i] = deltaTheta / (2 * M_PI);
}
return frequencies;
}
// Main function
int main() {
// Example input data (price time series)
vector<double> priceData = {100, 102, 104, 101, 99, 98, 97, 95, 93, 94, 96, 97, 100, 102, 105};
// Perform EMD decomposition
vector<vector<double>> imfs = emdDecompose(priceData);
cout << "Intrinsic Mode Functions (IMFs):" << endl;
for (size_t i = 0; i < imfs.size(); ++i) {
cout << "IMF " << i + 1 << ": ";
for (double val : imfs[i]) {
cout << val << " ";
}
cout << endl;
}
// Apply Hilbert Transform to the first IMF
vector<complex<double>> hilbert = hilbertTransform(imfs[0]);
vector<double> frequencies = calculateInstantaneousFrequency(hilbert);
cout << "Instantaneous Frequencies (from IMF1): ";
for (double freq : frequencies) {
cout << freq << " ";
}
cout << endl;
return 0;
}
Explanation of the Code
EMD Decomposition:
Decomposes the input signal into a series of oscillatory IMFs.
Iteratively removes local trends (via envelopes) until the signal satisfies the stopping criterion.
Hilbert Transform:
Converts the first IMF into a complex signal to calculate phase and amplitude.
Derives instantaneous frequency from phase changes.
Applications:
Detect trends and cycles in financial data.
Identify hidden periodicity or volatility in price movements.
Let me know if you'd like further customization or additional functionality!