
DexorynA copy-trading bot should not assume that a trade is worth copying just because another wallet...
A copy-trading bot should not assume that a trade is worth copying just because another wallet executed it.
The source trader might have entered at a favorable price. By the time your bot detects the transaction, the market may have moved, the available liquidity may have changed, or the opportunity may no longer meet your strategy's requirements.
The solution is to separate trade detection from trade evaluation.
In this tutorial, we'll build a small Python component that evaluates a proposed copied trade using the follower's estimated entry price, probability estimate, and transaction costs. We'll also add tests for common rejection scenarios.
The example is an educational model. It does not connect to a live wallet, submit orders, or establish that a strategy is profitable.
The source wallet's price is useful context, but the follower needs to make a decision using the price available when the follower intends to trade.
Let's represent the relevant information with a Python dataclass.
from dataclasses import dataclass
from decimal import Decimal
@dataclass(frozen=True)
class CopyTradeCandidate:
token_id: str
source_price: Decimal
follower_entry_price: Decimal
estimated_probability: Decimal
estimated_cost_per_share: Decimal
Each field has a distinct role:
The source price and follower entry price should never be treated as interchangeable.
For a real implementation, validate the token identifier against trusted market metadata and obtain price information from a current, reliable market-data source.
For a binary outcome share bought and held until resolution, a winning share pays $1 and a losing share pays $0.
If the estimated probability of winning is (q), the entry price is (p), and the estimated cost per share is (c), the simplified expected value per share is:
[
EV=q-p-c
]
This calculation assumes the probability estimate is meaningful and that the purchase price represents the actual cost of acquiring a share, before the separately modeled costs.
Implement it as follows:
def expected_value_per_share(
probability: Decimal,
entry_price: Decimal,
cost_per_share: Decimal,
) -> Decimal:
if not Decimal("0") < probability < Decimal("1"):
raise ValueError("Probability must be between 0 and 1")
if not Decimal("0") < entry_price < Decimal("1"):
raise ValueError("Entry price must be between 0 and 1")
if cost_per_share < Decimal("0"):
raise ValueError("Cost cannot be negative")
return probability - entry_price - cost_per_share
Using "Decimal" helps avoid binary floating-point representation issues in financial calculations.
It does not make the probability estimate more accurate or remove uncertainty from the model.
Consider this hypothetical example:
The source's entry price and the follower's entry price produce different estimated values.
For the follower:
[
EV=0.60-0.52-0.01=$0.07
]
The estimated expected value is $0.07 per share under these assumptions.
Now suppose the follower's entry price increases to $0.60:
[
EV=0.60-0.60-0.01=-$0.01
]
The estimated expected value becomes negative.
The source trade has not changed, but the follower's economics have changed. A copy-trading bot should not blindly reproduce the transaction when its own entry conditions are no longer satisfied.
These figures are illustrative, not actual Polymarket market data or a forecast of returns.
Now combine the calculation with a minimum expected-value threshold and a maximum acceptable price difference from the source trade.
@dataclass(frozen=True)
class EvaluationResult:
approved: bool
reason: str
expected_value: Decimal | None
def evaluate_copy_trade(
candidate: CopyTradeCandidate,
*,
minimum_ev: Decimal,
maximum_price_difference: Decimal,
) -> EvaluationResult:
if not candidate.token_id:
return EvaluationResult(
False, "MISSING_TOKEN_ID", None
)
if not (
Decimal("0") < candidate.source_price < Decimal("1")
):
return EvaluationResult(
False, "INVALID_SOURCE_PRICE", None
)
price_difference = (
candidate.follower_entry_price
- candidate.source_price
)
if price_difference > maximum_price_difference:
return EvaluationResult(
False, "PRICE_MOVED_TOO_FAR", None
)
ev = expected_value_per_share(
candidate.estimated_probability,
candidate.follower_entry_price,
candidate.estimated_cost_per_share,
)
if ev < minimum_ev:
return EvaluationResult(
False, "EXPECTED_VALUE_TOO_LOW", ev
)
return EvaluationResult(True, "APPROVED", ev)
This example focuses on a BUY trade, where a higher follower entry price is less favorable. A production implementation should validate all inputs, explicitly handle supported trade directions, and model costs according to the applicable fee schedule.
The price-difference rule is also strategy-specific. A price increase does not automatically make a trade unattractive, and a small price difference does not automatically make it worthwhile. The expected-value check remains necessary.
Before using this function in production, add explicit checks for probability bounds, entry-price bounds, negative costs, and any other invalid input states.
A copy-trading decision should be testable without submitting a real order.
The following pytest tests cover a candidate that meets the example thresholds and one that exceeds the permitted price difference.
from decimal import Decimal
def make_candidate(overrides):
values = {
"token_id": "example-token",
"source_price": Decimal("0.40"),
"follower_entry_price": Decimal("0.42"),
"estimated_probability": Decimal("0.60"),
"estimated_cost_per_share": Decimal("0.01"),
}
values.update(overrides)
return CopyTradeCandidate(values)
def test_approves_candidate_with_sufficient_edge():
result = evaluate_copy_trade(
make_candidate(),
minimum_ev=Decimal("0.05"),
maximum_price_difference=Decimal("0.05"),
)
assert result.approved is True
assert result.reason == "APPROVED"
def test_rejects_excessive_price_movement():
result = evaluate_copy_trade(
make_candidate(
follower_entry_price=Decimal("0.50")
),
minimum_ev=Decimal("0.05"),
maximum_price_difference=Decimal("0.05"),
)
assert result.approved is False
assert result.reason == "PRICE_MOVED_TOO_FAR"
Save the implementation and tests in separate files, importing the relevant classes and functions, then run:
pip install pytest
pytest -q
These tests check the intended behavior of the example. They do not test live order-book data, order submission, or actual profitability.
A fuller test suite should cover invalid inputs, stale market data, missing tokens, insufficient expected value, exposure limits, and price changes immediately before execution.
A real copy-trading system needs to retrieve market data for the specific outcome token it intends to trade.
Polymarket's "official data guide" (https://institute.polymarket.com/data) documents public market-data access, including retrieving token prices and historical prices.
For a practical integration, the bot should:
The best available ask may not represent the average price for a large order. A production system may need to inspect multiple ask levels to estimate the cost of filling the intended quantity.
Market data can also change between retrieval and submission. The execution layer should enforce appropriate price limits rather than assuming that the previously observed price remains available.
The evaluation function should decide whether a candidate meets the strategy's requirements. It should not sign or submit orders itself.
A safer architecture separates the responsibilities:
This structure makes failures easier to investigate.
For example, if a bot repeatedly rejects copied trades because prices have moved too far, the logs can help establish whether the issue is detection latency, slow processing, or a strategy threshold that needs review.
If an order submission times out, the execution service should reconcile the order's status before retrying. Otherwise, a retry could duplicate an order that was already accepted.
Conclusion
Copying a transaction is not the same as copying the opportunity.
A useful copy-trading system evaluates the follower's own entry price, estimated probability, costs, and risk constraints before deciding whether to act.
The Python example in this tutorial demonstrates how to isolate that decision logic and test it independently of live trading infrastructure. It is a foundation for further development, not a complete production bot.
The next engineering steps are to integrate reliable market data, model executable prices for the intended order size, add portfolio risk controls, and test execution failure scenarios.
AI assistance disclosure: This article was prepared with AI assistance. The code and technical claims should be reviewed and tested before use in a live trading system.