This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
position-sizing
Traduce un presupuesto de riesgo a una cantidad que el exchange acepte, sin pasarse nunca del riesgo.
pip install position-sizing
El problema
El cálculo parece trivial —riesgo / distancia_al_stop— y no lo es, porque el
resultado hay que hacerlo caber en las restricciones del mercado. Ahí es donde se
pierde dinero, y de dos formas silenciosas.
Redondear hacia arriba. Este patrón está en incontables bots:
cantidad = max(1, int(cantidad_teorica / contract_size))
Si la cantidad teórica sale 0.1 contratos, int() la trunca a 0 y
max(1, ...) fuerza uno. Acabas de abrir una posición que arriesga diez
veces lo autorizado. Con saldo pequeño o stop ancho ocurre siempre, y sin
avisar.
Ignorar el nocional mínimo. La orden se rechaza al llegar al exchange, el bot lo registra como error de red, y nadie se entera de que esa señal nunca se operó.
Qué hace esta librería
- Redondea siempre hacia abajo. Pasarse del presupuesto se paga con dinero; quedarse corto solo cuesta rendimiento. El error es asimétrico y el redondeo también.
- Se niega a operar cuando el mínimo del mercado no cabe en el riesgo — y dice cuánto saldo haría falta para que cupiera.
- Respeta
contractSize, escalón de cantidad, mínimos, máximos y nocional mínimo. - Cuenta comisiones de ida y vuelta: con un stop ajustado son la mitad del riesgo real.
- Distingue margen de riesgo: apalancarse cambia lo que puedes abrir, no lo que pierdes si salta el stop.
Uso
from decimal import Decimal
from position_sizing import MarketSpec, size_for_risk
spec = MarketSpec(
amount_step=Decimal("0.001"), # escalón de cantidad
min_amount=Decimal("0.001"),
min_notional=Decimal("5"), # valor mínimo de la orden
)
r = size_for_risk(
balance=Decimal("1000"),
risk_fraction=Decimal("0.01"), # arriesgar el 1%
entry=Decimal("50000"),
stop=Decimal("49000"),
spec=spec,
fee_rate=Decimal("0.0005"), # 0,05% por lado
)
if r.ok:
exchange.create_order(symbol, "market", "buy", float(r.quantity))
else:
log.warning("sin operar: %s", r.reason)
# -> "la cantidad mínima del mercado arriesga más de lo presupuestado;
# harían falta 5000.00 de saldo"
size_for_risk devuelve siempre un Sizing, nunca None:
| Campo | Qué es |
|---|---|
quantity |
Cantidad a enviar. Cero si se rechazó |
notional |
precio × cantidad × contract_size |
risk_amount |
Pérdida si salta el stop, comisiones incluidas |
risk_budget |
Lo que se pidió arriesgar. risk_amount nunca lo supera |
rejection |
None, o el motivo tipado |
min_balance_required |
Saldo necesario para superar el rechazo |
.ok / .reason |
Booleano y explicación lista para un log |
Con ccxt
from position_sizing import spec_from_ccxt, size_for_risk
spec = spec_from_ccxt(exchange.market("BTC/USDT:USDT"))
r = size_for_risk(balance=bal, risk_fraction=Decimal("0.01"),
entry=precio, stop=sl, spec=spec)
El adaptador es el único punto que sabe de ccxt. Resuelve además una
inconsistencia real: unos exchanges dan precision.amount como escalón (0.001)
y otros como número de decimales (3). Confundirlos hace que toda cantidad salga
mal.
Dos decisiones de diseño
Decimal, no float. El bug de arriba nace de un truncamiento. Con decimal y
redondeo explícito, la dirección es una decisión y no un accidente del tipo.
Función pura: recibe una MarketSpec, no un exchange. Se prueba sin red,
sirve para cualquier exchange y —lo que más importa— calcula igual en backtest
que en producción. Un backtest que asume posiciones fraccionarias que el mercado
nunca aceptaría infla el retorno; con la misma función en ambos lados, esa mentira
desaparece.
Alcance
Esto dimensiona órdenes. No decide cuándo operar, ni dónde poner el stop, ni en qué dirección. Es aritmética sobre restricciones de exchange, no una estrategia.
Licencia
Apache-2.0 · Copyright 2026 Juan Carlos Isaza Arenas
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file position_sizing-0.1.0.tar.gz.
File metadata
- Download URL: position_sizing-0.1.0.tar.gz
- Upload date:
- Size: 14.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd89169607e676342700c2a59eeb4f3f29d2f88e5050d7d84b5781073fc8f964
|
|
| MD5 |
7d035f098a308d044bb4c28150cf32f2
|
|
| BLAKE2b-256 |
db8d21a5ca8ec1ec12a987653c2dee4af4fbc0d0ffcf1e6402d8b9ceee36952e
|
Provenance
The following attestation bundles were made for position_sizing-0.1.0.tar.gz:
Publisher:
publish.yml on isazajuancarlos/position-sizing
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
position_sizing-0.1.0.tar.gz -
Subject digest:
fd89169607e676342700c2a59eeb4f3f29d2f88e5050d7d84b5781073fc8f964 - Sigstore transparency entry: 2195431535
- Sigstore integration time:
-
Permalink:
isazajuancarlos/position-sizing@2d232c376b05562d4612d0e72036887ba84114e1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2d232c376b05562d4612d0e72036887ba84114e1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file position_sizing-0.1.0-py3-none-any.whl.
File metadata
- Download URL: position_sizing-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d948438cae02ecc1a03ce0730fff0dfe18dd7e8a4112ebe34249c98560161a27
|
|
| MD5 |
0f9f71e2d4f7ee898e81d00c83842d13
|
|
| BLAKE2b-256 |
03f105dcfe3ef0d6f547e085cd00198a9d03228608809826b8123400f04fd64c
|
Provenance
The following attestation bundles were made for position_sizing-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on isazajuancarlos/position-sizing
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
position_sizing-0.1.0-py3-none-any.whl -
Subject digest:
d948438cae02ecc1a03ce0730fff0dfe18dd7e8a4112ebe34249c98560161a27 - Sigstore transparency entry: 2195431539
- Sigstore integration time:
-
Permalink:
isazajuancarlos/position-sizing@2d232c376b05562d4612d0e72036887ba84114e1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2d232c376b05562d4612d0e72036887ba84114e1 -
Trigger Event:
push
-
Statement type: