Trading Bot in C# — Part 7— Getting Trade History
In the previous blog post, we discussed reconnecting strategies to deal with network issues. This article will offer an opportunity to take a look at your past orders trades.
First, let’s clarify what we mean by “trade.” A trade refers to an event in which the order book matching engine pairs your order with another one. In other words, a trade represents either a partial or a full fill of an order.
Motivation
It’s easy to get caught up in current or upcoming trading opportunities for profit. However, failing to reflect on past experiences can lead to repeated mistakes. Just like in other areas, learning from previous errors is crucial in trading as well.
What insights can your past trades reveal? More frequently than one might expect, they reveal a tendency to deviate from one’s trading strategy due to a lack of discipline. An effective trading strategy minimizes the risk of losing capital; therefore, if your trading data suggests that you do not adhere to it, the risk increases, leading to additional stress. For example, traders might place both a take-profit order and a stop-loss order simultaneously to ensure they either secure a profit or incur only a small loss. However, even institutional traders occasionally overlook risk management rules. A notable example occurred at the French bank Société Générale in 2007 and 2008, when Mr. Jérôme Kerviel incurred a loss of €4.9 billion for the bank by taking on large unauthorized directional positions without the necessary hedging that was required by the company.
Time and time again, many individuals — even very intelligent and experienced ones — slowly start believing that they finally fully grasp the market’s behavior and can accurately predict future price movements. They deviate from their strategy by making high-risk trades, often resulting in swift consequences.
“My biggest losses have always followed my largest profits.” (Martin Schwartz)
Certain aspects of “being human” can be addressed by implementing the trading strategy in Whale’s Secret ScriptApiLib. Each such strategy runs on a machine, thus it is not prone to experiencing fatigue, becoming emotional, or diverting from strategy rules.
While learning from the past is important, there are more practical reasons for retrieving one’s past trades: taxes. Governments worldwide have intensified their regulations regarding cryptocurrencies, and one key aspect of this is how cryptocurrencies should be taxed. At first glance, this may seem unusual, as currencies are not typically subject to taxation. However, tax authorities do not view cryptocurrencies, such as Bitcoin, as traditional fiat currencies. Instead, crypto currencies are typically classified as either property or commodities. Additionally, tokens offered in an Initial Coin Offering (ICO), or similar schemes, may be categorized as securities.
Why do we bring up this seemingly dull information? The answer is straightforward. Depending on your tax residency, each trade could be considered a taxable event. Therefore, it’s important to keep track of all your trades to accurately calculate your taxes.
API
ScriptApiLib allows to retrieve historical trades using the GetTradesAsync
method. We might want to retrieve yesterday’s trades:
ITradeApiClient client = await scriptApi.ConnectAsync(ExchangeMarket.KucoinSpot);
Print("Connected to KuCoin.");
// Suppose, we are interested in trades that were executed yesterday.
DateOnly yesterdayDate = DateOnly.FromDateTime(DateTime.UtcNow).AddDays(-1);
IReadOnlyList<ITrade> trades = await client.GetTradesAsync(yesterdayDate);
Print($"For {yesterdayDate}, there were {trades.Count} trades:");
foreach (ITrade trade in trades)
{
Print($"* {trade}");
}
The sample output is:
[09:00:11] Connected to KuCoin.
[09:00:11] For 2025-06-10, there were 2 trades:
[09:00:11] #1: [SymbolPair=`BTC/USDT`,Side=Sell,ExchangeOrderId=`o-6847ffb88c21700007fc1510`,TradeId=`15542552101404673`,Timestamp=06/10/2025 09:49:44,Price=109355.5,BaseQuantity=0.00004686,QuoteQuantity=5.12439873,CommissionAmount=0.00512439873,CommissionAsset=`USDT`,IsMaker=False]
[09:00:11] #2: [SymbolPair=`BTC/USDT`,Side=Buy,ExchangeOrderId=`o-6848003f759520000728a118`,TradeId=`15542582918397953`,Timestamp=06/10/2025 09:51:59,Price=109376.7,BaseQuantity=0.00004571,QuoteQuantity=4.999608957,CommissionAmount=0.004999608957,CommissionAsset=`USDT`,IsMaker=False]
TheGetTradesAsync
method retrieves trades for a specific date, as exchanges generally offer API to handle it with just one exchange API request. Additionally,GetTradesAsync
method caches the data, making subsequent calls to the method more efficient.
In the earlier code example, we retrieved KuCoin’s trades. Unsurprisingly, ScriptApiLib offers the same API for Binance as well. However, there’s a significant caveat to consider. Typically, one wants to get trades for a certain time period. Generally, when getting trades for a specific time frame, Binance allows you to retrieve them, but only for one symbol at a time (e.g., BTC/USDT
). This limitation makes obtaining a comprehensive trading history from Binance quite challenging. ScriptApiLib handles the limitation by introducing a set of heuristics to mitigate the issue.
Filtering Options
ScriptApiLib also provides a basic filtering API. For example, you might want to fetch only buy-limit orders for a specific date:
TradeFilterOptions options = new()
{
TradeSide = OrderSide.Buy,
OrderTypes = new HashSet() { OrderType.Limit }
};
IReadOnlyList trades = await client.GetTradesAsync(date, options);
Please note that for purposes of data mining, we recommend to download trades and feed them into a database. This approach gives you full expressive power to analyze your trades, calculate statistics, etc.
Historical Orders
If you take a closer look at the ITrade
type, you may notice that the properties of the corresponding order are not directly included; instead, they are accessed through the ITrade.Order
property. It is worthwhile to take a moment to understand the relationship between historical orders and historical trades.
A typical exchange offers an API endpoint to access historical trades for a specific time period, as well as another endpoint for historical orders within the same time frame. But there is a caveat that makes things harder: An order can be created at a certain day and filled on the following day or any subsequent day. In practice, we cannot determine when an order was created based on a trade, so we need to rely on heuristics to provide data quickly and to save API requests as well.
As ScriptApiLib does the heavy-lifting, you can achieve your task by writing a few lines of code:
IReadOnlyList<ITrade> trades = await client.GetTradesAsync(yesterdayDate);
Print($"For {yesterdayDate}, there were {trades.Count} orders:");
foreach (ITrade trade in trades)
{
Print($"Trade {trade.TradeId} belongs to order '{trade.Order}'.");
}
However, if you need just to retrieve historical orders, the API is straightforward again:
DateOnly yesterdayDate = DateOnly.FromDateTime(DateTime.UtcNow).AddDays(-1);
IReadOnlyList orders = await client.GetOrdersAsync(yesterdayDate);
Print($"For {yesterdayDate}, there were {orders.Count} orders:");
foreach (IOrder order in orders)
{
Print($"* {order}");
}
Conclusion
In this blog post, we presented API for retrieving historical orders and trades in ScriptApiLib, allowing users to reflect on past successes or failures, as well as to prepare data for tax reporting purposes. Next time, we will discuss how to access and work with account balance data.
Disclaimer: The information provided in this blog post should not be considered financial advice. Always conduct your own research and consider your personal circumstances before acting on any financial information.