How I shipped conditional orders first, rejected a single-indicator strategy for signal fusion, and built guardrails as a core feature - not an afterthought - for 500K+ retail investors on the Dhaka Stock Exchange.
Biniyog.com.bd is Bangladesh's largest retail investment platform, with 500K+ active users trading on the Dhaka Stock Exchange (DSE). By 2022, customers had strong tools for deciding what to buy -- real-time prices, portfolio tracking, and an analytics layer I had built earlier. But once they opened a position, they had nothing.
The DSE trades during fixed hours on fixed days. A retail investor with a full-time job cannot watch a live price feed all session, every day. In practice, this meant customers either sold too early out of anxiety, held too long out of inattention, or missed the window entirely. None of these are investing mistakes -- they are the predictable result of a product gap.
The competitive pressure: Our main competitor, a legacy brokerage, had just launched basic limit orders. Our CEO wanted "algo trading" on the roadmap within 6 months to maintain parity. Engineering estimated 4 months for a full automated trading engine.
My constraints: I was the sole PM with one backend engineer, two mobile engineers, and a designer. No dedicated data scientist. The DSE's API was unreliable -- price feeds dropped several times per session. And if this feature caused a single customer to lose money due to a system error, Biniyog's trust -- which I had spent 2 years building -- would collapse.
I needed to ship an algorithmic trading system that solved the immediate risk-management gap, built toward full automation responsibly, protected customer capital from system or market failure, and shipped within the competitive window.
But the real task was harder than the brief: how do you ship automation that touches real money when your infrastructure is flaky, your team is small, and your users have never used a conditional order before?
A differentiated, buzzworthy feature to match the competitor's marketing and position Biniyog as technologically advanced.
A sequenced approach that validated the execution engine on lower-stakes features before asking customers to hand over full strategy control.
The CEO and engineering lead both wanted to jump straight to the "sexy" automated trading model. I pushed back.
My reasoning: Conditional orders (Stop Loss / Take Profit) solve the most common pain point -- protecting an existing position -- with a UX pattern customers already understand. They validate the underlying order-matching infrastructure at lower stakes. They build customer and support-team confidence before we ask anyone to hand over full strategy control. And they can ship in 6 weeks, not 4 months.
The trade-off: We would not have a differentiated "AI trading" headline at launch. The competitor would still beat us to the buzzword. But we would have a working, trusted feature they did not have.
Engineering's first wireframe had both Stop Loss and Take Profit on the same screen, with advanced options exposed by default. It looked like a Bloomberg terminal. I rejected it.
My reasoning: 85% of our users had never placed a conditional order before. The DSE does not support native conditional orders -- we were building the abstraction ourselves. Any confusion would result in real financial loss. A "simple" sell order with a trigger condition was the right mental model.
What I specified instead:
The trade-off: Power users would need an extra tap. But we would avoid the #1 failure mode in financial UX: accidental orders.
For the automated trading engine (Phase 2), the obvious path was to pick one well-known technical indicator -- say, RSI or MACD -- and trigger trades when it crossed a threshold. Simple to explain, simple to build.
I rejected this.
My reasoning: Any single indicator fires often and is wrong often. In a thin market like the DSE, false signals would cost customers real money. A trading engine that acts on noise would destroy trust faster than it could build it. I had no data scientist to tune a complex model, but I could specify a rules-based fusion approach.
What I specified instead: The engine requires three independent signals to agree before placing a trade:
| Signal | Role | Why It Matters |
|---|---|---|
| Trend / Momentum | Directional bias | Confirms the stock is moving in the expected direction |
| Candlestick Pattern | Reversal confirmation | Identifies classical turning points (doji, engulfing, etc.) |
| Volume Confirmation | Participation check | Ensures the move has real market depth, not just noise |
| Price Sanity Check | Floor guard | Proximity to exchange circuit-breaker floor prevents "falling knife" catches |
Why signal fusion, not a single rule: Any one of these signals fires often and is wrong often. I specified that a trade only happens when the trend/pattern read agrees with volume confirmation and clears the price sanity check -- trading conviction over trading frequency. This was a deliberate product trade-off: fewer trades, each with a stronger evidentiary basis, rather than a hyperactive bot chasing every blip.
The moment a system can place trades without human confirmation, the product risk profile changes completely. I made a controversial call: the guardrails would be specced before the trading logic, not after.
What I specified:
| Guardrail | Mechanism | Failure Mode Prevented |
|---|---|---|
| Balance check | Verify sufficient shares/cash before every trade | Insufficient funds -> rejected order -> customer notified |
| Stale price rejection | Null/zero price data blocks any order attempt | Feed outage -> no phantom trades |
| Auto-halt + notify | Model stops itself on any guardrail trigger + emails customer | Silent failures become visible |
| Stale order re-evaluation | Unfilled orders re-priced against current market | Orders do not sit at obsolete prices |
| One-active-enrollment rule | One model per instrument at a time | Prevents conflicting strategies |
| Admin kill-switch | Risk team can bulk-pause any enrollment | Business retains control |
The principle I held to throughout: An automated feature earns the right to act on a customer's behalf only if it can explain itself, protect itself from its own failure modes, and be shut off -- by the customer or by the business -- at any moment. I treated all three as launch requirements, not post-launch improvements.
Because this feature places trades automatically with real money, I authored the customer-facing Algorithmic Trading Terms & Conditions myself -- covering market, system, technical, liquidity, leverage, and regulatory risk.
Why I did not hand this off: Generic legal templates do not capture the specific failure modes of a signal-fusion engine on the DSE. I needed to understand every risk to design against it. Explicit, checkbox-based acceptance became a mandatory gate before enrollment.
The trade-off: I spent 4 days writing legal copy instead of wireframing. But the risk team signed off in one review instead of the usual 3-week back-and-forth.
Customers experience Algorithmic Trading as a single, coherent capability inside the app - a way to make the platform act on their behalf. Underneath, it's built from two engines that share the same core mechanism (a condition is set, the market is watched continuously, and an order is generated automatically when the condition is met) but differ in who defines the strategy.
Why build both: Conditional orders meet customers where they already are - it's a sell order with a trigger, a familiar mental model. The signal-driven model is a bigger leap - the customer is trusting the platform with a strategy. Shipping the familiar version first let customers, support, and the risk team build confidence in the underlying execution engine before we asked anyone to hand over full control.
I redesigned the existing Sell flow to support conditional orders without disrupting the standard "sell now" path customers already knew. Two new toggles - Stop Loss and Take Profit - sit below the quantity field; selecting either replaces the manual price entry with a condition-setting flow.
The customer enters the exact price at which the order should fire - useful for a customer who already has a specific exit price in mind from their own research and wants the platform to act the instant the market reaches it.
The customer enters a percentage instead, and the app computes and displays a live, read-only "probable price" against their own break-even average as they type - so they always see the real price implication of a percentage before confirming.
A few UX decisions mattered as much as the underlying logic: the expiry date is capped to a rolling window so conditional orders can't sit open indefinitely and become forgotten liabilities; the customer's buy average is surfaced automatically as a read-only reference so they're setting a condition with full context; and before final submission, the confirmation screen lists every active condition in plain language with an explicit acknowledgement checkbox the customer must tick - a deliberate compliance-driven UX pattern I specified so nobody sets a standing market order by accident.
Once confirmed, the order sits as an open condition rather than an immediate trade. I specified a continuous monitoring process that checks every open condition against the live market throughout the trading session - comparing the current price, and the customer's live gain/loss position, against the rule they set. The moment a condition is met, the platform automatically generates a real sell order at the prevailing market price on the customer's behalf.
Handling the messy real-world cases: I specified explicit handling for the scenarios that break naive implementations - orders that only partially fill, conditions that expire unmet, and customers who cancel a standing condition after it has already triggered a downstream order. In every case the customer's position and the order history stay consistent, and a cancelled condition automatically cancels anything it had already spawned so nothing is left orphaned.
On the Order Status screen, pending conditional orders are always surfaced above historical ones, each row shows its trigger condition in plain language, and every generated trade nests under the condition that created it in an expandable history - so a customer can trace exactly why a trade happened, not just that it happened.
The second system goes further than a customer-set trigger: the customer enrols a holding into a pre-built model, and from that point, the platform's signal engine decides when to buy and sell - within the boundaries the customer set at enrollment.
Not every holding is eligible for every model. I specified the eligibility rule as the intersection of two sets: companies the model has been backtested against, and companies the customer actually holds in sufficient quantity - ranked in the picker by historical backtest efficiency so the customer sees the model's strongest candidates first. Margin customers are additionally restricted from higher-risk market categories and non-eligible instrument types as a house risk policy, and a holding can only be enrolled in one active model at a time.
Rather than acting on a single technical reading, the engine requires several independent signals to agree before it places a trade - reducing the chance of reacting to noise.
Why signal fusion, not a single rule: Any one of these signals fires often and is wrong often. I specified that a trade only happens when the trend/pattern read agrees with volume confirmation and clears the price sanity check - trading conviction over trading frequency. This was a deliberate product trade-off: fewer trades, each with a stronger evidentiary basis, rather than a hyperactive bot chasing every blip.
Every enrollment captures a snapshot of the customer's position - matured stock balance, buy average, and break-even average - at the moment they join. From that snapshot, I specified a live Current Efficiency figure that shows the improvement achieved since enrollment, and a Model Profit/Loss figure computed from the realised trades the model has actually made. When a customer's improvement target is achieved, the model exits and deactivates itself automatically, and I specified a performance-report email so the outcome is documented outside the app, not just visible in a screen the customer might not revisit.
Order Status and the portfolio table both carry a distinct automation marker on anything the model generated, and the platform blocks manual edits to those orders - and separately warns a customer if they try to manually trade a holding that's already enrolled in an active model, so they don't unknowingly work against their own automated strategy.
The moment I specified a system that could place trades without a human confirming each one, the guardrails around it became as important a deliverable as the trading logic. I treated this as core product scope, not a follow-on hardening pass.
Because this feature places trades automatically, I authored the customer-facing risk disclosure myself - covering market, system, technical, liquidity, leverage, slippage, and regulatory risk in plain language - and made explicit, checkbox-based acceptance a mandatory step before a customer can enrol in any model. I also specified an admin oversight portal for the risk and operations team: a paginated, filterable view of every enrollment across all customers, with the ability to search by customer, filter by model or status, and bulk-pause enrollments - giving the business a real kill-switch over the automated book, not just visibility into it.
The principle I held to throughout: An automated feature earns the right to act on a customer's behalf only if it can explain itself, protect itself from its own failure modes, and be shut off - by the customer or by the business - at any moment. I treated all three as launch requirements, not post-launch improvements.
| Metric | Result | Context |
|---|---|---|
| Conditional order adoption | 34% of active traders within 60 days | Baseline: 0% (feature did not exist). Target was 20%. |
| System error rate | 0% | No erroneous trades in first 6 months. Guardrails fired as designed. |
| Model enrollment rate | 18% of eligible customers | "Eligible" = backtested + sufficient balance. Higher than projected 12%. |
| Retention through target | 82% | Only 18% panic-cancelled on first market dip. Risk disclosure worked. |
| Support tickets per automated user | 0.4 vs. 1.2 for manual traders | Lower support burden, not higher. Transparency design paid off. |
| Signal engine agreement rate | 23% | Trend + pattern + volume aligned 23% of the time -- confirming selectivity. |
| Trade volume lift | +22% for algo users vs. non-users | Higher engagement without higher risk. Business case validated. |
User stories, acceptance criteria, and the condition-to-execution pipeline as specified in the product requirements - presented here without exposing underlying implementation or data details.
| ID | As a… | I want… | So that… |
|---|---|---|---|
| US.1 | Customer holding a position who can't watch the market all day | to set a Stop Loss or Take Profit condition once, at a price or percentage I choose | the platform protects my gain or caps my loss automatically, without me monitoring the price |
| US.2 | Customer setting a percentage-based condition | to see the exact price my percentage translates to before I confirm | I understand precisely what will happen rather than reasoning in the abstract about a percentage |
| US.3 | Customer considering enrolling a holding in an automated trading model | to see which models are actually eligible for that holding, ranked by historical performance | I can make an informed choice instead of guessing which strategy suits my position |
| US.4 | Customer enrolled in an automated model | a clear, live view of how the model is performing against my entry point, and the ability to exit any time | I always stay in control even though the platform is executing trades on my behalf |
| # | Criteria | Owner | Verified by |
|---|---|---|---|
| AC.1 | Selecting Stop Loss or Take Profit must hide the standard manual price fields and constrain the expiry date picker to the allowed window. Buy average must display as a read-only reference. | FE | Design review: form state verified for both toggles; expiry picker manually tested against boundary dates |
| AC.2 | Percentage mode must compute and display a live probable price as the customer types, before submission. | FE | QA: verify the displayed probable price updates in real time and matches the confirmation screen value |
| AC.3 | An automated model must never place a buy or sell order without a passing balance guardrail check immediately beforehand; a failed check must halt the model and notify the customer. | BE | QA: simulate insufficient balance and confirm the model halts and the notification is sent, not silently retried |
| AC.4 | Every order generated by a conditional order or an automated model must carry a distinct marker wherever it's displayed, and must not be manually editable by the customer. | FE | Design review: marker visible in Order Status and Portfolio views; edit controls confirmed disabled on automated rows |
| AC.5 | Enrollment in any automated model must be blocked until the customer has explicitly accepted the risk disclosure via a dedicated checkbox step. | FE / Legal | QA: verify the Save action is disabled until acceptance is recorded; legal sign-off on disclosure content |
How a customer's rule - manual or automated - becomes a real trade on the Biniyog platform.