OpenAlgo – An Open-Source Algo Trading Platform for Amibroker Users

Hi everyone,

I’ve been using Amibroker for over a decade, and like many here, I have learned a lot from Dr. Tomasz Janeczko and the countless contributors to the Amibroker AFL ecosystem. Their work has shaped the way we develop trading strategies and automate execution. Inspired by this, I wanted to create something that makes automated trading simple, efficient, and self-hosted.

That’s how OpenAlgo was born – an Open-Source platform designed to make trading automation easy while giving traders full control over the codes and infrastructure.

What is OpenAlgo?

  • Self-hosted and fully Open-Source, giving traders complete ownership.
  • Runs on your desktop, laptop, or cloud servers (Windows, Mac, Linux, VPS).
  • Built using Python Flask, with a clean, user-friendly UI powered by DaisyUI/Tailwind CSS.
  • Uses SQLite for smooth and reliable local data management.
  • Supports 12+ Indian brokers like Zerodha, Upstox, Angel, Dhan, Fyers, Kotak, and more.
  • Easily connects with Amibroker for fast and reliable order execution.
  • Supports Amiquotes to fetch intraday and EOD data directly from the Brokers Feed (Supports NSE, BSE, MCX , NFO, CDS , BFO exchanges)
  • Supports Both VBscript method and Modern Amibroker Internet Functions to Place Orders

Minimum requirements

AmiBroker version 6.00 or higher (Supports 32-bit and 64-bit Amibroker). Recommended to use 6.4 or Higher versions.

OpenAlgo is Open-Source and completely self-hosted, so there’s no dependency on third-party services. If you’re interested, you can check out the source code here:

:link: GitHub: GitHub - marketcalls/openalgo: Open Source Algo Trading Platform for Everyone

Requirements for Installing OpenAlgo to Automate Amibroker Strategies

  • Windows 10 or Higher

  • Visual Studio Code (VS Code) is installed.

  • Python version 3.10 or higher version installed

  • Git for cloning the repository

OpenAlgo is built with Amibroker users in mind, aiming to make algorithmic trading more accessible, flexible, and efficient. I would love to hear your thoughts, feedback, and suggestions on how OpenAlgo can further enhance the trading experience for Amibroker users.

Looking forward to your thoughts!

2 Likes

Hi Rajandran,

Congratulations.

The Broker specs have been so unstandardised that many have wished for such a middleware.
I hope it helps many who have not been able to build their own interfaces.

I have been on a similar journey and would like to highlight the AmiBroker interfaces,
used by you.

AmiBroker being the pinnacle of Trading softwares,your design choice for universally
using http calls for data and trading interface could in the future be pursued to
follow the AB design philosophy.

Using COM/OLE to import data via AmiQuote will work, but it has too many limitations.
Especially for intraday live data.

Similarly, I can see that the trading interface is wholly VBScript inside AFL. So much COM
code inside AFL is detrimental to performance. GetTradingInterface() is for this keeping AFL as light as possible as opposed to embedding VBScript inside.
A lot of COM stuff is possibly limited by critical section there by serializing calls and processing all http in the afl threads, on the other hand, the trading interface transfers the overhead to a separate compiled appl.

Hopefully, future enhancements align with AB design philosophy.
I would like to understand why the choice of http calls over the websocket implementations provided by all brokers.

I couldn't agree more. The design of trading interface should follow exactly the design of open source IBController

A separate application that does all the bookkeeping, allowing AFL to be lightweight.

It is worth noting that VBScript is obsolete and should not be used in new projects.

Thank you for the insights, Tomasz. I completely agree that VBScript is obsolete, and I am already replacing the VBScript-based implementation with native AmiBroker Internet functions to send POST requests for order placement. However, to maintain backward compatibility for users on older versions, the VBScript option will still be available.

I also appreciate the reference to IBController and will explore its design further to see how OpenAlgo can align with best practices while ensuring flexibility for users.

I completely agree that broker specifications are highly unstandardized, making it difficult for traders to build their own interfaces. OpenAlgo aims to bridge this gap by providing a middleware that simplifies the integration process for AmiBroker users.

Regarding the trading interface, I acknowledge the performance concerns related to embedding VBScript inside AFL. That's why I am in the process of replacing the VBScript implementation with AmiBroker’s native Internet functions for sending POST requests, while still keeping VBScript as a fallback for older versions. I will also explore IBController further to see how OpenAlgo can align better with AmiBroker’s design philosophy.

As for the choice of HTTP over WebSockets, the primary reason is to ensure compatibility across a wide range of Indian brokers, many of whom still rely on REST APIs for order placement

This is my endeavour, WS based data plugin.

WsRtd - Websocket-Json standard Data plugin - Test Release - Plug-ins - AmiBroker Community Forum

Working on TI here,
BrokerWS is a Trading Interface for AmiBroker using Json & WebSocket

Data plugin is 100% compliant and very high performance unlike some 3rd party stuff.

1 Like

thats cool will experiment on that. I truly appreciate the effort you've put into bringing broker WebSocket data into AmiBroker. it's no small feat, given the lack of standardization across different brokers.

Here’s the architecture I’ve designed to handle signals (BUY, SELL, SHORT, COVER) from AmiBroker. The flow is straightforward: AmiBroker sends a simple POST request to OpenAlgo, which then translates it into the broker’s specific API format.

The goal is to keep the integration lightweight on the AmiBroker side while centralizing the heavy lifting in OpenAlgo. This way, the architecture remains scalable and adaptable to various brokers and exchanges.

Would love to hear your thoughts or any suggestions for further improvements!

1 Like

@Tomasz and @nsm51

Currently I use OpenAlgo (Authentication, Symbol Management, Common Broker API Management, GUI based Trade Reports, API Validation (testing strategies) and Trade Management (Time Based Trade Management) etc.

This is the Amibroker Native Function to place orders. It is native to Amibroker to placeorder. Will it be efficient enough or still IBcontroller techniques are preferred?

// OpenAlgo - Amibroker SmartOrder Chart Trading Module v2.0
// Date - 12/12/2024

_SECTION_BEGIN("OpenAlgo SmartOrder Trading Module - Modern Methods");

RequestTimedRefresh(1, False);

// Define parameter controls
apikey = ParamStr("OpenAlgo API Key", "******");
strategy = ParamStr("Strategy", "Amibroker");
symbol = ParamStr("Symbol", "YESBANK");
exchange = ParamList("Exchange", "NSE|NFO|BSE|MCX|CDS");
pricetype = ParamStr("Price Type", "MARKET");
product = ParamList("Product", "MIS|NRML|CNC");
quantity = Param("Quantity", 1, 1, 1000, 1);
position_size = Param("Position Size", 0, -1000, 1000, 1);

host = ParamStr("Host", "http://127.0.0.1:5000");
ver = ParamStr("API Version", "v1");

VoiceAlert = ParamList("Voice Alert", "Disable|Enable", 1);
EnableAlgo = ParamList("Algo Mode", "Disable|Enable", 0);
EnableButton = ParamList("Button Trading", "Disable|Enable", 0);

Entrydelay = Param("Entry Delay",0,0,1,1);
Exitdelay = Param("Exit Delay",0,0,1,1);

AlgoBuy = lastvalue(Ref(Buy,-Entrydelay));
AlgoSell = lastvalue(Ref(Sell,-Exitdelay));
AlgoShort = lastvalue(Ref(Short,-Entrydelay));
AlgoCover = lastvalue(Ref(Cover,-Exitdelay));

// Construct URL base
bridgeurl = host + "/api/" + ver;

static_name_ = Name()+GetChartID()+interval(2)+strategy;
static_name_algo = static_name_+interval(2)+strategy+"algostatus";



//OpenAlgo Dashboard

GfxSelectFont( "BOOK ANTIQUA", 14, 100 );
GfxSetBkMode( 1 );
if(EnableAlgo == "Enable")
{
AlgoStatus = "Algo Enabled";
GfxSetTextColor( colorGreen ); 
GfxTextOut( "Algostatus : "+AlgoStatus , 20, 40); 
if(Nz(StaticVarGet(static_name_algo),0)!=1)
{
_TRACE("Algo Status : Enabled");
StaticVarSet(static_name_algo, 1);
}
}
if(EnableAlgo == "Disable")
{
AlgoStatus = "Algo Disabled";
GfxSetTextColor( colorRed ); 
GfxTextOut( "Algostatus : "+AlgoStatus , 20, 40); 
if(Nz(StaticVarGet(static_name_algo),0)!=0)
{
_TRACE("Algo Status : Disabled");
StaticVarSet(static_name_algo, 0);
}
}



// Function to Place Smart Order
function PlaceSmartOrder(action, quantity, position_size) {
    postData = "{\"apikey\": \"" + apikey + "\", " +
               "\"strategy\": \"" + strategy + "\", " +
               "\"symbol\": \"" + symbol + "\", " +
               "\"action\": \"" + action + "\", " +
               "\"exchange\": \"" + exchange + "\", " +
               "\"pricetype\": \"" + pricetype + "\", " +
               "\"product\": \"" + product + "\", " +
               "\"quantity\": \"" + quantity + "\", " +
               "\"position_size\": \"" + position_size + "\"}";

    headers = "Content-Type: application/json\r\n" +
              "Accept-Encoding: gzip, deflate\r\n";
    InternetSetHeaders(headers);

    _TRACE("Smart Order Request Sent: " + postData); // Log request
    ih = InternetPostRequest(bridgeurl + "/placesmartorder", postData);

    if (ih) {
        response = "";
        while ((line = InternetReadString(ih)) != "") {
            response += line;
        }
        _TRACEF("Smart Order Response: %s", response);
        if (VoiceAlert == "Enable") Say(action + " Smart Order Placed.");
        InternetClose(ih);
    } else {
        _TRACE("Failed to place smart order.");
    }
}

// Function to Exit Order
function ExitOrder(action) {
    postData = "{\"apikey\": \"" + apikey + "\", " +
               "\"strategy\": \"" + strategy + "\", " +
               "\"symbol\": \"" + symbol + "\", " +
               "\"action\": \"" + action + "\", " +
               "\"exchange\": \"" + exchange + "\", " +
               "\"pricetype\": \"" + pricetype + "\", " +
               "\"product\": \"" + product + "\", " +
               "\"quantity\": \"0\", " +
               "\"position_size\": \"0\"}";

    headers = "Content-Type: application/json\r\n" +
              "Accept-Encoding: gzip, deflate\r\n";
    InternetSetHeaders(headers);

    _TRACE("Exit Order Request Sent: " + postData); // Log request
    ih = InternetPostRequest(bridgeurl + "/placesmartorder", postData);

    if (ih) {
        response = "";
        while ((line = InternetReadString(ih)) != "") {
            response += line;
        }
        _TRACEF("Exit Order Response: %s", response);
        if (VoiceAlert == "Enable") Say(action + " Exit Order Placed.");
        InternetClose(ih);
    } else {
        _TRACE("Failed to place exit order.");
    }
}

// Function to Square Off All Positions
function SquareOffAll() {
    postData = "{\"apikey\": \"" + apikey + "\", " +
               "\"strategy\": \"" + strategy + "\"}";

    headers = "Content-Type: application/json\r\n" +
              "Accept-Encoding: gzip, deflate\r\n";
    InternetSetHeaders(headers);

    _TRACE("Square Off Request Sent: " + postData); // Log request
    ih = InternetPostRequest(bridgeurl + "/closeposition", postData);

    if (ih) {
        response = "";
        while ((line = InternetReadString(ih)) != "") {
            response += line;
        }
        _TRACEF("Square Off Response: %s", response);
        if (VoiceAlert == "Enable") Say("All positions squared off.");
        InternetClose(ih);
    } else {
        _TRACE("Failed to square off positions.");
    }
}

// Function to Draw Buttons
function DrawButton(Text, x1, y1, x2, y2, colorFrom, colorTo) {
    GfxSetOverlayMode(0);
    GfxSelectFont("Verdana", 9, 700);
    GfxSetBkMode(1);
    GfxGradientRect(x1, y1, x2, y2, colorFrom, colorTo);
    GfxDrawText(Text, x1, y1, x2, y2, 32 | 1 | 4 | 16);
}

// Execution Module
if (EnableAlgo != "Disable") {
    lasttime = StrFormat("%0.f", LastValue(BarIndex()));
    SetChartBkColor(colorDarkGrey);

    if (EnableAlgo == "Enable") {
        if (AlgoBuy == True AND AlgoCover == True AND StaticVarGet(static_name_ + "buyCoverAlgo") == 0 AND StaticVarGetText(static_name_ + "buyCoverAlgo_barvalue") != lasttime) {
            PlaceSmartOrder("BUY", quantity, quantity);
            if (VoiceAlert == "Enable") Say("Buy Order Triggered");
            _TRACE("API Request: " + sm_api_request);
            _TRACE("API Response: " + sm_api_response);
            StaticVarSetText(static_name_ + "buyCoverAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "buyCoverAlgo", 1);
        } else if (AlgoBuy != True OR AlgoCover != True) {
            StaticVarSet(static_name_ + "buyCoverAlgo", 0);
            StaticVarSetText(static_name_ + "buyCoverAlgo_barvalue", "");
        }

        if (AlgoBuy == True AND AlgoCover != True AND StaticVarGet(static_name_ + "buyAlgo") == 0 AND StaticVarGetText(static_name_ + "buyAlgo_barvalue") != lasttime) {
            PlaceSmartOrder("BUY", quantity, position_size);
            if (VoiceAlert == "Enable") Say("Buy Order Triggered");
            _TRACE("API Request: " + sm_api_request);
            _TRACE("API Response: " + sm_api_response);
            StaticVarSetText(static_name_ + "buyAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "buyAlgo", 1);
        } else if (AlgoBuy != True) {
            StaticVarSet(static_name_ + "buyAlgo", 0);
            StaticVarSetText(static_name_ + "buyAlgo_barvalue", "");
        }

        if (AlgoSell == true AND AlgoShort != True AND StaticVarGet(static_name_ + "sellAlgo") == 0 AND StaticVarGetText(static_name_ + "sellAlgo_barvalue") != lasttime) {
            ExitOrder("SELL");
            if (VoiceAlert == "Enable") Say("Sell Exit Order Triggered");
            _TRACE("API Request: " + ex_api_request);
            _TRACE("API Response: " + ex_api_response);
            StaticVarSetText(static_name_ + "sellAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "sellAlgo", 1);
        } else if (AlgoSell != True) {
            StaticVarSet(static_name_ + "sellAlgo", 0);
            StaticVarSetText(static_name_ + "sellAlgo_barvalue", "");
        }

        if (AlgoShort == True AND AlgoSell == True AND StaticVarGet(static_name_ + "ShortSellAlgo") == 0 AND StaticVarGetText(static_name_ + "ShortSellAlgo_barvalue") != lasttime) {
            PlaceSmartOrder("SELL", quantity, -1 * quantity);
            if (VoiceAlert == "Enable") Say("Short Order Triggered");
            _TRACE("API Request: " + sm_api_request);
            _TRACE("API Response: " + sm_api_response);
            StaticVarSetText(static_name_ + "ShortSellAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "ShortSellAlgo", 1);
        } else if (AlgoShort != True OR AlgoSell != True) {
            StaticVarSet(static_name_ + "ShortSellAlgo", 0);
            StaticVarSetText(static_name_ + "ShortSellAlgo_barvalue", "");
        }

        if (AlgoShort == True AND AlgoSell != True AND StaticVarGet(static_name_ + "ShortAlgo") == 0 AND StaticVarGetText(static_name_ + "ShortAlgo_barvalue") != lasttime) {
            PlaceSmartOrder("SELL", quantity, position_size);
            if (VoiceAlert == "Enable") Say("Short Order Triggered");
            _TRACE("API Request: " + sm_api_request);
            _TRACE("API Response: " + sm_api_response);
            StaticVarSetText(static_name_ + "ShortAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "ShortAlgo", 1);
        } else if (AlgoShort != True) {
            StaticVarSet(static_name_ + "ShortAlgo", 0);
            StaticVarSetText(static_name_ + "ShortAlgo_barvalue", "");
        }

        if (AlgoCover == true AND AlgoBuy != True AND StaticVarGet(static_name_ + "CoverAlgo") == 0 AND StaticVarGetText(static_name_ + "CoverAlgo_barvalue") != lasttime) {
            ExitOrder("BUY");
            if (VoiceAlert == "Enable") Say("Short Exit Order Triggered");
            _TRACE("API Request: " + ex_api_request);
            _TRACE("API Response: " + ex_api_response);
            StaticVarSetText(static_name_ + "CoverAlgo_barvalue", lasttime);
            StaticVarSet(static_name_ + "CoverAlgo", 1);
        } else if (AlgoCover != True) {
            StaticVarSet(static_name_ + "CoverAlgo", 0);
            StaticVarSetText(static_name_ + "CoverAlgo_barvalue", "");
        }
    }

    if (EnableButton == "Enable") {
        DrawButton("BE", X0, Y0, X0 + X1, Y0 + 50, colorGreen, colorGreen);
        CursorInBEButton = MouseX >= X0 AND MouseX <= X0 + X1 AND MouseY >= Y0 AND MouseY <= Y0 + 50;
        BEButtonClick = CursorInBEButton AND LBClick;

        DrawButton("BX", X0 + 65, Y0, X0 + X1 + 65, Y0 + 50, colorRed, colorRed);
        CursorInBXButton = MouseX >= X0 + 65 AND MouseX <= X0 + X1 + 65 AND MouseY >= Y0 AND MouseY <= Y0 + 50;
        BXButtonClick = CursorInBXButton AND LBClick;

        DrawButton("SE", X0, Y0 + 55, X0 + X1, Y0 + 105, colorRed, colorRed);
        CursorInSEButton = MouseX >= X0 AND MouseX <= X0 + X1 AND MouseY >= Y0 + 55 AND MouseY <= Y0 + 105;
        SEButtonClick = CursorInSEButton AND LBClick;

        DrawButton("SX", X0 + 65, Y0 + 55, X0 + X1 + 65, Y0 + 105, colorGreen, colorGreen);
        CursorInSXButton = MouseX >= X0 + 65 AND MouseX <= X0 + X1 + 65 AND MouseY >= Y0 + 55 AND MouseY <= Y0 + 105;
        SXButtonClick = CursorInSXButton AND LBClick;

        DrawButton("CLOSE ALL", X0, Y0 + 110, X0 + X1 + 65, Y0 + 155, colorRed, colorRed);
        CursorInCXButton = MouseX >= X0 AND MouseX <= X0 + X1 + 65 AND MouseY >= Y0 + 110 AND MouseY <= Y0 + 155;
        CXButtonClick = CursorInCXButton AND LBClick;

        if (BEButtonClick AND StaticVarGet(static_name_ + "BEAlgo") == 0) {
            PlaceSmartOrder("BUY", quantity, position_size);
            if (VoiceAlert == "Enable") {
                Say("Buy Order Triggered");
            }
            _TRACE("API Request: " + postData);
            StaticVarSet(static_name_ + "BEAlgo", 1);
        } else {
            StaticVarSet(static_name_ + "BEAlgo", 0);
        }

        if (BXButtonClick AND StaticVarGet(static_name_ + "BXAlgo") == 0) {
            PlaceSmartOrder("SELL", quantity, position_size);
            if (VoiceAlert == "Enable") {
                Say("Sell Order Triggered");
            }
            _TRACE("API Request: " + postData);
            StaticVarSet(static_name_ + "BXAlgo", 1);
        } else {
            StaticVarSet(static_name_ + "BXAlgo", 0);
        }

        if (SEButtonClick AND StaticVarGet(static_name_ + "SEAlgo") == 0) {
            PlaceSmartOrder("SELL", quantity, position_size);
            if (VoiceAlert == "Enable") {
                Say("Short Order Triggered");
            }
            _TRACE("API Request: " + postData);
            StaticVarSet(static_name_ + "SEAlgo", 1);
        } else {
            StaticVarSet(static_name_ + "SEAlgo", 0);
        }

        if (SXButtonClick AND StaticVarGet(static_name_ + "SXAlgo") == 0) {
            PlaceSmartOrder("BUY", quantity, position_size);
            if (VoiceAlert == "Enable") {
                Say("Cover Order Triggered");
            }
            _TRACE("API Request: " + postData);
            StaticVarSet(static_name_ + "SXAlgo", 1);
        } else {
            StaticVarSet(static_name_ + "SXAlgo", 0);
        }

        if (CXButtonClick AND StaticVarGet(Name() + GetChartID() + "CXAlgo") == 0) {
            SquareOffAll();
            if (VoiceAlert == "Enable") {
                Say("Square Off All Triggered");
            }
            _TRACE("API Request: " + postData);
            StaticVarSet(Name() + GetChartID() + "CXAlgo", 1);
        } else {
            StaticVarSet(Name() + GetChartID() + "CXAlgo", 0);
        }
    }
}

_SECTION_END();