22 .initial_cash = 100000.0,
23 .risk_free_rate = 0.03,
26 .execution_mean = 0.0,
27 .execution_stdev = 0.0,
29 .tx_cost_value = 0.001,
31 .take_profit_pct = NAN
50 if (open_time.
units != close_time.
units)
return 0.0;
51 int64_t diff = close_time.
mark - open_time.
mark;
53 switch (open_time.
units) {
55 seconds = (double)diff;
58 seconds = (double)diff / 1000.0;
61 seconds = (double)diff / 1000000.0;
64 seconds = (double)diff / 1000000000.0;
69 return seconds / (365.0 * 86400.0);
86 return price * (1.0 + (is_buy ? slippage : -slippage));
91 double random_factor = ((double)rand() / RAND_MAX - 0.5) * 2.0;
92 double random_slippage = mean + stdev * random_factor;
93 return price * (1.0 + (is_buy ? random_slippage : -random_slippage));
106 for (
int i = 0; i < vec->
capacity; i++) {
132 const WU_BasicPortfolio p = (
const WU_BasicPortfolio) portfolio;
133 double position_value = 0.0;
134 for (
int asset_idx = 0; asset_idx < p->num_assets; asset_idx++) {
138 return p->cash + position_value;
155 double price = signal.
price;
161 (WU_Portfolio) portfolio);
165 double target_proportion = signal.
quantity;
166 return portfolio_value * target_proportion * ps->
size_value / price;
168 return (portfolio->cash * ps->
size_value) / price;
173 (WU_Portfolio) portfolio);
174 double alloc_per_asset = portfolio_value / portfolio->num_assets;
175 double target_value = alloc_per_asset * ps->
size_value;
176 return target_value / price;
180 (WU_Portfolio) portfolio);
181 double target_proportion = signal.
quantity;
182 if (target_proportion < 0.0) target_proportion = 0.0;
183 if (target_proportion > 1.0) target_proportion = 1.0;
184 return portfolio_value * target_proportion * ps->
size_value
215 return cost + tx_cost;
231 portfolio->params.execution_policy,
true);
233 portfolio->params.execution_policy);
236 double cost_multiplier;
238 cost_multiplier = exec_price + portfolio->params.execution_policy.tx_cost_value / quantity;
240 cost_multiplier = exec_price * (1.0 + portfolio->params.execution_policy.tx_cost_value);
242 quantity = portfolio->cash / cost_multiplier;
244 portfolio->params.execution_policy);
248 .timestamp = signal.timestamp,
249 .quantity = quantity,
254 .tx_cost =
total_cost - (quantity * exec_price)
268 double total_cost = 0.0;
269 for (
int i = 0; i < vec->
capacity; i++) {
287 if (asset_index < 0 || asset_index >= portfolio->num_assets)
return;
293 if (total_quantity < 0) {
295 double abs_quantity = -total_quantity;
297 double abs_cost = -total_cost;
299 portfolio->params.execution_policy,
true);
300 double cost_to_close = abs_quantity * buy_price;
303 tx_cost = portfolio->params.execution_policy.tx_cost_value;
305 tx_cost = cost_to_close * portfolio->params.execution_policy.tx_cost_value;
307 double pnl = abs_cost - cost_to_close - tx_cost;
308 portfolio->cash -= cost_to_close + tx_cost;
309 portfolio->stats->accum_tx_fees += tx_cost;
315 if (portfolio->cash < -portfolio->params.borrow_params.limit)
return;
321 portfolio->stats->accum_tx_fees += result.
tx_cost;
333 double quantity,
double cost_basis,
double sell_price,
335 double proceeds = quantity * sell_price;
338 tx_cost = portfolio->params.execution_policy.tx_cost_value;
340 tx_cost = proceeds * portfolio->params.execution_policy.tx_cost_value;
342 double pnl = proceeds - cost_basis -
tx_cost;
343 portfolio->cash += proceeds -
tx_cost;
344 portfolio->stats->accum_tx_fees +=
tx_cost;
355 if (asset_index < 0 || asset_index >= portfolio->num_assets)
return;
362 if (total_quantity > 0) {
366 portfolio->params.execution_policy,
false);
370 }
else if (total_quantity < 0) {
372 double abs_quantity = -total_quantity;
376 portfolio->params.execution_policy,
true);
377 double cost_to_close = abs_quantity * buy_price;
380 tx_cost = portfolio->params.execution_policy.tx_cost_value;
382 tx_cost = cost_to_close * portfolio->params.execution_policy.tx_cost_value;
384 double pnl = abs_cost - cost_to_close -
tx_cost;
385 portfolio->cash -= cost_to_close +
tx_cost;
386 portfolio->stats->accum_tx_fees +=
tx_cost;
391 if (can_short && total_quantity == 0) {
393 double total_short_value = 0.0;
394 for (
int i = 0; i < portfolio->num_assets; i++) {
397 total_short_value += (-qty) * portfolio->positions[i]->last_price;
404 portfolio->params.execution_policy,
false);
405 double new_short_value = quantity * exec_price;
407 if (total_short_value + new_short_value > portfolio->params.borrow_params.limit)
return;
409 double proceeds = new_short_value;
412 tx_cost = portfolio->params.execution_policy.tx_cost_value;
414 tx_cost = proceeds * portfolio->params.execution_policy.tx_cost_value;
424 portfolio->cash += proceeds - tx_cost;
425 portfolio->stats->accum_tx_fees += tx_cost;
438 double pnl_pct = is_short ?
441 bool should_close =
false;
443 if (!isnan(portfolio->params.execution_policy.stop_loss_pct) &&
444 portfolio->params.execution_policy.stop_loss_pct > 0 &&
445 pnl_pct <= -portfolio->params.execution_policy.stop_loss_pct) {
449 if (!isnan(portfolio->params.execution_policy.take_profit_pct) &&
450 portfolio->params.execution_policy.take_profit_pct > 0 &&
451 pnl_pct >= portfolio->params.execution_policy.take_profit_pct) {
455 if (!should_close)
return false;
458 double price = is_short ?
459 execution_price(current_price, portfolio->params.execution_policy,
true) :
460 execution_price(current_price, portfolio->params.execution_policy,
false);
461 double cost_basis = abs_quantity * pos.
price;
474 int asset_index,
double current_price) {
475 assert(asset_index >= 0 && asset_index < portfolio->num_assets);
477 if (vec->
count == 0)
return;
478 for (
int i = 0; i < vec->
capacity; i++) {
481 if (!found)
continue;
494 WU_BasicPortfolio p = (WU_BasicPortfolio) portfolio;
496 return current_value - p->params.initial_cash;
504 double total_short_value = 0.0;
505 for (
int i = 0; i < p->num_assets; i++) {
508 total_short_value += (-qty) * p->positions[i]->last_price;
511 return total_short_value;
521 bool has_valid_signal =
false;
523 for (
int i = 0; i < count && !has_valid_signal; i++) {
526 has_valid_signal =
true;
530 if (!has_valid_signal)
return;
534 if (total_short_value > 0.0 && p->params.borrow_params.rate > 0.0 &&
535 p->last_update_time.mark != 0) {
537 double borrow_interest = total_short_value * p->params.borrow_params.rate * years_elapsed;
538 p->cash -= borrow_interest;
539 p->stats->accum_borrow_interest += borrow_interest;
542 p->last_update_time = current_time;
545 for (
int i = 0; i < count; i++) {
548 p->positions[i]->last_price = signal.
price;
557 for (
int i = 0; i < p->num_assets; i++) {
561 p->positions[i]->symbol, qty, value, p->positions[i]->last_price);
576 for (
int i = 0; i < count; i++) {
588 for (
int i = 0; i < count; i++) {
603 for (
int i = 0; i < count; i++) {
629 const WU_Signal* signals,
int count,
int buy_count) {
631 double cash_per_signal = use_cash_splitting
632 ? (p->cash / buy_count) : 0.0;
633 for (
int i = 0; i < count; i++) {
637 if (use_cash_splitting) {
638 double original_cash = p->cash;
639 p->cash = cash_per_signal;
641 double cash_used = cash_per_signal - p->cash;
642 p->cash = original_cash - cash_used;
654 if (!p->pending_orders)
return;
656 for (
int i = 0; i < p->num_assets; i++) {
657 WU_Signal pending = p->pending_orders[i];
682 WU_BasicPortfolio p = (WU_BasicPortfolio) portfolio;
683 int count = p->num_assets;
695 for (
int i = 0; i < count; i++) {
698 p->pending_orders[i] = signal;
720 WU_BasicPortfolio p = (WU_BasicPortfolio) portfolio;
722 for (
int i = 0; i < p->num_assets; i++) {
726 free(p->pending_orders);
739 if (!positions)
return NULL;
740 for (
int i = 0; i < num_assets; i++) {
743 for (
int j = 0; j < i; j++) {
765 const char* symbols[]) {
767 if (!portfolio)
return NULL;
769 for (num_assets = 0; symbols[num_assets] != NULL; num_assets++);
771 if (!portfolio->positions) {
777 portfolio->pending_orders = calloc(num_assets,
sizeof(
WU_Signal));
778 if (!portfolio->pending_orders) {
779 for (
int i = 0; i < num_assets; i++) {
782 free(portfolio->positions);
793 portfolio->params = params;
795 portfolio->num_assets = num_assets;
800 for (
int i = 0; i < num_assets; i++) {
802 portfolio->positions[i]->symbol, 0.0, 0.0, 0.0);
814 if (!portfolio || asset_index < 0
815 || asset_index >= portfolio->num_assets) {
829 if (!portfolio || asset_index < 0
830 || asset_index >= portfolio->num_assets) {
static double execution_price(double price, WU_ExecutionPolicy policy, bool is_buy)
Calculates the execution price based on the execution policy.
WU_BasicPortfolio wu_basic_portfolio_new(WU_PortfolioParams params, const char *symbols[])
This constructor function creates a new basic portfolio instance.
static double calculate_position_size(WU_BasicPortfolio portfolio, WU_Signal signal)
This function calculates the position size for a given signal based on the portfolio's position sizin...
static double compute_total_cost(double quantity, double price, WU_ExecutionPolicy policy)
Computes the total cost of a trade including transaction costs.
static void process_buy_signals(WU_BasicPortfolio p, const WU_Signal *signals, int count, int buy_count)
Helper function to process all buy signals.
static int count_buy_signals(const WU_Signal *signals, int count)
Helper function to count valid buy signals.
double wu_basic_portfolio_asset_quantity(WU_BasicPortfolio portfolio, int asset_index)
Computes the total quantity held for a specific asset by summing the quantities of all active positio...
static double calculate_years_held(WU_TimeStamp open_time, WU_TimeStamp close_time)
Helper function to calculate the time difference in years between two timestamps, accounting for diff...
WU_PortfolioParams wu_portfolio_params_default(void)
This file implements a basic portfolio management system that supports multiple assets with a shared ...
static double calculate_total_short_value(WU_BasicPortfolio p)
Helper function to calculate total value of all short positions across all assets in the portfolio.
static void check_and_close_positions(WU_BasicPortfolio portfolio, int asset_index, double current_price)
This function iterates through all active positions for a given asset and checks if any of them can b...
static void execute_buy(WU_BasicPortfolio portfolio, WU_Signal signal, int asset_index)
This function executes a buy signal.
static void process_sell_signals(WU_BasicPortfolio p, const WU_Signal *signals, int count)
Helper function to process all sell signals.
static void close_and_update_portfolio(WU_BasicPortfolio portfolio, double quantity, double cost_basis, double sell_price, WU_CloseReason reason)
This function handles the logic for closing a position and updating the portfolio's cash balance and ...
static void execute_pending_orders(WU_BasicPortfolio p)
Helper function to execute pending orders from previous update.
static bool close_position(WU_BasicPortfolio portfolio, struct WU_Position_ pos, double current_price)
This function checks if a position should be closed based on the current price and the portfolio's st...
static struct BuyPositionResult set_buy_position(WU_BasicPortfolio portfolio, WU_Signal signal)
This function calculates the position size for a buy signal, applies execution policy to determine th...
double wu_basic_portfolio_asset_value(WU_BasicPortfolio portfolio, int asset_index)
The value of a specific asset's positions is calculated by summing the value of each active position.
static bool uses_strategy_guided_allocation(WU_BasicPortfolio p, const WU_Signal *signals, int count)
Helper function to check if signals use strategy-guided allocation.
static double calculate_portfolio_pnl(const struct WU_Portfolio_ *portfolio)
The profit and loss (PnL) of the portfolio is calculated by taking the current total value of the por...
static double calculate_total_cost_basis(WU_PositionVector *vec)
This function calculates the total cost basis for all active positions in a given position vector.
static WU_PositionVector ** initialize_positions(const char *symbols[], int num_assets)
Helper function to initialize position vectors for all assets.
static void wu_basic_portfolio_free(struct WU_Portfolio_ *portfolio)
This function is responsible for freeing all memory associated with the portfolio.
static void update_portfolio(WU_Portfolio portfolio, const WU_Signal *signals)
The update_portfolio function is the core of the portfolio management logic.
static double calculate_portfolio_value(const struct WU_Portfolio_ *portfolio)
To calculate the total value of the portfolio, an iterative process is used to sum the value of each ...
static void update_prices_and_check_exits(WU_BasicPortfolio p, const WU_Signal *signals, int count)
Helper function to update last prices, apply borrow interest on short positions, check exits,...
static void execute_sell(WU_BasicPortfolio portfolio, WU_Signal signal, int asset_index)
This function executes a sell signal.
static double calculate_positions_value(WU_PositionVector *vec, double price)
Helper function to calculate the total value of all positions in a position vector at a given price.
bool wu_signal_validate(const WU_Signal *signal)
@ WU_TRANSACTION_COST_PROPORTIONAL
@ WU_TRANSACTION_COST_FIXED
@ WU_EXECUTION_POLICY_IMMEDIATE
@ WU_EXECUTION_POLICY_RANDOM_SLIPPAGE
@ WU_EXECUTION_POLICY_FIXED_SLIPPAGE
@ WU_EXECUTION_POLICY_NEXT_CLOSE
#define wu_portfolio_value(portfolio)
Calculates the current value of the portfolio.
#define wu_position_clear(vec)
#define wu_position_get(vec, index, found)
#define wu_position_add(vec, pos)
WU_PositionVector * wu_position_vector_new(const char *symbol)
@ WU_POSITION_SIZE_PCT_EQUAL
@ WU_POSITION_SIZE_STRATEGY_GUIDED
#define wu_position_remove(vec, index)
#define wu_position_total_quantity(vec)
#define wu_position_vector_delete(vec)
#define wu_portfolio_stats_update_position(stats, idx, sym, qty, val, price)
Updates position info for a specific asset.
#define wu_portfolio_stats_record_trade(stats, pnl, reason)
Records a trade in the stats.
WU_PortfolioStats wu_portfolio_stats_new(double initial_cash, double risk_free_rate)
Creates a new WU_PortfolioStats instance.
#define wu_portfolio_stats_delete(stats)
#define wu_portfolio_stats_update(stats, cash, value, timestamp)
Updates portfolio state in stats.
An auxiliary struct to hold the result of setting a buy position and calculating the total cost.
struct WU_Position_ position
A basic portfolio implementation that supports multiple assets.
WU_ExecutionPolicyValue policy
WU_TransactionCostType tx_cost_type
The parameters to define a portfolio.
Base for a portfolio, which defines the minimal interface for updating the portfolio with signals,...
WU_PositionSizeType size_type
WU_PositionVector is a data structure that holds multiple positions for a single asset.
struct WU_Position_ * positions
WU_Position represents an open position in the portfolio.
WU_Signal represents a trading signal generated by a strategy.
A timestamp represent a mark in time given relative to unix epoch.
WU_CloseReason
WU_CloseReason represents the reason for closing a position, which can be a trading signal,...
@ WU_CLOSE_REASON_TAKE_PROFIT
@ WU_CLOSE_REASON_STOP_LOSS