object oriented – Rust backtest forex engine

I wrote a backtest engine in Rust – essentially it is a bunch of if statements mutating variables in a intensive loop.

Recently I had to rewrite the whole because it was getting too complicated for me to understand.

I first wrote the code in my usual procedural style with functional iterators. When it started getting complicated, only thing that made sense was object-oriented style – a big struct with many methods.

It’s easier now and makes sense. Is object-oriented style idiomatic Rust for this kind of problem?

Here is the core code

object-oriented

loop

let mut engine = Backtest{
        instruments,
        strategies,
        algos: data.clone(),
        combos,
        timestamps,
        candles:all_candles.clone(),
        equity: accounts(0).balance,
        initial_balance: accounts(0).balance,
        balance: accounts(0).balance,
        entry_candle: None,
        entry_timestamp_option: None,
        tp_price: None,
        sl_price: None,
        combo: None,
        instrument_candles_option: None,
        instrument_option: None,
        entry_candle_iter_option: None,
        entry_candle_index: 0,
        index_time: None,
        index_candle: None,
        candles_cursor: 0,
        prog_candles: vec!(),
        progs: vec!(),
        entry_price: None,
        exit_price: None,
        wl: None
    };
loop {
        if engine.is_account_drawdown_breached() { break; }

        if engine.entry_candle.is_none() {

            let pti = engine
                .get_previous_timestamp_id();

            let next_combo_candle = engine
                .get_next_combo_candle(&combo_candles,pti);

            if next_combo_candle.is_none() { break; }

            engine.set_instrument_by_candle(next_combo_candle.unwrap());
            engine.set_instrument_candles_by_instrument();

            let ci_from_instrument_candles =
                engine.get_candle_index_from_instrument_candles_by_candle(
                        next_combo_candle.unwrap());
            if engine.is_last_instrument_candle(ci_from_instrument_candles.unwrap()) { break; }

            engine.entry_candle_index = ci_from_instrument_candles.unwrap()+1;
            engine.candles_cursor = ci_from_instrument_candles.unwrap()+1;
            engine.set_entry_candle();
            engine.set_entry_timestamp();
            engine.set_combo();
            engine.set_tp_price();
            engine.set_sl_price();
            engine.set_entry_price();
        }

        // set up candle index
        engine.set_index_candle();
        if engine.is_last_index_candle() { break; }
        engine.set_index_time();
        engine.add_to_prog_candles(engine.index_candle.unwrap());

        engine.set_exit_price();
        engine.set_balance();
        engine.add_to_progs();

        engine.reset_prog_candles();

        engine.increment_candles_cursor();
    }

struct with methods

use crate::db::model::define::*;
use chrono::Duration;

pub struct Backtest {
    pub instruments:Vec<Instrument>,
    pub strategies:Vec<Strategy>,
    pub algos: Vec<Algo>,
    pub combos: Vec<Combo>,
    pub timestamps: Vec<Timestamp>,
    pub candles:Vec<CandleTable>,
    pub equity:f64,
    pub initial_balance:f64,
    pub balance:f64,
    pub entry_candle:Option<CandleTable>,
    pub entry_timestamp_option:Option<Timestamp>,
    pub tp_price:Option<f64>,
    pub sl_price:Option<f64>,
    pub combo:Option<Combo>,
    pub instrument_candles_option:Option<Vec<CandleTable>>,
    pub instrument_option:Option<Instrument>,
    pub entry_candle_iter_option:Option<std::vec::IntoIter<CandleTable>>,
    pub entry_candle_index:usize,
    pub index_time:Option<Timestamp>,
    pub index_candle:Option<CandleTable>,
    pub candles_cursor:usize,
    pub prog_candles:Vec<CandleTable>,
    pub progs:Vec<Prog>,
    pub entry_price:Option<f64>,
    pub exit_price:Option<f64>,
    pub wl:Option<f64>,
}


impl Backtest {

    pub fn is_account_drawdown_breached(&self) -> bool{
        self.balance <= self.initial_balance / 2.0 || self.equity < 0.0
    }

    pub fn is_next_combo_candle_required(&self) -> bool{
        self.entry_candle.is_none()
    }

    pub fn is_last_instrument_candle(&self, index:usize) -> bool{
        self.instrument_candles_option.as_ref().unwrap().get(index+1).is_none()
    }

    pub fn is_last_index_candle(&self) -> bool{
        self.index_candle.is_none()
    }

    pub fn is_trade_expired(&self) ->bool{
        self.index_time
            .unwrap()
            .timestamp
            .signed_duration_since(
                self.entry_timestamp_option.unwrap().timestamp) > Duration::weeks(2)
    }

    pub fn is_buy(&self) -> bool {
        self.combo.as_ref().unwrap().action == 1
    }

    pub fn is_sell(&self) -> bool {
        self.combo.as_ref().unwrap().action == 0
    }
    pub fn set_exit_price(&mut self){
        if self.is_trade_expired() {

            if self.is_buy() {
                self.exit_price = Some(self.index_candle.unwrap().bid_open_num);
            } else {
                self.exit_price = Some(self.index_candle.unwrap().ask_open_num);
            }

        } else if self.is_stop_loss_reached() {
            if self.is_buy() {
                self.exit_price = Some(self.index_candle.unwrap().bid_low_num)
            } else {
                self.exit_price = Some(self.index_candle.unwrap().ask_high_num)
            }
        } else if self.is_take_profit_reached() {
            if self.is_buy() {
                self.exit_price = Some(self.index_candle.unwrap().ask_high_num)
            } else {
                self.exit_price = Some(self.index_candle.unwrap().bid_low_num)
            }
        }
    }

}

procedural

everything

    let account = &accounts(0);
    let strategy = &strategies(0);
    let equity = account.balance;
    let mut balance = account.balance;
    let mut entry_candle:Option<CandleTable> = None;
    let mut entry_timestamp_option:Option<&Timestamp> = None;
    let mut tp_price:Option<f64> = None;
    let mut sl_price:Option<f64> = None;
    let mut combo:Option<&Combo> = None;
    let mut instrument_candles_option:Option<Vec<CandleTable>> = None;
    let mut instrument_option:Option<&Instrument> = None;
    let mut entry_candle_iter_option:Option<std::vec::IntoIter<CandleTable>> = None;
    let mut entry_candle_index = 0;
    let mut index_time:Option<Timestamp> = None;
    let mut index_candle:Option<CandleTable> = None;
    let mut candles_cursor = 0;
    let mut history:History = History{..Default::default()};
    let mut histories:Vec<History> = vec!();

    let mut prog_candles:Vec<CandleTable> = vec!();
    let mut progs:Vec<Prog> = vec!();
    let mut entry_price:Option<f64> = None;
    let mut exit_price:Option<f64> = None;
    let mut wl:Option<f64> = None;


    //Backtest loop

    loop {

        // TODO - add drawdown to db
        if balance <= account.balance / 2.0 || equity < 0.0 {
            break;
        }

        /// Set trade_candle, trade_time, sl, tp
        ///
        /// trade_candle?
        /// ! Entry candle (point of entry) is trade candle.
        /// ! Signal candle is candle where a combo is identified.
        /// Find trade_candle by getting latest combo candle
        /// that hasn't been traded. The entry candle would
        /// be in front.
        ///
        /// trade_time?
        /// timestamp of trade_candle
        ///
        /// sl?
        /// sl from trade_candle
        ///
        /// tp?
        /// tp from trade_candle
        if entry_candle.is_none() {

            // set last known timestamp index
            let last_known_tindex = if entry_candle_index == 0 {
                0 as usize
            } else {
                if index_time.is_none() {
                    println!("{:?} {:?}",index_candle,index_time);
                    unreachable!()
                }
                index_time
                    .unwrap()
                    .id.unwrap() as usize + 1 as usize
            };

            // Get signal candle
            let signal_candle = combo_candles
                .iter()
                .find(|x|
                    x.timestamp_id >= last_known_tindex as i64);
            if signal_candle.is_none() {
                break;
            }

            // set instrument,
            // instrument_id,
            // instrument_candles,
            // signal_candle_index,
            // and check for valid entry candle
            // then set entry candle index,
            // entry candle,
            // entry timestamp
            let instrument_id = signal_candle.unwrap().instrument_id;
            instrument_option = instruments
                .iter()
                .find(|x| x.id.unwrap() == instrument_id);
            let instrument_candles = candles
                .iter()
                .filter(|x| x.instrument_id == instrument_id)
                .cloned()
                .collect::<Vec<_>>();
            instrument_candles_option = Some(instrument_candles.clone());
            let signal_candle_index = instrument_candles
                .iter()
                .position(|x|x.timestamp_id == signal_candle.unwrap().timestamp_id);
            if signal_candle_index.is_none() ||
                instrument_candles.get(signal_candle_index.unwrap()+1).is_none() {
                break;
            }
            entry_candle_index = signal_candle_index.unwrap()+1;
            candles_cursor = entry_candle_index;
            entry_candle = instrument_candles
                .get(entry_candle_index)
                .cloned();
            entry_timestamp_option = timestamps
                .iter()
                .find(|x|x.id.unwrap() == entry_candle.unwrap().timestamp_id);


            prog_candles.push(entry_candle.unwrap());


            // set entry candle iter
            let entry_candle_iter = instrument_candles
                .into_iter();
            entry_candle_iter_option = Option::from(entry_candle_iter);

            // Set combo
            combo = combos
                .iter()
                .find(|x| x.has_instrument_id(instrument_id))
                .or(Option::from(combos.get(0)));

            if combo.as_ref().unwrap().action == 1 {
                sl_price = Some(entry_candle.unwrap().bid_open_num - (strategy.sl / instrument_option.unwrap().decimal_place_value as f64));
                tp_price = Some(entry_candle.unwrap().bid_open_num + (strategy.tp / instrument_option.unwrap().decimal_place_value as f64));
                entry_price = Some(entry_candle.unwrap().bid_open_num)
            } else {
                sl_price = Some(entry_candle.unwrap().ask_open_num + (strategy.sl / instrument_option.unwrap().decimal_place_value as f64));
                tp_price = Some(entry_candle.unwrap().ask_open_num - (strategy.tp / instrument_option.unwrap().decimal_place_value as f64));
                entry_price = Some(entry_candle.unwrap().ask_open_num)
            }
        }

        // necessary vars
        index_candle = instrument_candles_option
            .clone()
            .unwrap()
            .get(candles_cursor)
            .cloned();
        if index_candle.is_none() {
            println!("{:?} {:?} {} {:?}",entry_candle,instrument_option,candles_cursor,
            instrument_candles_option.clone().unwrap().get(3025));
            println!("{} {}","out of bounds",balance);
            break;
        }
        index_time = timestamps
            .iter()
            .cloned()
            .find(|x|x.id.unwrap() == index_candle.unwrap().timestamp_id);

        // 1. if trade expired, perform 2. or 3.
        // 2. if sl reached, deduct losses from balance
        // 3. if tp reached, add profit to balance
        let _sl_price = sl_price.unwrap();
        let _tp_price = tp_price.unwrap();

        //TODO - trade expiry duration should be in strategy db
        //TODO - handle edge case - expiry candle could open in loss or profit
        if index_time.unwrap().timestamp.signed_duration_since(entry_timestamp_option.unwrap().timestamp) > Duration::weeks(2) {
            if combo.as_ref().unwrap().action == 1 {
                exit_price = Some(index_candle.unwrap().bid_open_num);
                let pl = (index_candle.unwrap().bid_open_num - entry_candle.unwrap().bid_open_num) * instrument_option.unwrap().decimal_place_value as f64;
                balance = balance + pl;
                wl = Some(pl);
            } else {
                exit_price = Some(index_candle.unwrap().ask_open_num);
                let pl = (entry_candle.unwrap().ask_open_num - index_candle.unwrap().ask_open_num) * instrument_option.unwrap().decimal_place_value as f64;
                balance = balance + pl;
                wl = Some(pl);
            }


            progs.push(Prog{
                id: None,
                candles:prog_candles.clone(),
                instrument: instrument_option.unwrap().clone(),
                combo: combo.unwrap().clone(),
                entry_price: entry_price.unwrap(),
                exit_price: exit_price.unwrap(),
                wl: if wl.unwrap() > 0.0 {1} else { 0 }
            });

            entry_candle = None;
            tp_price = None;
            sl_price = None;
            instrument_candles_option = None;
            instrument_option = None;
        } else if (combo.as_ref().expect("combo issue").action == 1 && index_candle.expect("index candle issue").bid_low_num as f64 <= _sl_price) ||
            (combo.as_ref().expect("combo issue").action == 0 && index_candle.expect("index candle issue").ask_high_num as f64 >= _sl_price) {
            balance = balance - strategy.sl;
            // todo - not entirely correct but serves the point
            if combo.unwrap().action == 1 {
                exit_price = Some(index_candle.unwrap().bid_low_num)
            } else {
                exit_price = Some(index_candle.unwrap().ask_high_num)
            }


            progs.push(Prog{
                id: None,
                candles:prog_candles.clone(),
                instrument: instrument_option.unwrap().clone(),
                combo: combo.unwrap().clone(),
                entry_price: entry_price.unwrap(),
                exit_price: exit_price.unwrap(),
                wl: 0
            });

            entry_candle = None;
            tp_price = None;
            sl_price = None;

            instrument_candles_option = None;
            instrument_option = None;
        } else if (combo.as_ref().unwrap().action == 1 && index_candle.unwrap().ask_high_num as f64 >= _tp_price) ||
            (combo.as_ref().unwrap().action == 0 && index_candle.unwrap().bid_low_num as f64 <= _tp_price) {
            balance = balance + strategy.tp;
            if combo.unwrap().action == 1 {
                exit_price = Some(index_candle.unwrap().ask_high_num)
            } else {
                exit_price = Some(index_candle.unwrap().bid_low_num)
            }


            progs.push(Prog{
                id: None,
                candles:prog_candles.clone(),
                instrument: instrument_option.unwrap().clone(),
                combo: combo.unwrap().clone(),
                entry_price: entry_price.unwrap(),
                exit_price: exit_price.unwrap(),
                wl: 1
            });

            entry_candle = None;
            tp_price = None;
            sl_price = None;

            instrument_candles_option = None;
            instrument_option = None;
        } else {
            candles_cursor = candles_cursor + 1;
        }

    }
```

Xxl Forex Real Profit Ea

 

How I Make $1000 In One Week With Forex Copy Trading

IPB Image

Earn easy and invest in top traders!
Even if you are a beginner, you can benefit from financial markets thanks to our copy service!

See More

It is really simple to get started!
Choose traders with the best results and get the same results for you!
Choose most liked Get the same result Earn Money

Copy other’s success.
No minimum deposit to start! Use others’ knowledge to earn!

Best Forex Brokers For Social Copy Trading

Join millions who’ve already discovered smarter investing by automatically copying the leading traders in our community, or get copied yourself to earn a second income.

100% stocks, 0% commission
The advantages of buying stocks on eToro don’t end with pricing. There are also no limits on commission-free trades and you can buy fractional shares.

Join Now

Trade and invest in top stocks and ETFs. Trade currency pairs, Indices and Commodities via CFDs. Investing in the financial markets has never been easier.
Start Trading

Join the Social Trading revolution. Connect with other traders, discuss trading strategies, and use our patented CopyTrade technology to automatically copy their trading portfolio performance.
Discover People

Join Now

beercheers.gif

Start Copy Trading Your Perfect Forex Partner

Copy trading Masters or copyists earn extra stable income-an investment platform for everyone

Copy trading provides customers with the opportunity to automatically copy the strategies of leading traders without having to spend a long time developing their own trading strategies. Choose from the best foreign exchange trading masters and diversify your trading portfolio.

Follow the best traders
Find the master you wish to follow and click ‘copy’. Its positions will be copied automatically. The deposit percentage setting will help you manage your investment portfolio. Try to copy different strategies and decide the best one from them!

Follow the best traders Monitor and profit
There is no limit to the number of master traders you can copy. Therefore, you can create a balanced and diversified trading portfolio and obtain stable income. You can also fully control the entire process and revise/stop copying transactions at any given time. You can view the detailed transaction data of the copy master trader in your copying area.

Monitor and monetize

Follow the most profitable traders

No need to be a foreign exchange expert

Stable income from a diversified portfolio

COPY TRADING

Enjoy all the advantages of copying transactions in the mobile App!

Zulutrade Social Forex Trading

Auto Trading with ZuluTrade is a cutting-edge auto trading platform that connects directly with both the MetaTrader 4 brand the MetaTrader 5 through an API. That means you can use it directly on these platforms.

Follow and copy seasoned traders
A wide selection of leading signal providers
Ranking based on performance, portfolio, and more
Risk Management and strategy tools
Be part of a dynamic trading community

Why ZuluTrade?

Simple and intuitive
Available for traders of all levels, the ZuluTrade trading platform is easy to use and has a user-friendly interface and accessible layout.

Proven and successful traders
Browse your way through ZuluTrade’s diverse list of expert traders and choose who to follow based on their success rates and the assets they trade.

ZuluGuard
You can protect your account and your capital against strategy changes, or in the event your chosen signal provider’s trades are unsuccessful.

Social trading
Become part of an active community of traders. Share ideas and your own strategy to pick up followers from around the world.

Trade from anywhere
You can web-trade on ZuluTrade, implement it on the MT4 or MT5 platform, or download the intuitive trading app onto your smartphone or tablet.

Practice Trading ZuluTrade
New to trading? Start your trading experience with a $100,000 demo, allowing you to practice trading and get assistance from the support team.

Best Forex Hft Robot 2021: Real Accounts Profit

Hello, dear traders!

The Best Forex Strategy 2021.
Only Real Accounts Monitoring
http://westernpips.com/myfxbook_monitorings.html

Earn bitcoin arbitrage brokers 2021: trading story

Was used Westernpips Private 7 Software – this is a full-featured trading Multiterminal for latency HFT arbitrage with built-in algorithms for automated trading on any forex, crypto brokers using the technology of direct trading access to servers through a TCP connection.
Unlimited opportunities open up for you on the options of connecting fast / slow broker in any combination. Trade and fast quotes on FIX / API / ITCH protocols. The speed of order execution using the new technology is 15-70 ms faster! All orders look like placed manually!

You can also see the results of work on the investor password:

Live Trading Rezults

Reduced Size Image

Real Account Profit: + 287 % 2 581 USD
Real Account Profit: + 434 % 2 174 USD
• Real Accounts: 30433461, 30437895, 30436385
Invest Pass: westernpips7
Broker MT5: AdmiralMarkets-Live
Reduced Size Image
Real Account Profit: 15000 USD +869 %
• Real Account: 2040061
Invest Pass: westernpips7
Broker: XBTFX-MetaTrader5 (mt5.xbtfx.io

Reduced Size Image
Real Account Profit: + 162 % 4 604 USD
• Real Account: 10001785
Invest Pass: westernpips7
Broker: FBS-Real MT5
Reduced Size Image
Real Account Profit: 6 165 USD +5577 %
• Real Account: 2031974
Invest Pass: westernpips7
Broker: TradersDomainFX-MetaTrader5шком
Join our new group in telegram https://t.me/hft_arbitrage_westernpips
The newest video of our software, updates and trading results
you can see on our YouTube channel
https://www.youtube.com/channel/UCK6bNinUXzLSWQ59rpL-g_Q

Best regards, Sales / Westernpips Group Business Development Manager
Telegram: westernpips
WECHAT: westernpips
Email: (email protected)
Skype: group.westernpips

❓ASK – How to improve your skills in forex trading market? | Proxies123.com

Well this one is my favourit interest and i can talk about till tomorrow, to enter to the forex you have to know it first, after all you gotta learn some tool to know how in inevest to be a winner, guys its a long term too, wanna know in details just pm me

 

❓ASK – Forex Trading too risky? | Proxies123.com

I have lot interest in trading and i have already stepped into crypto trading but now i wanna start forex trading but problem is that i heard that forex trading too much risky.

Ofcourse, everything carries a certain amount of risk. That is the very nature of things in this universe. Evolution happened because creatures and organisms took certain risk to venture out of their safe cocoon.

Risk needs to be understood and then managed. Do you know trading? If you know, well, you need to find out the areas of risk and have a strategy to manage those risks.
If you do not know trading, then learn how different securities, currencies or commodities are traded. Learning is the key. if you are new to trading, I can teach you trading. It will be paid course, and one needs to spend a reasonably good time to master the art of trading. even then it does not eliminate RISK. Risk will follow wherever you go. But you can manage it, if you learn that part well.