Advertisement IC Markets

Your Trading Plan Needs These 7 Non-Negotiable Sections

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

Implementation details:

Store the latest alert in a global variable or simple queue.

Provide clear setup instructions.

Ensure security (briefly mention Ngrok or IP whitelisting).

Structure:

Direct opening.

Python Code block.

Setup instructions.

Follow up about setting up ngrok or full deployment.

Let's refine the Python code:

Code: Select all

from flask import Flask, request, jsonify
import json

app = Flask(__name__)

# Global variable to store the latest signal
latest_signal = {}

@app.route('/webhook', methods=['POST'])
def webhook():
    global latest_signal
    try:
        # Parse the JSON payload from TradingView
        data = request.get_json(force=True)
        
        # Basic validation to ensure required fields exist
        if 'action' in data and 'symbol' in data:
            latest_signal = data
            print(f"✅ New Signal Received: {data}")
            return jsonify({"status": "success", "message": "Signal stored"}), 200
        else:
            print("❌ Invalid payload format")
            return jsonify({"status": "error", "message": "Invalid payload"}), 400
            
    except Exception as e:
        print(f"❌ Error processing webhook: {str(e)}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/get_latest_alert', methods=['GET'])
def get_latest_alert():
    # MT5 will hit this endpoint every second
    global latest_signal
    
    if latest_signal:
        return jsonify(latest_signal), 200
    else:
        # Return an empty JSON object if no signal has been received yet
        return jsonify({}), 200

if __name__ == '__main__':
    # Run the server on port 5000 (localhost)
    app.run(host='0.0.0.0', port=5000)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

Are there any specifics? Let's clear the signal after reading?

No, the MT5 script handles duplicate execution prevention via json_string != last_processed_signal. So keeping the latest state is fine, but clearing it after read is safer in case of MT5 restart. Actually, let's keep it simple. The EA logic already has if(json_string != last_processed_signal).

I'll provide the Python code and explain how to bridge the gap using Ngrok (to give TradingView a public URL while keeping the local port 5000 open for MT5).

Let's structure the answer.
No labeled closings. Direct lead in.

"This Python Flask server acts as the central router between the internet and your local MetaTrader 5 terminal. It listens for the HTTP POST webhook from TradingView, validates the JSON payload, and holds it in memory so your MT5 EA can fetch it via a standard GET request."
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

Here is the MQL4 version. While the underlying architecture—polling the Python Flask server every second—remains exactly the same, MT4 handles order execution and network requests differently than MT5.

Most notably, MT4 does not have the CTrade class, meaning you must manage order tickets, slippage, and price normalization manually using OrderSend() and OrderClose().
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

The MQL4 Execution Logic

Save this as TV_Webhook_Receiver.mq4 in your MQL4/Experts folder. Just like the MT5 version, it relies on the open-source JAson.mqh library (which is fully backwards-compatible with MT4) to parse the JSON string.

Code: Select all

//+------------------------------------------------------------------+
//|                                           TV_Webhook_Receiver.mq4|
//+------------------------------------------------------------------+
#property strict
#include <JAson.mqh> // Download from MQL4/MQL5 CodeBase

string server_url = "http://localhost:5000/get_latest_alert"; 
string last_processed_signal = "";
int magic_number = 777777; // Unique ID to manage only TV webhook trades

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Enable polling every 1 second
    EventSetTimer(1); 
    Print("EA Initialized. Listening for TradingView signals...");
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    EventKillTimer();
}

//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()
{
    string headers = "";
    string result_headers = "";
    char post[], result[];
    
    // MQL4 uses a slightly different WebRequest parameter structure than MT5
    int res = WebRequest("GET", server_url, headers, 500, post, result, result_headers);
    
    if(res == 200)
    {
        string json_string = CharArrayToString(result);
        
        // Prevent duplicate execution of the same signal
        if(json_string != last_processed_signal && json_string != "") 
        {
            ParseAndExecute(json_string);
            last_processed_signal = json_string;
        }
    }
}

//+------------------------------------------------------------------+
//| Parse JSON and Execute Trade                                     |
//+------------------------------------------------------------------+
void ParseAndExecute(string json_payload)
{
    CJAVal json;
    
    // Deserialize the JSON string sent by TradingView
    if(json.Deserialize(json_payload))
    {
        string action = json["action"].ToStr();
        string symbol = json["symbol"].ToStr();
        
        // Fetch current broker pricing and digit structure
        double ask = MarketInfo(symbol, MODE_ASK);
        double bid = MarketInfo(symbol, MODE_BID);
        int digits = (int)MarketInfo(symbol, MODE_DIGITS);
        
        // Placeholder for Rule 5 position sizing
        double lot_size = 0.1; 
        
        if(action == "long")
        {
            // MT4 requires strict price normalization before execution
            double sl = NormalizeDouble(json["sl"].ToDbl(), digits);
            double tp = NormalizeDouble(json["tp"].ToDbl(), digits);
            
            int ticket = OrderSend(symbol, OP_BUY, lot_size, ask, 3, sl, tp, "TV Long Sweep", magic_number, 0, clrGreen);
            if(ticket < 0) Print("OrderSend Error: ", GetLastError());
            else Print("Executed Long on ", symbol);
        }
        else if(action == "short")
        {
            double sl = NormalizeDouble(json["sl"].ToDbl(), digits);
            double tp = NormalizeDouble(json["tp"].ToDbl(), digits);
            
            int ticket = OrderSend(symbol, OP_SELL, lot_size, bid, 3, sl, tp, "TV Short Sweep", magic_number, 0, clrRed);
            if(ticket < 0) Print("OrderSend Error: ", GetLastError());
            else Print("Executed Short on ", symbol);
        }
        else if(action == "exit_long" || action == "exit_short")
        {
            // Iterate backwards through the order pool to safely close positions
            for(int i = OrdersTotal() - 1; i >= 0; i--)
            {
                if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
                {
                    if(OrderSymbol() == symbol && OrderMagicNumber() == magic_number)
                    {
                        // Determine correct closing price based on order direction
                        double close_price = (OrderType() == OP_BUY) ? MarketInfo(symbol, MODE_BID) : MarketInfo(symbol, MODE_ASK);
                        bool closed = OrderClose(OrderTicket(), OrderLots(), close_price, 3, clrGray);
                        
                        if(!closed) Print("OrderClose Error: ", GetLastError());
                        else Print("Closed position on ", symbol, " due to TV exit signal.");
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

Critical MT4 Setup Steps

Allow WebRequest: Just like MT5, you must go to Tools > Options > Expert Advisors, check "Allow WebRequest for listed URL," and add http://localhost:5000 so MT4 has permission to ping the Python script.

Auto Trading: Ensure the "Auto Trading" button at the top of your MT4 terminal is green, and that "Allow live trading" is checked in the EA's settings when you attach it to a chart.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

And this is the break down how to expose this localhost Flask server to TradingView using Ngrok so I can paste a public URL into my TradingView alerts.

Ngrok bridges the gap between TradingView's cloud servers and your local machine by creating a secure tunnel straight through your firewall.

1.) Install the Ngrok Agent - Download the executable from ngrok.com, extract the file, and open your terminal or command prompt in that directory.

2.) Authenticate the CLI:Required once per machine.Create a free ngrok account. In your dashboard, locate your personal Authtoken and run
this command to link your local agent:

Code: Select all

ngrok config add-authtoken YOUR_TOKEN_HERE
3.) Start the HTTP Tunnel:Must run concurrently with Flask.With your Python Flask server already running on port 5000, execute:

Code: Select all

ngrok http 5000
Leave this terminal window completely alone. Closing it will immediately kill the connection.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

4.Route the TradingView Alert:

Ngrok will display a status dashboard in your terminal. Locate the Forwarding row and copy the generated https:// address.

In TradingView, open your alert settings, enable Webhook URL, and paste the address. You MUST append your specific Flask route (/webhook) to the end of the URL so the payload goes to the right place:[https://1a2b-34-56-78-90.ngrok-free.app/webhook](https://1a2b-34-56-78-90.ngrok-free.app/webhook)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

TradingView strictly requires webhooks to be sent to port 80 or 443. Ngrok handles this automatically by generating a standard HTTPS (port 443) URL and securely routing that incoming traffic backward through your firewall to your local port 5000. One major operational constraint: on the free tier, Ngrok generates a completely new, randomized URL every time you restart the tunnel. If your machine reboots or you close the terminal window, you will have to manually update your active TradingView alerts with the newly generated address.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

Because your Ngrok URL is publicly accessible, unauthorized bots actively scan these endpoints looking for open trading bridges. To lock them out, we must implement a two-layer defense system: an IP Allowlist to drop unauthorized traffic at the door, and a Payload Passphrase to authenticate the actual JSON data.

Here is the secured version of your Flask server:

Code: Select all

from flask import Flask, request, jsonify

app = Flask(__name__)

# Holds the most recent webhook payload in memory
latest_signal = {}

# Layer 1: TradingView's official webhook IP addresses
TRADINGVIEW_IPS = [
    "52.89.214.238",
    "34.212.75.30",
    "54.218.53.128",
    "52.32.178.7"
]

# Layer 2: Your custom secret PIN
SECRET_PASSPHRASE = "FairmanBlueprint2026!"

@app.route('/webhook', methods=['POST'])
def webhook():
    global latest_signal
    
    # --- SECURITY LAYER 1: IP ALLOWLIST ---
    # Because Ngrok proxies the connection locally, request.remote_addr is always 127.0.0.1.
    # We must extract the true origin IP from the X-Forwarded-For header.
    forwarded_for = request.headers.get('X-Forwarded-For', '')
    client_ip = forwarded_for.split(',')[0].strip() if forwarded_for else request.remote_addr
    
    if client_ip not in TRADINGVIEW_IPS:
        print(f"⛔ Intrusion blocked: Unauthorized IP {client_ip}")
        return jsonify({"error": "Unauthorized IP"}), 403

    try:
        data = request.get_json(force=True)
        
        # --- SECURITY LAYER 2: PASSPHRASE ---
        if data.get("passphrase") != SECRET_PASSPHRASE:
            print(f"⛔ Intrusion blocked: Invalid Passphrase from {client_ip}")
            return jsonify({"error": "Unauthorized payload"}), 403
            
        # Execute trade logic
        if 'action' in data and 'symbol' in data:
            latest_signal = data
            print(f"⚡ Secure Signal Processed: {data['action'].upper()} on {data['symbol']}")
            return jsonify({"status": "success", "message": "Signal queued for MT4"}), 200
        else:
            return jsonify({"error": "Invalid payload structure"}), 400
            
    except Exception as e:
        print(f"❌ Webhook Error: {str(e)}")
        return jsonify({"error": "Failed to process payload"}), 500

@app.route('/get_latest_alert', methods=['GET'])
def get_latest_alert():
    global latest_signal
    if latest_signal:
        return jsonify(latest_signal), 200
    else:
        return jsonify({}), 200

if __name__ == '__main__':
    print("🚀 Secure Webhook Server running on http://localhost:5000")
    app.run(host='0.0.0.0', port=5000)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Your Trading Plan Needs These 7 Non-Negotiable Sections

Post by PTScalper »

How the Defenses Work

The IP Allowlist (The Guest List): TradingView publishes four official IP addresses that their servers use to dispatch webhooks. The script checks the incoming IP against this list. If a random bot scans your Ngrok URL, its IP won't match, and the server instantly drops the connection with a 403 Forbidden error.

The Passphrase (The PIN Code): IP addresses can technically be spoofed in HTTP headers. The ultimate defense is requiring a hardcoded password inside the JSON payload itself. If an attacker manages to bypass the IP check but doesn't know your exact passphrase, the script rejects the payload before it ever reaches MT4.

Updating Your Pine Script

To make this work, you must update the JSON construction strings in the Execution block of your Pine Script so TradingView includes your secret passphrase in every alert it sends.

Simply inject "passphrase": "FairmanBlueprint2026!" at the very beginning of your JSON strings:

Code: Select all

// Construct dynamic JSON for Long Entry
string long_json = '{"passphrase": "FairmanBlueprint2026!", "action": "long", "symbol": "' + syminfo.ticker + '", "price": ' + str.tostring(close) + ', "sl": ' + str.tostring(long_sl) + ', "tp": ' + str.tostring(long_tp) + '}'
Once this is updated on the chart and your alerts are recreated, your execution bridge is completely locked down.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply